Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/apisix-conformance-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
ARCH: amd64
ENABLE_PROXY: "false"
BASE_IMAGE_TAG: "debug"
ADC_VERSION: "dev"
# ADC_VERSION: "dev"
run: |
echo "building images..."
make build-image
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/apisix-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
ARCH: amd64
ENABLE_PROXY: "false"
BASE_IMAGE_TAG: "debug"
ADC_VERSION: "dev"
# ADC_VERSION: "dev"
run: |
echo "building images..."
make build-image
Expand Down
186 changes: 112 additions & 74 deletions internal/manager/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/apache/apisix-ingress-controller/internal/manager/readiness"
"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"
)

// K8s
Expand Down Expand Up @@ -97,101 +98,138 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro
if err := indexer.SetupIndexer(mgr); err != nil {
return nil, err
}
return []Controller{
&controller.GatewayClassReconciler{

setupLog := ctrl.LoggerFrom(ctx).WithName("setup")
var controllers []Controller

// Gateway API Controllers - conditional registration based on API availability
if utils.HasAPIResource(mgr, &gatewayv1.GatewayClass{}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic here is basically similar, we can write it like this:

	controllers := []Controller{}
	for resource, controller := range map[client.Object]Controller{
		&gatewayv1.GatewayClass{}: &controller.GatewayClassReconciler{
			Client:  mgr.GetClient(),
			Scheme:  mgr.GetScheme(),
			Updater: updater,
		},
		&gatewayv1.Gateway{}: &controller.GatewayReconciler{
			Client:  mgr.GetClient(),
			Scheme:  mgr.GetScheme(),
			Updater: updater,
		},
	} {
		if !utils.HasAPIResource(mgr, resource) {
			// log
		} else {
			controllers = append(controllers, controller)
		}
	}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

controllers = append(controllers, &controller.GatewayClassReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("GatewayClass"),
Updater: updater,
},
&controller.GatewayReconciler{
})
} else {
setupLog.Info("Skipping GatewayClass controller setup, API not found in cluster", "api", "gateway.networking.k8s.io/v1.GatewayClass")
}

if utils.HasAPIResource(mgr, &gatewayv1.Gateway{}) {
controllers = append(controllers, &controller.GatewayReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Gateway"),
Provider: pro,
Updater: updater,
},
&controller.HTTPRouteReconciler{
})
} else {
setupLog.Info("Skipping Gateway controller setup, API not found in cluster", "api", "gateway.networking.k8s.io/v1.Gateway")
}

if utils.HasAPIResource(mgr, &gatewayv1.HTTPRoute{}) {
controllers = append(controllers, &controller.HTTPRouteReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("HTTPRoute"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.IngressReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Ingress"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.ConsumerReconciler{
})
} else {
setupLog.Info("Skipping HTTPRoute controller setup, API not found in cluster", "api", "gateway.networking.k8s.io/v1.HTTPRoute")
}

// Core Kubernetes Controllers - always register these
controllers = append(controllers, &controller.IngressReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Ingress"),
Provider: pro,
Updater: updater,
Readier: readier,
})

controllers = append(controllers, &controller.IngressClassReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("IngressClass"),
Provider: pro,
})

// v1alpha1 Extension Controllers - conditional registration
if utils.HasAPIResource(mgr, &v1alpha1.Consumer{}) {
controllers = append(controllers, &controller.ConsumerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Consumer"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.IngressClassReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("IngressClass"),
Provider: pro,
},
&controller.ApisixGlobalRuleReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixGlobalRule"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.ApisixRouteReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixRoute"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.ApisixConsumerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixConsumer"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.ApisixPluginConfigReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixPluginConfig"),
Updater: updater,
},
&controller.ApisixTlsReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixTls"),
Provider: pro,
Updater: updater,
Readier: readier,
},
&controller.ApisixUpstreamReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixUpstream"),
Updater: updater,
},
&controller.GatewayProxyController{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("GatewayProxy"),
Provider: pro,
},
}, nil
})
} else {
setupLog.Info("Skipping Consumer controller setup, API not found in cluster", "api", "apisix.apache.org/v1alpha1.Consumer")
}

controllers = append(controllers, &controller.GatewayProxyController{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("GatewayProxy"),
Provider: pro,
})

// APISIX v2 Controllers - always register these as they are core to the controller
controllers = append(controllers, &controller.ApisixGlobalRuleReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixGlobalRule"),
Provider: pro,
Updater: updater,
Readier: readier,
})

controllers = append(controllers, &controller.ApisixRouteReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixRoute"),
Provider: pro,
Updater: updater,
Readier: readier,
})

controllers = append(controllers, &controller.ApisixConsumerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixConsumer"),
Provider: pro,
Updater: updater,
Readier: readier,
})

controllers = append(controllers, &controller.ApisixPluginConfigReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixPluginConfig"),
Updater: updater,
})

controllers = append(controllers, &controller.ApisixTlsReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixTls"),
Provider: pro,
Updater: updater,
Readier: readier,
})

controllers = append(controllers, &controller.ApisixUpstreamReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixUpstream"),
Updater: updater,
})

setupLog.Info("Controllers setup completed", "total_controllers", len(controllers))
return controllers, nil
}

func registerReadinessGVK(c client.Client, readier readiness.ReadinessManager) {
Expand Down
72 changes: 72 additions & 0 deletions pkg/utils/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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"

"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 := types.GvkOf(obj)
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
}
Loading