diff --git a/.github/workflows/apisix-e2e-test.yml b/.github/workflows/apisix-e2e-test.yml index 280122cc..46c48760 100644 --- a/.github/workflows/apisix-e2e-test.yml +++ b/.github/workflows/apisix-e2e-test.yml @@ -124,3 +124,67 @@ jobs: else make ginkgo-e2e-test fi + + disable-gateway-api: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Go Env + uses: actions/setup-go@v4 + with: + go-version: "1.24" + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "*" + + - name: Install kind + run: | + go install sigs.k8s.io/kind@v0.23.0 + + - name: Install ginkgo + run: | + make install-ginkgo + + - name: Build images + env: + TAG: dev + ARCH: amd64 + ENABLE_PROXY: "false" + BASE_IMAGE_TAG: "debug" + run: | + echo "building images..." + make build-image + + - name: Launch Kind Cluster + run: | + make kind-up + + - name: Loading Docker Image to Kind Cluster + run: | + make kind-load-images + + - name: Extract adc binary + if: ${{ env.ADC_VERSION == 'dev' }} + run: | + docker create --name adc-temp ghcr.io/api7/adc:dev + docker cp adc-temp:main.js adc.js + docker rm adc-temp + node $(pwd)/adc.js -v + echo "ADC_BIN=node $(pwd)/adc.js" >> $GITHUB_ENV + + - name: Install CRDs + run: make install-crds + + - name: Run E2E test suite + shell: bash + env: + TEST_DIR: "./test/e2e/apisix/" + TEST_ENV: CI + TEST_FOCUS: "Test ApisixRoute Basic tests" + run: make e2e-test diff --git a/Makefile b/Makefile index f38b307b..b7fdaffd 100644 --- a/Makefile +++ b/Makefile @@ -333,7 +333,10 @@ uninstall-gateway-api: ## Uninstall Gateway API CRDs from the K8s cluster specif kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/$(GATEAY_API_VERSION)/experimental-install.yaml .PHONY: install -install: manifests kustomize install-gateway-api ## Install CRDs into the K8s cluster specified in ~/.kube/config. +install: manifests kustomize install-gateway-api install-crds ## Install CRDs and Gateway API into the K8s cluster specified in ~/.kube/config. + +.PHONY: install-crds +install-crds: manifests kustomize ## Install CRDs into the K8s cluster specified $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - .PHONY: install-crds-nocel diff --git a/config/samples/config.yaml b/config/samples/config.yaml index 1b727820..3f26ea38 100644 --- a/config/samples/config.yaml +++ b/config/samples/config.yaml @@ -33,6 +33,8 @@ secure_metrics: false # The secure metrics configuration. exec_adc_timeout: 15s # The timeout for the ADC to execute. # The default value is 15 seconds. +disable_gateway_api: false # Whether to disable the Gateway API support. + # The default value is false. disable_gateway_api: false # Whether to disable the Gateway API support. # The default value is false. diff --git a/internal/controller/consumer_controller.go b/internal/controller/consumer_controller.go index 6ebe8c96..cfb1b296 100644 --- a/internal/controller/consumer_controller.go +++ b/internal/controller/consumer_controller.go @@ -44,6 +44,7 @@ import ( "github.com/apache/apisix-ingress-controller/internal/provider" internaltypes "github.com/apache/apisix-ingress-controller/internal/types" "github.com/apache/apisix-ingress-controller/internal/utils" + pkgutils "github.com/apache/apisix-ingress-controller/pkg/utils" ) // ConsumerReconciler reconciles a Gateway object. @@ -60,6 +61,10 @@ type ConsumerReconciler struct { //nolint:revive // SetupWithManager sets up the controller with the Manager. func (r *ConsumerReconciler) SetupWithManager(mgr ctrl.Manager) error { + if config.ControllerConfig.DisableGatewayAPI || !pkgutils.HasAPIResource(mgr, &gatewayv1.Gateway{}) { + r.Log.Info("skipping Consumer controller setup as Gateway API is not available") + return nil + } return ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.Consumer{}, builder.WithPredicates( diff --git a/internal/controller/gateway_controller.go b/internal/controller/gateway_controller.go index 76648a4a..af2b35a1 100644 --- a/internal/controller/gateway_controller.go +++ b/internal/controller/gateway_controller.go @@ -43,6 +43,7 @@ import ( "github.com/apache/apisix-ingress-controller/internal/provider" internaltypes "github.com/apache/apisix-ingress-controller/internal/types" "github.com/apache/apisix-ingress-controller/internal/utils" + pkgutils "github.com/apache/apisix-ingress-controller/pkg/utils" ) // GatewayReconciler reconciles a Gateway object. @@ -80,7 +81,15 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { ). Watches( &gatewayv1.HTTPRoute{}, +<<<<<<< HEAD handler.EnqueueRequestsFromMapFunc(r.listGatewaysForHTTPRoute), +======= + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ). + Watches( + &gatewayv1.GRPCRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) ). Watches( &v1alpha1.GatewayProxy{}, @@ -97,6 +106,24 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { builder.WithPredicates(referenceGrantPredicates(KindGateway)), ) } + if pkgutils.HasAPIResource(mgr, &gatewayv1alpha2.TCPRoute{}) { + bdr.Watches( + &gatewayv1alpha2.TCPRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ) + } + if pkgutils.HasAPIResource(mgr, &gatewayv1alpha2.TLSRoute{}) { + bdr.Watches( + &gatewayv1alpha2.TLSRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ) + } + if pkgutils.HasAPIResource(mgr, &gatewayv1alpha2.UDPRoute{}) { + bdr.Watches( + &gatewayv1alpha2.UDPRoute{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewaysForStatusParentRefs), + ) + } return bdr.Complete(r) } diff --git a/internal/controller/gatewayproxy_controller.go b/internal/controller/gatewayproxy_controller.go index 5ab1d877..613f853b 100644 --- a/internal/controller/gatewayproxy_controller.go +++ b/internal/controller/gatewayproxy_controller.go @@ -53,6 +53,7 @@ type GatewayProxyController struct { Log logr.Logger Provider provider.Provider +<<<<<<< HEAD ICGV schema.GroupVersion // supportsEndpointSlice indicates whether the cluster supports EndpointSlice API supportsEndpointSlice bool @@ -81,6 +82,16 @@ func (r *GatewayProxyController) SetupWithManager(mrg ctrl.Manager) error { } bdr := ctrl.NewControllerManagedBy(mrg). +======= + disableGatewayAPI bool +} + +func (r *GatewayProxyController) SetupWithManager(mrg ctrl.Manager) error { + if config.ControllerConfig.DisableGatewayAPI || !pkgutils.HasAPIResource(mrg, &gatewayv1.Gateway{}) { + r.disableGatewayAPI = true + } + builder := ctrl.NewControllerManagedBy(mrg). +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) For(&v1alpha1.GatewayProxy{}). WithEventFilter(predicate.Or(eventFilters...)). Watches(&corev1.Service{}, @@ -110,7 +121,19 @@ func (r *GatewayProxyController) SetupWithManager(mrg ctrl.Manager) error { Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForSecret), ). +<<<<<<< HEAD Complete(r) +======= + Watches(&networkingv1.IngressClass{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForIngressClass), + ) + if !r.disableGatewayAPI { + builder.Watches(&gatewayv1.Gateway{}, + handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesByGateway), + ) + } + return builder.Complete(r) +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) } func (r *GatewayProxyController) Reconcile(ctx context.Context, req ctrl.Request) (reconcile.Result, error) { @@ -170,8 +193,18 @@ func (r *GatewayProxyController) Reconcile(ctx context.Context, req ctrl.Request indexKey := indexer.GenIndexKey(gp.GetNamespace(), gp.GetName()) // list Gateways that reference the GatewayProxy +<<<<<<< HEAD if r.supportsGateway { var gatewayList gatewayv1.GatewayList +======= + var ( + gatewayList gatewayv1.GatewayList + ingressClassList networkingv1.IngressClassList + indexKey = indexer.GenIndexKey(gp.GetNamespace(), gp.GetName()) + ) + + if !r.disableGatewayAPI { +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) if err := r.List(ctx, &gatewayList, client.MatchingFields{indexer.ParametersRef: indexKey}); err != nil { r.Log.Error(err, "failed to list GatewayList") return ctrl.Result{}, nil @@ -195,6 +228,7 @@ func (r *GatewayProxyController) Reconcile(ctx context.Context, req ctrl.Request tctx.GatewayProxyReferrers[req.NamespacedName] = append(tctx.GatewayProxyReferrers[req.NamespacedName], utils.NamespacedNameKind(&item)) } } +<<<<<<< HEAD } switch r.ICGV.String() { @@ -227,6 +261,24 @@ func (r *GatewayProxyController) Reconcile(ctx context.Context, req ctrl.Request tctx.GatewayProxyReferrers[req.NamespacedName] = append(tctx.GatewayProxyReferrers[req.NamespacedName], utils.NamespacedNameKind(&item)) } } +======= + r.Log.V(1).Info("found Gateways for GatewayProxy", "gatewayproxy", req.String(), "gateways", len(gatewayList.Items), "gatewayclasses", len(gatewayclassList.Items), "ingressclasses", len(ingressClassList.Items)) + } + + // list IngressClasses that reference the GatewayProxy + if err := r.List(ctx, &ingressClassList, client.MatchingFields{indexer.IngressClassParametersRef: indexKey}); err != nil { + r.Log.Error(err, "failed to list IngressClassList") + return reconcile.Result{}, err + } + + for _, item := range ingressClassList.Items { + if item.Spec.Controller != config.GetControllerName() { + continue + } + tctx.GatewayProxyReferrers[req.NamespacedName] = append(tctx.GatewayProxyReferrers[req.NamespacedName], utils.NamespacedNameKind(&item)) + } + +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) if len(tctx.GatewayProxyReferrers[req.NamespacedName]) == 0 { return ctrl.Result{}, nil } diff --git a/internal/controller/indexer/indexer.go b/internal/controller/indexer/indexer.go index 0acdf693..a5d8b125 100644 --- a/internal/controller/indexer/indexer.go +++ b/internal/controller/indexer/indexer.go @@ -36,7 +36,10 @@ import ( "github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations" "github.com/apache/apisix-ingress-controller/internal/controller/config" internaltypes "github.com/apache/apisix-ingress-controller/internal/types" +<<<<<<< HEAD k8sutils "github.com/apache/apisix-ingress-controller/internal/utils" +======= +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) "github.com/apache/apisix-ingress-controller/pkg/utils" ) @@ -58,6 +61,7 @@ const ( ControllerName = "controllerName" ) +<<<<<<< HEAD func SetupIndexer(mgr ctrl.Manager) error { setupLog := ctrl.LoggerFrom(context.Background()).WithName("indexer-setup") @@ -106,9 +110,66 @@ func SetupIndexer(mgr ctrl.Manager) error { setupApisixTlsIndexer, setupApisixConsumerIndexer, setupApisixGlobalRuleIndexer, +======= +func SetupAPIv1alpha1Indexer(mgr ctrl.Manager) error { + setupLog := ctrl.LoggerFrom(context.Background()).WithName("indexer").WithName("apiv1alpha1") + for resource, setup := range map[client.Object]func(ctrl.Manager) error{ + &v1alpha1.BackendTrafficPolicy{}: setupBackendTrafficPolicyIndexer, + &v1alpha1.Consumer{}: setupConsumerIndexer, + &v1alpha1.GatewayProxy{}: setupGatewayProxyIndexer, +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) } { - if err := setup(mgr); err != nil { - return err + if utils.HasAPIResource(mgr, resource) { + if err := setup(mgr); err != nil { + return err + } + } else { + setupLog.Info("Skipping indexer setup, API not found in cluster", "api", utils.FormatGVK(resource)) + } + } + return nil +} +func SetupAPIv2Indexer(mgr ctrl.Manager) error { + setupLog := ctrl.LoggerFrom(context.Background()).WithName("indexer").WithName("apiv2") + + for resource, setup := range map[client.Object]func(ctrl.Manager) error{ + &networkingv1.IngressClass{}: setupIngressClassIndexer, + &networkingv1.Ingress{}: setupIngressIndexer, + &apiv2.ApisixConsumer{}: setupApisixConsumerIndexer, + &apiv2.ApisixRoute{}: setupApisixRouteIndexer, + &apiv2.ApisixPluginConfig{}: setupApisixPluginConfigIndexer, + &apiv2.ApisixTls{}: setupApisixTlsIndexer, + &apiv2.ApisixGlobalRule{}: setupApisixGlobalRuleIndexer, + } { + if utils.HasAPIResource(mgr, resource) { + if err := setup(mgr); err != nil { + return err + } + } else { + setupLog.Info("Skipping indexer setup, API not found in cluster", "api", utils.FormatGVK(resource)) + } + } + return nil +} + +func SetupGatewayAPIIndexer(mgr ctrl.Manager) error { + setupLog := ctrl.LoggerFrom(context.Background()).WithName("indexer").WithName("gatewayapi") + + for resource, setup := range map[client.Object]func(ctrl.Manager) error{ + &gatewayv1.Gateway{}: setupGatewayIndexer, + &gatewayv1.HTTPRoute{}: setupHTTPRouteIndexer, + &gatewayv1.GRPCRoute{}: setupGRPCRouteIndexer, + &gatewayv1alpha2.TCPRoute{}: setupTCPRouteIndexer, + &gatewayv1alpha2.UDPRoute{}: setupUDPRouteIndexer, + &gatewayv1alpha2.TLSRoute{}: setupTLSRouteIndexer, + &gatewayv1.GatewayClass{}: setupGatewayClassIndexer, + } { + if utils.HasAPIResource(mgr, resource) { + if err := setup(mgr); err != nil { + return err + } + } else { + setupLog.Info("Skipping indexer setup, API not found in cluster", "api", utils.FormatGVK(resource)) } } return nil @@ -153,6 +214,15 @@ func setupGatewayIndexer(mgr ctrl.Manager) error { return err } + if err := mgr.GetFieldIndexer().IndexField( + context.Background(), + &gatewayv1.Gateway{}, + SecretIndexRef, + GatewaySecretIndexFunc, + ); err != nil { + return err + } + return nil } diff --git a/internal/manager/controllers.go b/internal/manager/controllers.go index f8251e1d..96f297b7 100644 --- a/internal/manager/controllers.go +++ b/internal/manager/controllers.go @@ -42,6 +42,10 @@ import ( "github.com/apache/apisix-ingress-controller/internal/provider" types "github.com/apache/apisix-ingress-controller/internal/types" "github.com/apache/apisix-ingress-controller/pkg/utils" +<<<<<<< HEAD +======= + "github.com/go-logr/logr" +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) ) // K8s @@ -108,10 +112,14 @@ type Controller interface { } func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Provider, updater status.Updater, readier readiness.ReadinessManager) ([]Controller, error) { - if err := indexer.SetupIndexer(mgr); err != nil { + setupLog := ctrl.LoggerFrom(ctx).WithName("setup") + + if err := indexer.SetupAPIv1alpha1Indexer(mgr); err != nil { + setupLog.Error(err, "failed to setup v1alpha1 indexer") return nil, err } +<<<<<<< HEAD setupLog := ctrl.LoggerFrom(ctx).WithName("setup") var controllers []Controller @@ -120,10 +128,53 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro setupLog.Info("IngressClass v1 not found, falling back to IngressClass v1beta1") icgv = netv1beta1.SchemeGroupVersion controllers = append(controllers, &controller.IngressClassV1beta1Reconciler{ +======= + runnables := []Controller{} + if controllers, err := setupGatewayAPIControllers(ctx, mgr, pro, updater, readier); err != nil { + setupLog.Error(err, "failed to setup Gateway API controllers") + return nil, err + } else { + runnables = append(runnables, controllers...) + } + + if controllers, err := setupAPIv2Controllers(ctx, mgr, pro, updater, readier); err != nil { + setupLog.Error(err, "failed to setup API v2 controllers") + return nil, err + } else { + runnables = append(runnables, controllers...) + } + + // required controller + runnables = append(runnables, &controller.GatewayProxyController{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindGatewayProxy), + Provider: pro, + }) + return runnables, nil +} + +func setupGatewayAPIControllers(ctx context.Context, mgr manager.Manager, pro provider.Provider, updater status.Updater, readier readiness.ReadinessManager) ([]Controller, error) { + if err := indexer.SetupGatewayAPIIndexer(mgr); err != nil { + return nil, err + } + + setupLog := ctrl.LoggerFrom(ctx).WithName("setup").WithName("gatewayapi") + runnables := []Controller{} + for resource, controller := range map[client.Object]Controller{ + &gatewayv1.GatewayClass{}: &controller.GatewayClassReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindGatewayClass), + Updater: updater, + }, + &gatewayv1.Gateway{}: &controller.GatewayReconciler{ +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindIngressClass), Provider: pro, +<<<<<<< HEAD }) } @@ -207,16 +258,87 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindIngress), +======= + Updater: updater, + }, + &gatewayv1.HTTPRoute{}: &controller.HTTPRouteReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindHTTPRoute), +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) + Provider: pro, + Updater: updater, + Readier: readier, + }, +<<<<<<< HEAD +======= + &gatewayv1.GRPCRoute{}: &controller.GRPCRouteReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindGRPCRoute), + Provider: pro, + Updater: updater, + Readier: readier, + }, + &gatewayv1alpha2.TCPRoute{}: &controller.TCPRouteReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindTCPRoute), + Provider: pro, + Updater: updater, + Readier: readier, + }, + &gatewayv1alpha2.UDPRoute{}: &controller.UDPRouteReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindUDPRoute), + Provider: pro, + Updater: updater, + Readier: readier, + }, + &gatewayv1alpha2.TLSRoute{}: &controller.TLSRouteReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindTLSRoute), Provider: pro, Updater: updater, Readier: readier, }, + &v1alpha1.Consumer{}: &controller.ConsumerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindConsumer), + Provider: pro, + Updater: updater, + Readier: readier, + }, + } { + if utils.HasAPIResource(mgr, resource) { + runnables = append(runnables, controller) + } else { + setupLog.Info("Skipping indexer setup, API not found in cluster", "api", utils.FormatGVK(resource)) + } + } + return runnables, nil +} + +func setupAPIv2Controllers(ctx context.Context, mgr manager.Manager, pro provider.Provider, updater status.Updater, readier readiness.ReadinessManager) ([]Controller, error) { + if err := indexer.SetupAPIv2Indexer(mgr); err != nil { + return nil, err + } + + setupLog := ctrl.LoggerFrom(ctx).WithName("setup").WithName("apiv2") + + runnables := []Controller{} + for resource, controller := range map[client.Object]Controller{ +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) &netv1.IngressClass{}: &controller.IngressClassReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindIngressClass), Provider: pro, }, +<<<<<<< HEAD } { if utils.HasAPIResource(mgr, resource) { controllers = append(controllers, controller) @@ -228,13 +350,24 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro controllers = append(controllers, []Controller{ // Gateway Proxy Controller - always register this as it is core to the controller &controller.GatewayProxyController{ +======= + &netv1.Ingress{}: &controller.IngressReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindIngress), + Provider: pro, + Updater: updater, + Readier: readier, + }, + &apiv2.ApisixGlobalRule{}: &controller.ApisixGlobalRuleReconciler{ +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindGatewayProxy), Provider: pro, ICGV: icgv, }, - &controller.ApisixRouteReconciler{ + &apiv2.ApisixRoute{}: &controller.ApisixRouteReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindApisixRoute), @@ -252,7 +385,7 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro Readier: readier, ICGV: icgv, }, - &controller.ApisixConsumerReconciler{ + &apiv2.ApisixConsumer{}: &controller.ApisixConsumerReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindApisixConsumer), @@ -261,14 +394,14 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro Readier: readier, ICGV: icgv, }, - &controller.ApisixPluginConfigReconciler{ + &apiv2.ApisixPluginConfig{}: &controller.ApisixPluginConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindApisixPluginConfig), Updater: updater, ICGV: icgv, }, - &controller.ApisixTlsReconciler{ + &apiv2.ApisixTls{}: &controller.ApisixTlsReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindApisixTls), @@ -277,11 +410,12 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro Readier: readier, ICGV: icgv, }, - &controller.ApisixUpstreamReconciler{ + &apiv2.ApisixUpstream{}: &controller.ApisixUpstreamReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName(types.KindApisixUpstream), Updater: updater, +<<<<<<< HEAD ICGV: icgv, }, }...) @@ -375,13 +509,128 @@ func registerV1alpha1ForReadinessGVK(mgr manager.Manager, readier readiness.Read c := mgr.GetClient() readier.RegisterGVK(readiness.GVKConfig{ GVKs: gvks, +======= + }, + } { + if utils.HasAPIResource(mgr, resource) { + runnables = append(runnables, controller) + } else { + setupLog.Info("Skipping indexer setup, API not found in cluster", "api", utils.FormatGVK(resource)) + } + } + return runnables, nil +} + +func registerReadiness(mgr manager.Manager, readier readiness.ReadinessManager) { + log := ctrl.LoggerFrom(context.Background()).WithName("readiness") + + registerAPIv2ForReadiness(mgr, log, readier) + if !config.ControllerConfig.DisableGatewayAPI { + registerGatewayAPIForReadiness(mgr, log, readier) + } + registerAPIv1alpha1ForReadiness(mgr, log, readier) +} + +func registerGatewayAPIForReadiness( + mgr manager.Manager, + log logr.Logger, + readier readiness.ReadinessManager, +) { + var installed []schema.GroupVersionKind + for _, resource := range []client.Object{ + &gatewayv1.HTTPRoute{}, + &gatewayv1.GRPCRoute{}, + &gatewayv1alpha2.TCPRoute{}, + &gatewayv1alpha2.UDPRoute{}, + &gatewayv1alpha2.TLSRoute{}, + } { + gvk := types.GvkOf(resource) + if utils.HasAPIResource(mgr, resource) { + installed = append(installed, gvk) + } else { + log.Info("Skipping readiness registration, API not found", "gvk", gvk) + } + } + if len(installed) == 0 { + return + } + + readier.RegisterGVK(readiness.GVKConfig{GVKs: installed}) +} + +func registerAPIv2ForReadiness( + mgr manager.Manager, + log logr.Logger, + readier readiness.ReadinessManager, +) { + var installed []schema.GroupVersionKind + for _, resource := range []client.Object{ + &netv1.Ingress{}, + &apiv2.ApisixRoute{}, + &apiv2.ApisixGlobalRule{}, + &apiv2.ApisixPluginConfig{}, + &apiv2.ApisixTls{}, + &apiv2.ApisixConsumer{}, + &apiv2.ApisixUpstream{}, + } { + gvk := types.GvkOf(resource) + if utils.HasAPIResource(mgr, resource) { + installed = append(installed, gvk) + } else { + log.Info("Skipping readiness registration, API not found", "gvk", gvk) + } + } + + if len(installed) == 0 { + return + } + + readier.RegisterGVK(readiness.GVKConfig{ + GVKs: installed, + Filter: readiness.GVKFilter(func(obj *unstructured.Unstructured) bool { + icName, _, _ := unstructured.NestedString(obj.Object, "spec", "ingressClassName") + ingressClass, _ := controller.FindMatchingIngressClassByName(context.Background(), mgr.GetClient(), log, icName) + return ingressClass != nil + }), + }) +} + +func registerAPIv1alpha1ForReadiness( + mgr manager.Manager, + log logr.Logger, + readier readiness.ReadinessManager, +) { + var installed []schema.GroupVersionKind + for _, resource := range []client.Object{ + &v1alpha1.Consumer{}, + } { + gvk := types.GvkOf(resource) + if utils.HasAPIResource(mgr, resource) { + installed = append(installed, gvk) + } else { + log.Info("Skipping readiness registration, API not found", "gvk", gvk) + } + } + if len(installed) == 0 { + return + } + + readier.RegisterGVK(readiness.GVKConfig{ + GVKs: installed, +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) Filter: readiness.GVKFilter(func(obj *unstructured.Unstructured) bool { consumer := &v1alpha1.Consumer{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, consumer); err != nil { return false } +<<<<<<< HEAD return controller.MatchConsumerGatewayRef(context.Background(), c, log, consumer) }), }) log.Info("Registered v1alpha1 GVKs for readiness checks", "gvks", gvks) +======= + return controller.MatchConsumerGatewayRef(context.Background(), mgr.GetClient(), log, consumer) + }), + }) +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) } diff --git a/internal/manager/run.go b/internal/manager/run.go index 207cd5fd..cd9519c5 100644 --- a/internal/manager/run.go +++ b/internal/manager/run.go @@ -169,7 +169,11 @@ func Run(ctx context.Context, logger logr.Logger) error { } readier := readiness.NewReadinessManager(mgr.GetClient(), logger) +<<<<<<< HEAD registerReadinessGVK(mgr, readier) +======= + registerReadiness(mgr, readier) +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) if err := mgr.Add(readier); err != nil { setupLog.Error(err, "unable to add readiness manager") diff --git a/pkg/utils/k8s.go b/pkg/utils/k8s.go new file mode 100644 index 00000000..425f2ff9 --- /dev/null +++ b/pkg/utils/k8s.go @@ -0,0 +1,83 @@ +// 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 ( + "github.com/go-logr/logr" + "k8s.io/client-go/discovery" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + + "github.com/apache/apisix-ingress-controller/internal/types" +) + +// HasAPIResource checks if a specific API resource is available in the current cluster. +// It uses the Discovery API to query the cluster's available resources and returns true +// if the resource is found, false otherwise. +func HasAPIResource(mgr ctrl.Manager, obj client.Object) bool { + return HasAPIResourceWithLogger(mgr, obj, ctrl.Log.WithName("api-detection")) +} + +// HasAPIResourceWithLogger is the same as HasAPIResource but accepts a custom logger +// for more detailed debugging information. +func HasAPIResourceWithLogger(mgr ctrl.Manager, obj client.Object, logger logr.Logger) bool { + gvk, err := apiutil.GVKForObject(obj, mgr.GetScheme()) + if err != nil { + logger.Info("cannot derive GVK from scheme", "error", err) + return false + } + + groupVersion := gvk.GroupVersion().String() + + logger = logger.WithValues( + "kind", gvk.Kind, + "group", gvk.Group, + "version", gvk.Version, + "groupVersion", groupVersion, + ) + + // Create discovery client + discoveryClient, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig()) + if err != nil { + logger.Info("failed to create discovery client", "error", err) + return false + } + + // Query server resources for the specific group/version + apiResources, err := discoveryClient.ServerResourcesForGroupVersion(groupVersion) + if err != nil { + logger.Info("group/version not available in cluster", "error", err) + return false + } + + // Check if the specific kind exists in the resource list + for _, res := range apiResources.APIResources { + if res.Kind == gvk.Kind { + return true + } + } + + logger.Info("API resource kind not found in group/version") + return false +} + +func FormatGVK(obj client.Object) string { + gvk := types.GvkOf(obj) + return gvk.String() +} diff --git a/test/conformance/suite_test.go b/test/conformance/suite_test.go index e7ab91b1..0ae3bc70 100644 --- a/test/conformance/suite_test.go +++ b/test/conformance/suite_test.go @@ -170,6 +170,7 @@ func TestMain(m *testing.M) { InitSyncDelay: 20 * time.Minute, ProviderType: framework.ProviderType, ProviderSyncPeriod: 1 * time.Hour, + DisableGatewayAPI: framework.DisableGatewayAPI, }) adminEndpoint := fmt.Sprintf("http://%s.%s:9180", svc.Name, namespace) diff --git a/test/e2e/framework/apisix_consts.go b/test/e2e/framework/apisix_consts.go index 9277a222..82415da4 100644 --- a/test/e2e/framework/apisix_consts.go +++ b/test/e2e/framework/apisix_consts.go @@ -27,7 +27,12 @@ import ( ) var ( +<<<<<<< HEAD ProviderType = cmp.Or(os.Getenv("PROVIDER_TYPE"), ProviderTypeAPISIXStandalone) +======= + ProviderType = cmp.Or(os.Getenv("PROVIDER_TYPE"), "apisix") + DisableGatewayAPI = os.Getenv("DISABLE_GATEWAY_API") == "true" +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) ) const ( diff --git a/test/e2e/framework/ingress.go b/test/e2e/framework/ingress.go index 9c2e7199..fe627be8 100644 --- a/test/e2e/framework/ingress.go +++ b/test/e2e/framework/ingress.go @@ -53,6 +53,7 @@ type IngressDeployOpts struct { InitSyncDelay time.Duration WebhookEnable bool WebhookPort int + DisableGatewayAPI bool } func (f *Framework) DeployIngress(opts IngressDeployOpts) { diff --git a/test/e2e/framework/manifests/ingress.yaml b/test/e2e/framework/manifests/ingress.yaml index b7838494..75b84cf2 100644 --- a/test/e2e/framework/manifests/ingress.yaml +++ b/test/e2e/framework/manifests/ingress.yaml @@ -288,6 +288,7 @@ data: log_level: "debug" controller_name: {{ .ControllerName | default "apisix.apache.org/apisix-ingress-controller" }} leader_election_id: "apisix-ingress-controller-leader" + disable_gateway_api: {{ .DisableGatewayAPI | default false }} leader_election: lease_duration: 10s # lease_duration is the duration that non-leader candidates will wait # after observing a leadership renewal until attempting to acquire leadership of a diff --git a/test/e2e/scaffold/apisix_deployer.go b/test/e2e/scaffold/apisix_deployer.go index 62221879..4d121025 100644 --- a/test/e2e/scaffold/apisix_deployer.go +++ b/test/e2e/scaffold/apisix_deployer.go @@ -270,6 +270,7 @@ func (s *APISIXDeployer) DeployIngress() { Namespace: s.namespace, Replicas: ptr.To(1), WebhookEnable: s.runtimeOpts.EnableWebhook, + DisableGatewayAPI: framework.DisableGatewayAPI, }) } @@ -283,7 +284,12 @@ func (s *APISIXDeployer) ScaleIngress(replicas int) { ProviderType: framework.ProviderType, ProviderSyncPeriod: syncPeriod, Namespace: s.namespace, +<<<<<<< HEAD Replicas: &replicas, +======= + Replicas: ptr.To(replicas), + DisableGatewayAPI: framework.DisableGatewayAPI, +>>>>>>> 4f9cd000 (feat: support disable gateway-api (#2672)) }) }