Skip to content
Open
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
6 changes: 6 additions & 0 deletions config/samples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ exec_adc_timeout: 15s # The timeout for the ADC to execute.
disable_gateway_api: false # Whether to disable the Gateway API support.
# The default value is false.

listener_port_match_mode: "auto" # Mode for injecting server_port route vars from Gateway listener ports.
# - "auto": inject when parentRefs explicitly target listeners (sectionName/port) or when multiple listener ports are matched.
# - "explicit": inject only when parentRefs explicitly target listeners.
# - "off": never inject server_port vars.
# The default value is "auto".

provider:
type: "api7ee"

Expand Down
249 changes: 248 additions & 1 deletion internal/adc/translator/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import (

"github.com/incubator4/go-resty-expr/expr"
"github.com/stretchr/testify/assert"
"k8s.io/utils/ptr"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"

adctypes "github.com/apache/apisix-ingress-controller/api/adc"
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations"
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations/upstream"
"github.com/apache/apisix-ingress-controller/internal/controller/config"
)

type mockParser struct {
Expand Down Expand Up @@ -342,11 +345,255 @@ func TestTranslateIngressAnnotations(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
translator := &Translator{}
translator := &Translator{ListenerPortMatchMode: config.ListenerPortMatchModeAuto}
result := translator.TranslateIngressAnnotations(tt.anno)

assert.NotNil(t, result)
assert.Equal(t, tt.expected, result)
})
}
}

func TestAddServerPortVars(t *testing.T) {
tests := []struct {
name string
route *adctypes.Route
ports map[int32]struct{}
expected adctypes.Vars
}{
{
name: "empty ports map - no vars added",
route: &adctypes.Route{},
ports: map[int32]struct{}{},
expected: adctypes.Vars(nil),
},
{
name: "single port - uses == operator",
route: &adctypes.Route{},
ports: map[int32]struct{}{
9080: {},
},
expected: adctypes.Vars{
{
{StrVal: "server_port"},
{StrVal: "=="},
{StrVal: "9080"},
},
},
},
{
name: "two ports - uses 'in' operator",
route: &adctypes.Route{},
ports: map[int32]struct{}{
9080: {},
9081: {},
},
expected: adctypes.Vars{
{
{StrVal: "server_port"},
{StrVal: "in"},
{SliceVal: []adctypes.StringOrSlice{
{StrVal: "9080"},
{StrVal: "9081"},
}},
},
},
},
{
name: "three ports - uses 'in' operator",
route: &adctypes.Route{},
ports: map[int32]struct{}{
80: {},
443: {},
9080: {},
},
expected: adctypes.Vars{
{
{StrVal: "server_port"},
{StrVal: "in"},
{SliceVal: []adctypes.StringOrSlice{
{StrVal: "80"},
{StrVal: "443"},
{StrVal: "9080"},
}},
},
},
},
{
name: "vars are appended - preserves existing vars",
route: &adctypes.Route{
Vars: adctypes.Vars{
{
{StrVal: "uri"},
{StrVal: "~~"},
{StrVal: "^/api"},
},
},
},
ports: map[int32]struct{}{
9080: {},
},
expected: adctypes.Vars{
{
{StrVal: "uri"},
{StrVal: "~~"},
{StrVal: "^/api"},
},
{
{StrVal: "server_port"},
{StrVal: "=="},
{StrVal: "9080"},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
addServerPortVars(tt.route, tt.ports)
assert.Equal(t, tt.expected, tt.route.Vars)
})
}
}

func TestShouldInjectServerPortVars(t *testing.T) {
sectionName := gatewayv1.SectionName("http-main")
port := gatewayv1.PortNumber(9080)

tests := []struct {
name string
mode config.ListenerPortMatchMode
parentRefs []gatewayv1.ParentReference
ports map[int32]struct{}
expected bool
}{
{
name: "empty listener ports",
mode: config.ListenerPortMatchModeAuto,
ports: map[int32]struct{}{},
expected: false,
},
{
name: "single port without sectionName",
mode: config.ListenerPortMatchModeAuto,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw"},
},
ports: map[int32]struct{}{
9080: {},
},
expected: false,
},
{
name: "single port with sectionName",
mode: config.ListenerPortMatchModeAuto,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw", SectionName: &sectionName},
},
ports: map[int32]struct{}{
9080: {},
},
expected: true,
},
{
name: "multiple ports without sectionName",
mode: config.ListenerPortMatchModeAuto,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw"},
},
ports: map[int32]struct{}{
9080: {},
9081: {},
},
expected: true,
},
{
name: "explicit mode with multiple ports and no explicit target",
mode: config.ListenerPortMatchModeExplicit,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw"},
},
ports: map[int32]struct{}{
9080: {},
9081: {},
},
expected: false,
},
{
name: "explicit mode with parentRef.port",
mode: config.ListenerPortMatchModeExplicit,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw", Port: &port},
},
ports: map[int32]struct{}{
9080: {},
},
expected: true,
},
{
name: "explicit mode with single port and no explicit target",
mode: config.ListenerPortMatchModeExplicit,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw"},
},
ports: map[int32]struct{}{
9080: {},
},
expected: false,
},
{
name: "off mode ignores explicit target",
mode: config.ListenerPortMatchModeOff,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw", SectionName: &sectionName},
},
ports: map[int32]struct{}{
9080: {},
9081: {},
},
expected: false,
},
{
name: "off mode ignores explicit parentRef.port target",
mode: config.ListenerPortMatchModeOff,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw", Port: &port},
},
ports: map[int32]struct{}{
9080: {},
},
expected: false,
},
{
name: "explicit mode: non-Gateway parentRef with port is not treated as explicit target",
mode: config.ListenerPortMatchModeExplicit,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw"},
{Name: "svc", Kind: ptr.To(gatewayv1.Kind("Service")), Port: &port},
},
ports: map[int32]struct{}{
9080: {},
},
expected: false,
},
{
name: "auto mode: non-Gateway parentRef with port does not trigger single-port injection",
mode: config.ListenerPortMatchModeAuto,
parentRefs: []gatewayv1.ParentReference{
{Name: "gw"},
{Name: "svc", Kind: ptr.To(gatewayv1.Kind("Service")), Port: &port},
},
ports: map[int32]struct{}{
9080: {},
},
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
translator := &Translator{ListenerPortMatchMode: tt.mode}
assert.Equal(t, tt.expected, translator.shouldInjectServerPortVars(tt.parentRefs, tt.ports))
})
}
}
4 changes: 2 additions & 2 deletions internal/adc/translator/apisixconsumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
)

