You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by li...@apache.org on 2022/05/12 08:54:58 UTC

[apisix-ingress-controller] branch master updated: feat: support ApisixClusterConfig v2 (#977)

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

lingsamuel 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 bac9813e feat: support ApisixClusterConfig v2 (#977)
bac9813e is described below

commit bac9813e4c90a56dadba89808da2faa3d0834b79
Author: Sarasa Kisaragi <li...@gmail.com>
AuthorDate: Thu May 12 16:54:53 2022 +0800

    feat: support ApisixClusterConfig v2 (#977)
    
    * feat: support ApisixClusterConfig v2
    
    Signed-off-by: Ling Samuel <li...@gmail.com>
    
    * update
    
    Signed-off-by: Ling Samuel <li...@gmail.com>
    
    * fmt
    
    Signed-off-by: Ling Samuel <li...@gmail.com>
    
    * remove todo
    
    Signed-off-by: Ling Samuel <li...@gmail.com>
---
 cmd/ingress/ingress.go                   |   1 +
 pkg/config/config.go                     |  44 ++---
 pkg/config/config_test.go                |  34 ++--
 pkg/ingress/apisix_cluster_config.go     | 284 ++++++++++++++++++++++---------
 pkg/ingress/controller.go                |  25 ++-
 pkg/ingress/status.go                    |  16 ++
 pkg/kube/apisix_cluster_config.go        | 169 ++++++++++++++++++
 pkg/kube/translation/global_rule.go      |  23 ++-
 pkg/kube/translation/global_rule_test.go |   2 +-
 pkg/kube/translation/translator.go       |   5 +-
 10 files changed, 478 insertions(+), 125 deletions(-)

diff --git a/cmd/ingress/ingress.go b/cmd/ingress/ingress.go
index a4d6cbf9..d0a24be5 100644
--- a/cmd/ingress/ingress.go
+++ b/cmd/ingress/ingress.go
@@ -163,6 +163,7 @@ For example, no available LB exists in the bare metal environment.`)
 	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.IngressVersion, "ingress-version", config.IngressNetworkingV1, "the supported ingress api group version, can be \"networking/v1beta1\", \"networking/v1\" (for Kubernetes version v1.19.0 or higher) and \"extensions/v1beta1\"")
 	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ApisixRouteVersion, "apisix-route-version", config.ApisixRouteV2beta3, "the supported apisixroute api group version, can be \"apisix.apache.org/v2beta2\" or \"apisix.apache.org/v2beta3\"")
 	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ApisixTlsVersion, "apisix-tls-version", config.ApisixV2beta3, "the supported apisixtls api group version, can be \"apisix.apache.org/v2beta3\" or \"apisix.apache.org/v2\"")
+	cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ApisixClusterConfigVersion, "apisix-cluster-config-version", config.ApisixV2beta3, "the supported ApisixClusterConfig api group version, can be \"apisix.apache.org/v2beta3\" or \"apisix.apache.org/v2\"")
 	cmd.PersistentFlags().BoolVar(&cfg.Kubernetes.WatchEndpointSlices, "watch-endpointslices", false, "whether to watch endpointslices rather than endpoints")
 	cmd.PersistentFlags().BoolVar(&cfg.Kubernetes.EnableGatewayAPI, "enable-gateway-api", false, "whether to enable support for Gateway API")
 	cmd.PersistentFlags().StringVar(&cfg.APISIX.DefaultClusterBaseURL, "default-apisix-cluster-base-url", "", "the base URL of admin api / manager api for the default APISIX cluster")
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 0d41f406..15b1d10a 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -87,17 +87,18 @@ type Config struct {
 
 // KubernetesConfig contains all Kubernetes related config items.
 type KubernetesConfig struct {
-	Kubeconfig          string             `json:"kubeconfig" yaml:"kubeconfig"`
-	ResyncInterval      types.TimeDuration `json:"resync_interval" yaml:"resync_interval"`
-	AppNamespaces       []string           `json:"app_namespaces" yaml:"app_namespaces"`
-	NamespaceSelector   []string           `json:"namespace_selector" yaml:"namespace_selector"`
-	ElectionID          string             `json:"election_id" yaml:"election_id"`
-	IngressClass        string             `json:"ingress_class" yaml:"ingress_class"`
-	IngressVersion      string             `json:"ingress_version" yaml:"ingress_version"`
-	WatchEndpointSlices bool               `json:"watch_endpoint_slices" yaml:"watch_endpoint_slices"`
-	ApisixRouteVersion  string             `json:"apisix_route_version" yaml:"apisix_route_version"`
-	ApisixTlsVersion    string             `json:"apisix_tls_version" yaml:"apisix_tls_version"`
-	EnableGatewayAPI    bool               `json:"enable_gateway_api" yaml:"enable_gateway_api"`
+	Kubeconfig                 string             `json:"kubeconfig" yaml:"kubeconfig"`
+	ResyncInterval             types.TimeDuration `json:"resync_interval" yaml:"resync_interval"`
+	AppNamespaces              []string           `json:"app_namespaces" yaml:"app_namespaces"`
+	NamespaceSelector          []string           `json:"namespace_selector" yaml:"namespace_selector"`
+	ElectionID                 string             `json:"election_id" yaml:"election_id"`
+	IngressClass               string             `json:"ingress_class" yaml:"ingress_class"`
+	IngressVersion             string             `json:"ingress_version" yaml:"ingress_version"`
+	WatchEndpointSlices        bool               `json:"watch_endpoint_slices" yaml:"watch_endpoint_slices"`
+	ApisixRouteVersion         string             `json:"apisix_route_version" yaml:"apisix_route_version"`
+	ApisixTlsVersion           string             `json:"apisix_tls_version" yaml:"apisix_tls_version"`
+	ApisixClusterConfigVersion string             `json:"apisix_cluster_config_version" yaml:"apisix_cluster_config_version"`
+	EnableGatewayAPI           bool               `json:"enable_gateway_api" yaml:"enable_gateway_api"`
 }
 
 // APISIXConfig contains all APISIX related config items.
@@ -125,16 +126,17 @@ func NewDefaultConfig() *Config {
 		KeyFilePath:           "/etc/webhook/certs/key.pem",
 		EnableProfiling:       true,
 		Kubernetes: KubernetesConfig{
-			Kubeconfig:          "", // Use in-cluster configurations.
-			ResyncInterval:      types.TimeDuration{Duration: 6 * time.Hour},
-			AppNamespaces:       []string{v1.NamespaceAll},
-			ElectionID:          IngressAPISIXLeader,
-			IngressClass:        IngressClass,
-			IngressVersion:      IngressNetworkingV1,
-			ApisixRouteVersion:  ApisixRouteV2beta3,
-			ApisixTlsVersion:    ApisixV2beta3,
-			WatchEndpointSlices: false,
-			EnableGatewayAPI:    false,
+			Kubeconfig:                 "", // Use in-cluster configurations.
+			ResyncInterval:             types.TimeDuration{Duration: 6 * time.Hour},
+			AppNamespaces:              []string{v1.NamespaceAll},
+			ElectionID:                 IngressAPISIXLeader,
+			IngressClass:               IngressClass,
+			IngressVersion:             IngressNetworkingV1,
+			ApisixRouteVersion:         ApisixRouteV2beta3,
+			ApisixTlsVersion:           ApisixV2beta3,
+			ApisixClusterConfigVersion: ApisixV2beta3,
+			WatchEndpointSlices:        false,
+			EnableGatewayAPI:           false,
 		},
 	}
 }
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 112ebd83..515d8e6a 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -38,14 +38,15 @@ func TestNewConfigFromFile(t *testing.T) {
 		KeyFilePath:           "/etc/webhook/certs/key.pem",
 		EnableProfiling:       true,
 		Kubernetes: KubernetesConfig{
-			ResyncInterval:     types.TimeDuration{Duration: time.Hour},
-			Kubeconfig:         "/path/to/foo/baz",
-			AppNamespaces:      []string{""},
-			ElectionID:         "my-election-id",
-			IngressClass:       IngressClass,
-			IngressVersion:     IngressNetworkingV1,
-			ApisixRouteVersion: ApisixRouteV2beta3,
-			ApisixTlsVersion:   ApisixV2beta3,
+			ResyncInterval:             types.TimeDuration{Duration: time.Hour},
+			Kubeconfig:                 "/path/to/foo/baz",
+			AppNamespaces:              []string{""},
+			ElectionID:                 "my-election-id",
+			IngressClass:               IngressClass,
+			IngressVersion:             IngressNetworkingV1,
+			ApisixRouteVersion:         ApisixRouteV2beta3,
+			ApisixTlsVersion:           ApisixV2beta3,
+			ApisixClusterConfigVersion: ApisixV2beta3,
 		},
 		APISIX: APISIXConfig{
 			DefaultClusterName:     "default",
@@ -120,14 +121,15 @@ func TestConfigWithEnvVar(t *testing.T) {
 		KeyFilePath:           "/etc/webhook/certs/key.pem",
 		EnableProfiling:       true,
 		Kubernetes: KubernetesConfig{
-			ResyncInterval:     types.TimeDuration{Duration: time.Hour},
-			Kubeconfig:         "",
-			AppNamespaces:      []string{""},
-			ElectionID:         "my-election-id",
-			IngressClass:       IngressClass,
-			IngressVersion:     IngressNetworkingV1,
-			ApisixRouteVersion: ApisixRouteV2beta3,
-			ApisixTlsVersion:   ApisixV2beta3,
+			ResyncInterval:             types.TimeDuration{Duration: time.Hour},
+			Kubeconfig:                 "",
+			AppNamespaces:              []string{""},
+			ElectionID:                 "my-election-id",
+			IngressClass:               IngressClass,
+			IngressVersion:             IngressNetworkingV1,
+			ApisixRouteVersion:         ApisixRouteV2beta3,
+			ApisixTlsVersion:           ApisixV2beta3,
+			ApisixClusterConfigVersion: ApisixV2beta3,
 		},
 		APISIX: APISIXConfig{
 			DefaultClusterName:     "default",
diff --git a/pkg/ingress/apisix_cluster_config.go b/pkg/ingress/apisix_cluster_config.go
index 7bed3d46..ecc33d0e 100644
--- a/pkg/ingress/apisix_cluster_config.go
+++ b/pkg/ingress/apisix_cluster_config.go
@@ -16,6 +16,7 @@ package ingress
 
 import (
 	"context"
+	"fmt"
 	"time"
 
 	"go.uber.org/zap"
@@ -26,7 +27,8 @@ import (
 	"k8s.io/client-go/util/workqueue"
 
 	"github.com/apache/apisix-ingress-controller/pkg/apisix"
-	configv2beta3 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2beta3"
+	"github.com/apache/apisix-ingress-controller/pkg/config"
+	"github.com/apache/apisix-ingress-controller/pkg/kube"
 	"github.com/apache/apisix-ingress-controller/pkg/log"
 	"github.com/apache/apisix-ingress-controller/pkg/types"
 )
@@ -81,107 +83,204 @@ func (c *apisixClusterConfigController) runWorker(ctx context.Context) {
 }
 
 func (c *apisixClusterConfigController) sync(ctx context.Context, ev *types.Event) error {
-	key := ev.Object.(string)
+	event := ev.Object.(kube.ApisixClusterConfigEvent)
+	key := event.Key
 	_, name, err := cache.SplitMetaNamespaceKey(key)
 	if err != nil {
 		log.Errorf("found ApisixClusterConfig resource with invalid meta key %s: %s", key, err)
 		return err
 	}
-	acc, err := c.controller.apisixClusterConfigLister.Get(name)
+
+	var multiVersioned kube.ApisixClusterConfig
+	switch event.GroupVersion {
+	case config.ApisixV2beta3:
+		multiVersioned, err = c.controller.apisixClusterConfigLister.V2beta3(name)
+	case config.ApisixV2:
+		multiVersioned, err = c.controller.apisixClusterConfigLister.V2(name)
+	default:
+		return fmt.Errorf("unsupported ApisixClusterConfig group version %s", event.GroupVersion)
+	}
+
 	if err != nil {
 		if !k8serrors.IsNotFound(err) {
-			log.Errorf("failed to get ApisixClusterConfig %s: %s", key, err)
+			log.Errorw("failed to get ApisixClusterConfig",
+				zap.Error(err),
+				zap.String("key", key),
+				zap.String("version", event.GroupVersion),
+			)
 			return err
 		}
 		if ev.Type != types.EventDelete {
-			log.Warnf("ApisixClusterConfig %s was deleted before it can be delivered", key)
+			log.Warnw("ApisixClusterConfig was deleted before it can be delivered",
+				zap.String("key", key),
+				zap.String("version", event.GroupVersion),
+			)
 			return nil
 		}
 	}
 	if ev.Type == types.EventDelete {
-		if acc != nil {
+		if multiVersioned != 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 ApisixClusterConfig delete event since the %s exists", key)
 			return nil
 		}
-		acc = ev.Tombstone.(*configv2beta3.ApisixClusterConfig)
+		multiVersioned = ev.Tombstone.(kube.ApisixClusterConfig)
 	}
 
-	// Currently we don't handle multiple cluster, so only process
-	// the default apisix cluster.
-	if acc.Name != c.controller.cfg.APISIX.DefaultClusterName {
-		log.Infow("ignore non-default apisix cluster config",
-			zap.String("default_cluster_name", c.controller.cfg.APISIX.DefaultClusterName),
-			zap.Any("ApisixClusterConfig", acc),
+	switch event.GroupVersion {
+	case config.ApisixV2beta3:
+		acc := multiVersioned.V2beta3()
+		// Currently we don't handle multiple cluster, so only process
+		// the default apisix cluster.
+		if acc.Name != c.controller.cfg.APISIX.DefaultClusterName {
+			log.Infow("ignore non-default apisix cluster config",
+				zap.String("default_cluster_name", c.controller.cfg.APISIX.DefaultClusterName),
+				zap.Any("ApisixClusterConfig", acc),
+			)
+			return nil
+		}
+		// Cluster delete is dangerous.
+		// TODO handle delete?
+		if ev.Type == types.EventDelete {
+			log.Error("ApisixClusterConfig delete event for default apisix cluster will be ignored")
+			return nil
+		}
+
+		if acc.Spec.Admin != nil {
+			clusterOpts := &apisix.ClusterOptions{
+				Name:     acc.Name,
+				BaseURL:  acc.Spec.Admin.BaseURL,
+				AdminKey: acc.Spec.Admin.AdminKey,
+			}
+			log.Infow("updating cluster",
+				zap.Any("opts", clusterOpts),
+			)
+			// TODO we may first call AddCluster.
+			// Since now we already have the default cluster, we just call UpdateCluster.
+			if err := c.controller.apisix.UpdateCluster(ctx, clusterOpts); err != nil {
+				log.Errorw("failed to update cluster",
+					zap.String("cluster_name", acc.Name),
+					zap.Error(err),
+					zap.Any("opts", clusterOpts),
+				)
+				c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
+				c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
+				return err
+			}
+		}
+
+		globalRule, err := c.controller.translator.TranslateClusterConfigV2beta3(acc)
+		if err != nil {
+			log.Errorw("failed to translate ApisixClusterConfig",
+				zap.Error(err),
+				zap.String("key", key),
+				zap.Any("object", acc),
+			)
+			c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
+			c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
+			return err
+		}
+		log.Debugw("translated global_rule",
+			zap.Any("object", globalRule),
 		)
+
+		// TODO multiple cluster support
+		if ev.Type == types.EventAdd {
+			_, err = c.controller.apisix.Cluster(acc.Name).GlobalRule().Create(ctx, globalRule)
+		} else {
+			_, err = c.controller.apisix.Cluster(acc.Name).GlobalRule().Update(ctx, globalRule)
+		}
+		if err != nil {
+			log.Errorw("failed to reflect global_rule changes to apisix cluster",
+				zap.Any("global_rule", globalRule),
+				zap.Any("cluster", acc.Name),
+			)
+			c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
+			c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
+			return err
+		}
+		c.controller.recorderEvent(acc, corev1.EventTypeNormal, _resourceSynced, nil)
+		c.controller.recordStatus(acc, _resourceSynced, nil, metav1.ConditionTrue, acc.GetGeneration())
 		return nil
-	}
-	// Cluster delete is dangerous.
-	// TODO handle delete?
-	if ev.Type == types.EventDelete {
-		log.Error("ApisixClusterConfig delete event for default apisix cluster will be ignored")
-		return nil
-	}
+	case config.ApisixV2:
+		acc := multiVersioned.V2()
+		// Currently we don't handle multiple cluster, so only process
+		// the default apisix cluster.
+		if acc.Name != c.controller.cfg.APISIX.DefaultClusterName {
+			log.Infow("ignore non-default apisix cluster config",
+				zap.String("default_cluster_name", c.controller.cfg.APISIX.DefaultClusterName),
+				zap.Any("ApisixClusterConfig", acc),
+			)
+			return nil
+		}
+		// Cluster delete is dangerous.
+		// TODO handle delete?
+		if ev.Type == types.EventDelete {
+			log.Error("ApisixClusterConfig delete event for default apisix cluster will be ignored")
+			return nil
+		}
 
-	if acc.Spec.Admin != nil {
-		clusterOpts := &apisix.ClusterOptions{
-			Name:     acc.Name,
-			BaseURL:  acc.Spec.Admin.BaseURL,
-			AdminKey: acc.Spec.Admin.AdminKey,
+		if acc.Spec.Admin != nil {
+			clusterOpts := &apisix.ClusterOptions{
+				Name:     acc.Name,
+				BaseURL:  acc.Spec.Admin.BaseURL,
+				AdminKey: acc.Spec.Admin.AdminKey,
+			}
+			log.Infow("updating cluster",
+				zap.Any("opts", clusterOpts),
+			)
+			// TODO we may first call AddCluster.
+			// Since now we already have the default cluster, we just call UpdateCluster.
+			if err := c.controller.apisix.UpdateCluster(ctx, clusterOpts); err != nil {
+				log.Errorw("failed to update cluster",
+					zap.String("cluster_name", acc.Name),
+					zap.Error(err),
+					zap.Any("opts", clusterOpts),
+				)
+				c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
+				c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
+				return err
+			}
 		}
-		log.Infow("updating cluster",
-			zap.Any("opts", clusterOpts),
-		)
-		// TODO we may first call AddCluster.
-		// Since now we already have the default cluster, we just call UpdateCluster.
-		if err := c.controller.apisix.UpdateCluster(ctx, clusterOpts); err != nil {
-			log.Errorw("failed to update cluster",
-				zap.String("cluster_name", acc.Name),
+
+		globalRule, err := c.controller.translator.TranslateClusterConfigV2(acc)
+		if err != nil {
+			log.Errorw("failed to translate ApisixClusterConfig",
 				zap.Error(err),
-				zap.Any("opts", clusterOpts),
+				zap.String("key", key),
+				zap.Any("object", acc),
 			)
 			c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
 			c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
 			return err
 		}
-	}
-
-	globalRule, err := c.controller.translator.TranslateClusterConfig(acc)
-	if err != nil {
-		// TODO add status
-		log.Errorw("failed to translate ApisixClusterConfig",
-			zap.Error(err),
-			zap.String("key", key),
-			zap.Any("object", acc),
+		log.Debugw("translated global_rule",
+			zap.Any("object", globalRule),
 		)
-		c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
-		c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
-		return err
-	}
-	log.Debugw("translated global_rule",
-		zap.Any("object", globalRule),
-	)
 
-	// TODO multiple cluster support
-	if ev.Type == types.EventAdd {
-		_, err = c.controller.apisix.Cluster(acc.Name).GlobalRule().Create(ctx, globalRule)
-	} else {
-		_, err = c.controller.apisix.Cluster(acc.Name).GlobalRule().Update(ctx, globalRule)
-	}
-	if err != nil {
-		log.Errorw("failed to reflect global_rule changes to apisix cluster",
-			zap.Any("global_rule", globalRule),
-			zap.Any("cluster", acc.Name),
-		)
-		c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
-		c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
-		return err
+		// TODO multiple cluster support
+		if ev.Type == types.EventAdd {
+			_, err = c.controller.apisix.Cluster(acc.Name).GlobalRule().Create(ctx, globalRule)
+		} else {
+			_, err = c.controller.apisix.Cluster(acc.Name).GlobalRule().Update(ctx, globalRule)
+		}
+		if err != nil {
+			log.Errorw("failed to reflect global_rule changes to apisix cluster",
+				zap.Any("global_rule", globalRule),
+				zap.Any("cluster", acc.Name),
+			)
+			c.controller.recorderEvent(acc, corev1.EventTypeWarning, _resourceSyncAborted, err)
+			c.controller.recordStatus(acc, _resourceSyncAborted, err, metav1.ConditionFalse, acc.GetGeneration())
+			return err
+		}
+		c.controller.recorderEvent(acc, corev1.EventTypeNormal, _resourceSynced, nil)
+		c.controller.recordStatus(acc, _resourceSynced, nil, metav1.ConditionTrue, acc.GetGeneration())
+		return nil
+	default:
+		return fmt.Errorf("unsupported ApisixClusterConfig group version %s", event.GroupVersion)
 	}
-	c.controller.recorderEvent(acc, corev1.EventTypeNormal, _resourceSynced, nil)
-	c.controller.recordStatus(acc, _resourceSynced, nil, metav1.ConditionTrue, acc.GetGeneration())
-	return nil
 }
 
 func (c *apisixClusterConfigController) handleSyncErr(obj interface{}, err error) {
@@ -209,6 +308,11 @@ func (c *apisixClusterConfigController) handleSyncErr(obj interface{}, err error
 }
 
 func (c *apisixClusterConfigController) onAdd(obj interface{}) {
+	acc, err := kube.NewApisixClusterConfig(obj)
+	if err != nil {
+		log.Errorw("found ApisixClusterConfig resource with bad type", zap.Error(err))
+		return
+	}
 	key, err := cache.MetaNamespaceKeyFunc(obj)
 	if err != nil {
 		log.Errorf("found ApisixClusterConfig resource with bad meta key: %s", err.Error())
@@ -220,17 +324,28 @@ func (c *apisixClusterConfigController) onAdd(obj interface{}) {
 	)
 
 	c.workqueue.Add(&types.Event{
-		Type:   types.EventAdd,
-		Object: key,
+		Type: types.EventAdd,
+		Object: kube.ApisixClusterConfigEvent{
+			Key:          key,
+			GroupVersion: acc.GroupVersion(),
+		},
 	})
 
 	c.controller.MetricsCollector.IncrEvents("clusterConfig", "add")
 }
 
 func (c *apisixClusterConfigController) onUpdate(oldObj, newObj interface{}) {
-	prev := oldObj.(*configv2beta3.ApisixClusterConfig)
-	curr := newObj.(*configv2beta3.ApisixClusterConfig)
-	if prev.ResourceVersion >= curr.ResourceVersion {
+	prev, err := kube.NewApisixClusterConfig(oldObj)
+	if err != nil {
+		log.Errorw("found ApisixClusterConfig resource with bad type", zap.Error(err))
+		return
+	}
+	curr, err := kube.NewApisixClusterConfig(newObj)
+	if err != nil {
+		log.Errorw("found ApisixClusterConfig resource with bad type", zap.Error(err))
+		return
+	}
+	if prev.ResourceVersion() >= curr.ResourceVersion() {
 		return
 	}
 	key, err := cache.MetaNamespaceKeyFunc(newObj)
@@ -244,21 +359,29 @@ func (c *apisixClusterConfigController) onUpdate(oldObj, newObj interface{}) {
 	)
 
 	c.workqueue.Add(&types.Event{
-		Type:   types.EventUpdate,
-		Object: key,
+		Type: types.EventUpdate,
+		Object: kube.ApisixClusterConfigEvent{
+			Key:          key,
+			OldObject:    prev,
+			GroupVersion: curr.GroupVersion(),
+		},
 	})
 
 	c.controller.MetricsCollector.IncrEvents("clusterConfig", "update")
 }
 
 func (c *apisixClusterConfigController) onDelete(obj interface{}) {
-	acc, ok := obj.(*configv2beta3.ApisixClusterConfig)
-	if !ok {
+	acc, err := kube.NewApisixClusterConfig(obj)
+	if err != nil {
 		tombstone, ok := obj.(*cache.DeletedFinalStateUnknown)
 		if !ok {
 			return
 		}
-		acc = tombstone.Obj.(*configv2beta3.ApisixClusterConfig)
+		acc, err = kube.NewApisixClusterConfig(tombstone)
+		if err != nil {
+			log.Errorw("found ApisixClusterConfig resource with bad type", zap.Error(err))
+			return
+		}
 	}
 
 	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
@@ -270,8 +393,11 @@ func (c *apisixClusterConfigController) onDelete(obj interface{}) {
 		zap.Any("final state", acc),
 	)
 	c.workqueue.Add(&types.Event{
-		Type:      types.EventDelete,
-		Object:    key,
+		Type: types.EventDelete,
+		Object: kube.ApisixClusterConfigEvent{
+			Key:          key,
+			GroupVersion: acc.GroupVersion(),
+		},
 		Tombstone: acc,
 	})
 
diff --git a/pkg/ingress/controller.go b/pkg/ingress/controller.go
index 6755dc51..c1afded2 100644
--- a/pkg/ingress/controller.go
+++ b/pkg/ingress/controller.go
@@ -111,7 +111,7 @@ type Controller struct {
 	apisixRouteInformer         cache.SharedIndexInformer
 	apisixTlsLister             kube.ApisixTlsLister
 	apisixTlsInformer           cache.SharedIndexInformer
-	apisixClusterConfigLister   listersv2beta3.ApisixClusterConfigLister
+	apisixClusterConfigLister   kube.ApisixClusterConfigLister
 	apisixClusterConfigInformer cache.SharedIndexInformer
 	apisixConsumerInformer      cache.SharedIndexInformer
 	apisixConsumerLister        listersv2beta3.ApisixConsumerLister
@@ -200,9 +200,10 @@ func NewController(cfg *config.Config) (*Controller, error) {
 
 func (c *Controller) initWhenStartLeading() {
 	var (
-		ingressInformer     cache.SharedIndexInformer
-		apisixRouteInformer cache.SharedIndexInformer
-		apisixTlsInformer   cache.SharedIndexInformer
+		ingressInformer             cache.SharedIndexInformer
+		apisixRouteInformer         cache.SharedIndexInformer
+		apisixTlsInformer           cache.SharedIndexInformer
+		apisixClusterConfigInformer cache.SharedIndexInformer
 	)
 
 	kubeFactory := c.kubeClient.NewSharedIndexInformerFactory()
@@ -229,7 +230,10 @@ func (c *Controller) initWhenStartLeading() {
 		apisixFactory.Apisix().V2beta3().ApisixTlses().Lister(),
 		apisixFactory.Apisix().V2().ApisixTlses().Lister(),
 	)
-	c.apisixClusterConfigLister = apisixFactory.Apisix().V2beta3().ApisixClusterConfigs().Lister()
+	c.apisixClusterConfigLister = kube.NewApisixClusterConfigLister(
+		apisixFactory.Apisix().V2beta3().ApisixClusterConfigs().Lister(),
+		apisixFactory.Apisix().V2().ApisixClusterConfigs().Lister(),
+	)
 	c.apisixConsumerLister = apisixFactory.Apisix().V2beta3().ApisixConsumers().Lister()
 	c.apisixPluginConfigLister = kube.NewApisixPluginConfigLister(
 		apisixFactory.Apisix().V2beta3().ApisixPluginConfigs().Lister(),
@@ -276,13 +280,22 @@ func (c *Controller) initWhenStartLeading() {
 		panic(fmt.Errorf("unsupported ApisixTls version %s", c.cfg.Kubernetes.ApisixTlsVersion))
 	}
 
+	switch c.cfg.Kubernetes.ApisixClusterConfigVersion {
+	case config.ApisixV2beta3:
+		apisixClusterConfigInformer = apisixFactory.Apisix().V2beta3().ApisixClusterConfigs().Informer()
+	case config.ApisixV2:
+		apisixClusterConfigInformer = apisixFactory.Apisix().V2().ApisixClusterConfigs().Informer()
+	default:
+		panic(fmt.Errorf("unsupported ApisixClusterConfig version %v", c.cfg.Kubernetes.ApisixClusterConfigVersion))
+	}
+
 	c.namespaceInformer = kubeFactory.Core().V1().Namespaces().Informer()
 	c.podInformer = kubeFactory.Core().V1().Pods().Informer()
 	c.svcInformer = kubeFactory.Core().V1().Services().Informer()
 	c.ingressInformer = ingressInformer
 	c.apisixRouteInformer = apisixRouteInformer
 	c.apisixUpstreamInformer = apisixFactory.Apisix().V2beta3().ApisixUpstreams().Informer()
-	c.apisixClusterConfigInformer = apisixFactory.Apisix().V2beta3().ApisixClusterConfigs().Informer()
+	c.apisixClusterConfigInformer = apisixClusterConfigInformer
 	c.secretInformer = kubeFactory.Core().V1().Secrets().Informer()
 	c.apisixTlsInformer = apisixTlsInformer
 	c.apisixConsumerInformer = apisixFactory.Apisix().V2beta3().ApisixConsumers().Informer()
diff --git a/pkg/ingress/status.go b/pkg/ingress/status.go
index db9c4cf2..c2d090ed 100644
--- a/pkg/ingress/status.go
+++ b/pkg/ingress/status.go
@@ -210,6 +210,22 @@ func (c *Controller) recordStatus(at interface{}, reason string, err error, stat
 				)
 			}
 		}
+	case *configv2.ApisixClusterConfig:
+		// set to status
+		if v.Status.Conditions == nil {
+			conditions := make([]metav1.Condition, 0)
+			v.Status.Conditions = conditions
+		}
+		if c.verifyGeneration(&v.Status.Conditions, condition) {
+			meta.SetStatusCondition(&v.Status.Conditions, condition)
+			if _, errRecord := client.ApisixV2().ApisixClusterConfigs().
+				UpdateStatus(context.TODO(), v, metav1.UpdateOptions{}); errRecord != nil {
+				log.Errorw("failed to record status change for ApisixClusterConfig",
+					zap.Error(errRecord),
+					zap.String("name", v.Name),
+				)
+			}
+		}
 	case *networkingv1.Ingress:
 		// set to status
 		lbips, err := c.ingressLBStatusIPs()
diff --git a/pkg/kube/apisix_cluster_config.go b/pkg/kube/apisix_cluster_config.go
new file mode 100644
index 00000000..9eb8ecee
--- /dev/null
+++ b/pkg/kube/apisix_cluster_config.go
@@ -0,0 +1,169 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package kube
+
+import (
+	"fmt"
+
+	"github.com/apache/apisix-ingress-controller/pkg/config"
+	configv2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2"
+	configv2beta3 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2beta3"
+	listersv2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v2"
+	listersv2beta3 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/client/listers/config/v2beta3"
+)
+
+// ApisixClusterConfigLister is an encapsulation for the lister of ApisixClusterConfig,
+// it aims at to be compatible with different ApisixClusterConfig versions.
+type ApisixClusterConfigLister interface {
+	// V2beta3 gets the ApisixClusterConfig in apisix.apache.org/v2beta3.
+	V2beta3(string) (ApisixClusterConfig, error)
+	// V2 gets the ApisixClusterConfig in apisix.apache.org/v2.
+	V2(string) (ApisixClusterConfig, error)
+}
+
+// ApisixClusterConfigInformer is an encapsulation for the informer of ApisixClusterConfig,
+// it aims at to be compatible with different ApisixClusterConfig versions.
+type ApisixClusterConfigInformer interface {
+	Run(chan struct{})
+}
+
+// ApisixClusterConfig is an encapsulation for ApisixClusterConfig resource with different
+// versions, for now, they are apisix.apache.org/v1 and apisix.apache.org/v2alpha1
+type ApisixClusterConfig interface {
+	// GroupVersion returns the api group version of the
+	// real ApisixClusterConfig.
+	GroupVersion() string
+	// V2beta3 returns the ApisixClusterConfig in apisix.apache.org/v2beta3, the real
+	// ApisixClusterConfig must be in this group version, otherwise will panic.
+	V2beta3() *configv2beta3.ApisixClusterConfig
+	// V2 returns the ApisixClusterConfig in apisix.apache.org/v2, the real
+	// ApisixClusterConfig must be in this group version, otherwise will panic.
+	V2() *configv2.ApisixClusterConfig
+	// ResourceVersion returns the the resource version field inside
+	// the real ApisixClusterConfig.
+	ResourceVersion() string
+}
+
+// ApisixClusterConfigEvent contains the ApisixClusterConfig key (namespace/name)
+// and the group version message.
+type ApisixClusterConfigEvent struct {
+	Key          string
+	OldObject    ApisixClusterConfig
+	GroupVersion string
+}
+
+type apisixClusterConfig struct {
+	groupVersion string
+	v2beta3      *configv2beta3.ApisixClusterConfig
+	v2           *configv2.ApisixClusterConfig
+}
+
+func (acc *apisixClusterConfig) V2beta3() *configv2beta3.ApisixClusterConfig {
+	if acc.groupVersion != config.ApisixV2beta3 {
+		panic("not a apisix.apache.org/v2beta3 apisixClusterConfig")
+	}
+	return acc.v2beta3
+}
+
+func (acc *apisixClusterConfig) V2() *configv2.ApisixClusterConfig {
+	if acc.groupVersion != config.ApisixV2 {
+		panic("not a apisix.apache.org/v2 apisixClusterConfig")
+	}
+	return acc.v2
+}
+
+func (acc *apisixClusterConfig) GroupVersion() string {
+	return acc.groupVersion
+}
+
+func (acc *apisixClusterConfig) ResourceVersion() string {
+	if acc.groupVersion == config.ApisixV2beta3 {
+		return acc.V2beta3().ResourceVersion
+	}
+	return acc.V2().ResourceVersion
+}
+
+type apisixClusterConfigLister struct {
+	v2beta3Lister listersv2beta3.ApisixClusterConfigLister
+	v2Lister      listersv2.ApisixClusterConfigLister
+}
+
+func (l *apisixClusterConfigLister) V2beta3(name string) (ApisixClusterConfig, error) {
+	acc, err := l.v2beta3Lister.Get(name)
+	if err != nil {
+		return nil, err
+	}
+	return &apisixClusterConfig{
+		groupVersion: config.ApisixV2beta3,
+		v2beta3:      acc,
+	}, nil
+}
+
+func (l *apisixClusterConfigLister) V2(name string) (ApisixClusterConfig, error) {
+	acc, err := l.v2Lister.Get(name)
+	if err != nil {
+		return nil, err
+	}
+	return &apisixClusterConfig{
+		groupVersion: config.ApisixV2,
+		v2:           acc,
+	}, nil
+}
+
+// MustNewApisixClusterConfig creates a kube.ApisixClusterConfig object according to the
+// type of obj.
+func MustNewApisixClusterConfig(obj interface{}) ApisixClusterConfig {
+	switch acc := obj.(type) {
+	case *configv2beta3.ApisixClusterConfig:
+		return &apisixClusterConfig{
+			groupVersion: config.ApisixV2beta3,
+			v2beta3:      acc,
+		}
+	case *configv2.ApisixClusterConfig:
+		return &apisixClusterConfig{
+			groupVersion: config.ApisixV2,
+			v2:           acc,
+		}
+	default:
+		panic("invalid ApisixClusterConfig type")
+	}
+}
+
+// NewApisixClusterConfig creates a kube.ApisixClusterConfig object according to the
+// type of obj. It returns nil and the error reason when the
+// type assertion fails.
+func NewApisixClusterConfig(obj interface{}) (ApisixClusterConfig, error) {
+	switch acc := obj.(type) {
+	case *configv2beta3.ApisixClusterConfig:
+		return &apisixClusterConfig{
+			groupVersion: config.ApisixV2beta3,
+			v2beta3:      acc,
+		}, nil
+	case *configv2.ApisixClusterConfig:
+		return &apisixClusterConfig{
+			groupVersion: config.ApisixV2,
+			v2:           acc,
+		}, nil
+	default:
+		return nil, fmt.Errorf("invalid ApisixClusterConfig type %T", acc)
+	}
+}
+
+func NewApisixClusterConfigLister(v2beta3 listersv2beta3.ApisixClusterConfigLister, v2 listersv2.ApisixClusterConfigLister) ApisixClusterConfigLister {
+	return &apisixClusterConfigLister{
+		v2beta3Lister: v2beta3,
+		v2Lister:      v2,
+	}
+}
diff --git a/pkg/kube/translation/global_rule.go b/pkg/kube/translation/global_rule.go
index 7fc1eaef..d59cd982 100644
--- a/pkg/kube/translation/global_rule.go
+++ b/pkg/kube/translation/global_rule.go
@@ -16,6 +16,7 @@ package translation
 
 import (
 	"github.com/apache/apisix-ingress-controller/pkg/id"
+	configv2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2"
 	configv2beta3 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2beta3"
 	apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 )
@@ -26,7 +27,27 @@ type skywalkingPluginConfig struct {
 	SampleRatio float64 `json:"sample_ratio,omitempty"`
 }
 
-func (t *translator) TranslateClusterConfig(acc *configv2beta3.ApisixClusterConfig) (*apisixv1.GlobalRule, error) {
+func (t *translator) TranslateClusterConfigV2beta3(acc *configv2beta3.ApisixClusterConfig) (*apisixv1.GlobalRule, error) {
+	globalRule := &apisixv1.GlobalRule{
+		ID:      id.GenID(acc.Name),
+		Plugins: make(apisixv1.Plugins),
+	}
+
+	if acc.Spec.Monitoring != nil {
+		if acc.Spec.Monitoring.Prometheus.Enable {
+			globalRule.Plugins["prometheus"] = &prometheusPluginConfig{}
+		}
+		if acc.Spec.Monitoring.Skywalking.Enable {
+			globalRule.Plugins["skywalking"] = &skywalkingPluginConfig{
+				SampleRatio: acc.Spec.Monitoring.Skywalking.SampleRatio,
+			}
+		}
+	}
+
+	return globalRule, nil
+}
+
+func (t *translator) TranslateClusterConfigV2(acc *configv2.ApisixClusterConfig) (*apisixv1.GlobalRule, error) {
 	globalRule := &apisixv1.GlobalRule{
 		ID:      id.GenID(acc.Name),
 		Plugins: make(apisixv1.Plugins),
diff --git a/pkg/kube/translation/global_rule_test.go b/pkg/kube/translation/global_rule_test.go
index 8a584ac4..93cbc5fe 100644
--- a/pkg/kube/translation/global_rule_test.go
+++ b/pkg/kube/translation/global_rule_test.go
@@ -44,7 +44,7 @@ func TestTranslateClusterConfig(t *testing.T) {
 			},
 		},
 	}
-	gr, err := tr.TranslateClusterConfig(acc)
+	gr, err := tr.TranslateClusterConfigV2beta3(acc)
 	assert.Nil(t, err, "translating ApisixClusterConfig")
 	assert.Equal(t, gr.ID, id.GenID("qa-apisix"), "checking global_rule id")
 	assert.Len(t, gr.Plugins, 2)
diff --git a/pkg/kube/translation/translator.go b/pkg/kube/translation/translator.go
index c5217aa3..2acc5b4c 100644
--- a/pkg/kube/translation/translator.go
+++ b/pkg/kube/translation/translator.go
@@ -89,7 +89,10 @@ type Translator interface {
 	TranslateSSLV2(*configv2.ApisixTls) (*apisixv1.Ssl, error)
 	// TranslateClusterConfig translates the configv2beta3.ApisixClusterConfig object into the APISIX
 	// Global Rule resource.
-	TranslateClusterConfig(*configv2beta3.ApisixClusterConfig) (*apisixv1.GlobalRule, error)
+	TranslateClusterConfigV2beta3(*configv2beta3.ApisixClusterConfig) (*apisixv1.GlobalRule, error)
+	// TranslateClusterConfigV2 translates the configv2.ApisixClusterConfig object into the APISIX
+	// Global Rule resource.
+	TranslateClusterConfigV2(*configv2.ApisixClusterConfig) (*apisixv1.GlobalRule, error)
 	// TranslateApisixConsumer translates the configv2beta3.APisixConsumer object into the APISIX Consumer
 	// resource.
 	TranslateApisixConsumer(*configv2beta3.ApisixConsumer) (*apisixv1.Consumer, error)