Skip to content

feat: support ingress translator #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 7, 2025
23 changes: 21 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ else
GOBIN=$(shell go env GOBIN)
endif

GOOS ?= linux
GOARCH ?= amd64

ifeq ($(shell uname -s),Darwin)
GOOS = darwin
endif

ifeq ($(shell uname -m),arm64)
GOARCH = arm64
endif
ifeq ($(shell uname -m), aarch64)
GOARCH = arm64
endif

# CONTAINER_TOOL defines the container tool to be used for building images.
# Be aware that the target commands are only tested with Docker which is
# scaffolded by default. However, you might want to replace it to use other
Expand Down Expand Up @@ -142,7 +156,7 @@ pull-infra-images:

.PHONY: build
build: manifests generate fmt vet ## Build manager binary.
CGO_ENABLED=0 go build -o bin/api7-ingress-controller -ldflags $(GO_LDFLAGS) cmd/main.go
GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 go build -o bin/api7-ingress-controller -ldflags $(GO_LDFLAGS) cmd/main.go

linux-build:
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/api7-ingress-controller -ldflags $(GO_LDFLAGS) cmd/main.go
Expand All @@ -158,7 +172,7 @@ run: manifests generate fmt vet ## Run a controller from your host.
# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
.PHONY: docker-build
docker-build: build ## Build docker image with the manager.
docker-build: set-e2e-goos build ## Build docker image with the manager.
$(CONTAINER_TOOL) build -t ${IMG} -f Dockerfile .

.PHONY: docker-push
Expand Down Expand Up @@ -268,6 +282,11 @@ gofmt: ## Apply go fmt
@go fmt ./...
.PHONY: gofmt

set-e2e-goos:
$(eval GOOS=linux)
@echo "e2e GOOS: $(GOOS)"
.PHONY: set-e2e-goos

# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary
# $2 - package url which can be installed
Expand Down
2 changes: 1 addition & 1 deletion api/adc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ type Upstream struct {

HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"`
Key string `json:"key,omitempty" yaml:"key,omitempty"`
Nodes UpstreamNodes `json:"nodes,omitempty" yaml:"nodes,omitempty"`
Nodes UpstreamNodes `json:"nodes" yaml:"nodes"`
PassHost *PassHost `json:"pass_host,omitempty" yaml:"pass_host,omitempty"`
Retries *int64 `json:"retries,omitempty" yaml:"retries,omitempty"`
RetryTimeout *float64 `json:"retry_timeout,omitempty" yaml:"retry_timeout,omitempty"`
Expand Down
3 changes: 0 additions & 3 deletions config/samples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ log_level: "info" # The log level of the API7 Ingr
controller_name: gateway.api7.io/api7-ingress-controller # The controller name of the API7 Ingress Controller,
# which is used to identify the controller in the GatewayClass.
# The default value is "gateway.api7.io/api7-ingress-controller".

leader_election_id: "api7-ingress-controller-leader" # The leader election ID for the API7 Ingress Controller.
# The default value is "api7-ingress-controller-leader".

ingress_class: api7 # The ingress class name of the API7 Ingress Controller.
ingress_publish_service: "" # The service name of the ingress publish service.
ingress_status_address: [] # The status address of the ingress.

gateway_configs: # The configuration of the API7 Gateway.
- name: api7 # The name of the Gateway in the Gateway API.
control_plane:
Expand Down
113 changes: 82 additions & 31 deletions internal/controller/ingress_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/api7/api7-ingress-controller/internal/provider"
"github.com/api7/gopkg/pkg/log"
"github.com/go-logr/logr"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
networkingv1 "k8s.io/api/networking/v1"
Expand Down Expand Up @@ -41,7 +42,12 @@ func (r *IngressReconciler) SetupWithManager(mgr ctrl.Manager) error {
predicate.NewPredicateFuncs(r.checkIngressClass),
),
).
WithEventFilter(predicate.GenerationChangedPredicate{}).
WithEventFilter(
predicate.Or(
predicate.GenerationChangedPredicate{},
predicate.AnnotationChangedPredicate{},
),
).
Watches(
&networkingv1.IngressClass{},
handler.EnqueueRequestsFromMapFunc(r.listIngressForIngressClass),
Expand Down Expand Up @@ -134,16 +140,19 @@ func (r *IngressReconciler) checkIngressClass(obj client.Object) bool {
// find the ingress class that is marked as default
for _, ic := range ingressClassList.Items {
if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) {
log.Debugw("match the default ingress class")
return true
}
}

log.Debugw("no default ingress class found")
return false
}

configuredClass := config.GetIngressClass()
// if the ingress class name matches the configured ingress class name, return true
if *ingress.Spec.IngressClassName == configuredClass {
log.Debugw("match the configured ingress class name")
return true
}