func TestTranslateApisixConsumer_UsesMetadataLabelsWithoutOverwritingControllerLabels(t *testing.T) {
translator := NewTranslator(logr.Discard())
translator := NewTranslator(logr.Discard(), "")
tctx := provider.NewDefaultTranslateContext(context.Background())

consumer := &apiv2.ApisixConsumer{
Expand All @@ -49,7 +49,7 @@ func TestTranslateApisixConsumer_UsesMetadataLabelsWithoutOverwritingControllerL
},
},
Spec: apiv2.ApisixConsumerSpec{
AuthParameter: apiv2.ApisixConsumerAuthParameter{
AuthParameter: &apiv2.ApisixConsumerAuthParameter{
BasicAuth: &apiv2.ApisixConsumerBasicAuth{
Value: &apiv2.ApisixConsumerBasicAuthValue{
Username: "demo",
Expand Down
6 changes: 3 additions & 3 deletions internal/adc/translator/apisixroute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

func TestBuildRoute_HostsNotSet(t *testing.T) {
translator := NewTranslator(logr.Discard())
translator := NewTranslator(logr.Discard(), "")

ar := &apiv2.ApisixRoute{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -60,7 +60,7 @@ func TestBuildRoute_HostsNotSet(t *testing.T) {
}

func TestBuildService_HostsSet(t *testing.T) {
translator := NewTranslator(logr.Discard())
translator := NewTranslator(logr.Discard(), "")

ar := &apiv2.ApisixRoute{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -84,7 +84,7 @@ func TestBuildService_HostsSet(t *testing.T) {
}

func TestBuildRoute_MetadataLabelsDoNotOverwriteControllerLabels(t *testing.T) {
translator := NewTranslator(logr.Discard())
translator := NewTranslator(logr.Discard(), "")

ar := &apiv2.ApisixRoute{
TypeMeta: metav1.TypeMeta{
Expand Down
2 changes: 1 addition & 1 deletion internal/adc/translator/consumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
)

func TestTranslateConsumerV1alpha1_UsesMetadataLabelsWithoutOverwritingControllerLabels(t *testing.T) {
translator := NewTranslator(logr.Discard())
translator := NewTranslator(logr.Discard(), "")
tctx := provider.NewDefaultTranslateContext(context.Background())

consumer := &v1alpha1.Consumer{
Expand Down
13 changes: 13 additions & 0 deletions internal/adc/translator/grpcroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,19 @@ func (t *Translator) TranslateGRPCRoute(tctx *provider.TranslateContext, grpcRou

routes = append(routes, route)
}

// Collect unique listener ports for port-based routing.
listenerPorts := make(map[int32]struct{})
for _, listener := range tctx.Listeners {
listenerPorts[int32(listener.Port)] = struct{}{}
}

if t.shouldInjectServerPortVars(tctx.RouteParentRefs, listenerPorts) {
for _, route := range routes {
addServerPortVars(route, listenerPorts)
}
}

service.Routes = routes

result.Services = append(result.Services, service)
Expand Down
Loading
Loading