Expand All @@ -168,25 +177,55 @@ func (r *IngressReconciler) matchesIngressController(obj client.Object) bool {
}

// listIngressForIngressClass list all ingresses that use a specific ingress class
func (r *IngressReconciler) listIngressForIngressClass(ctx context.Context, ingressClass client.Object) []reconcile.Request {
ingressList := &networkingv1.IngressList{}
if err := r.List(ctx, ingressList, client.MatchingFields{
indexer.IngressClassRef: ingressClass.GetName(),
}); err != nil {
r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName())
func (r *IngressReconciler) listIngressForIngressClass(ctx context.Context, obj client.Object) []reconcile.Request {
ingressClass, ok := obj.(*networkingv1.IngressClass)
if !ok {
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass")
return nil
}

requests := make([]reconcile.Request, 0, len(ingressList.Items))
for _, ingress := range ingressList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
// check if the ingress class is the default ingress class
if IsDefaultIngressClass(ingressClass) {
ingressList := &networkingv1.IngressList{}
if err := r.List(ctx, ingressList); err != nil {
r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName())
return nil
}

requests := make([]reconcile.Request, 0, len(ingressList.Items))
for _, ingress := range ingressList.Items {
if ingress.Spec.IngressClassName == nil || *ingress.Spec.IngressClassName == "" ||
*ingress.Spec.IngressClassName == ingressClass.GetName() {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
}
}
return requests
} else {
ingressList := &networkingv1.IngressList{}
if err := r.List(ctx, ingressList, client.MatchingFields{
indexer.IngressClassRef: ingressClass.GetName(),
}); err != nil {
r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName())
return nil
}

requests := make([]reconcile.Request, 0, len(ingressList.Items))
for _, ingress := range ingressList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
}

return requests
}
return requests
}

// listIngressesByService list all ingresses that use a specific service
Expand All @@ -210,12 +249,14 @@ func (r *IngressReconciler) listIngressesByService(ctx context.Context, obj clie

requests := make([]reconcile.Request, 0, len(ingressList.Items))
for _, ingress := range ingressList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
if r.checkIngressClass(&ingress) {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
}
}
return requests
}
Expand All @@ -241,12 +282,14 @@ func (r *IngressReconciler) listIngressesBySecret(ctx context.Context, obj clien

requests := make([]reconcile.Request, 0, len(ingressList.Items))
for _, ingress := range ingressList.Items {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
if r.checkIngressClass(&ingress) {
requests = append(requests, reconcile.Request{
NamespacedName: client.ObjectKey{
Namespace: ingress.Namespace,
Name: ingress.Name,
},
})
}
}
return requests
}
Expand All @@ -267,6 +310,11 @@ func (r *IngressReconciler) processTLS(ctx context.Context, tctx *provider.Trans
return err
}

if secret.Data == nil {
log.Warnw("secret data is nil", zap.String("secret", secret.Namespace+"/"+secret.Name))
continue
}

// add the secret to the translate context
tctx.Secrets[types.NamespacedName{Namespace: ingress.Namespace, Name: tls.SecretName}] = &secret
}
Expand All @@ -283,19 +331,16 @@ func (r *IngressReconciler) processBackends(ctx context.Context, tctx *provider.
if rule.HTTP == nil {
continue
}

for _, path := range rule.HTTP.Paths {
if path.Backend.Service == nil {
continue
}

service := path.Backend.Service
if err := r.processBackendService(ctx, tctx, ingress.Namespace, service); err != nil {
terr = err
}
}
}

return terr
}

Expand Down Expand Up @@ -356,6 +401,11 @@ func (r *IngressReconciler) processBackendService(ctx context.Context, tctx *pro
Name: backendService.Name,
}] = endpointSliceList.Items

tctx.Services[client.ObjectKey{
Namespace: namespace,
Name: backendService.Name,
}] = &service

return nil
}

Expand Down Expand Up @@ -383,6 +433,7 @@ func (r *IngressReconciler) updateStatus(ctx context.Context, ingress *networkin
if err != nil {
return fmt.Errorf("invalid ingress-publish-service format: %s, expected format: namespace/name", publishService)
}
// if the namespace is not specified, use the ingress namespace
if namespace == "" {
namespace = ingress.Namespace
}
Expand Down
3 changes: 3 additions & 0 deletions internal/provider/adc/adc.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ func (d *adcClient) Delete(ctx context.Context, obj client.Object) error {
labels = label.GenLabel(obj)
case *gatewayv1.Gateway:
// delete all resources
case *networkingv1.Ingress:
resourceTypes = append(resourceTypes, "service", "ssl")
labels = label.GenLabel(obj)
case *v1alpha1.Consumer:
resourceTypes = append(resourceTypes, "consumer")
labels = label.GenLabel(obj)
Expand Down
Loading
Loading