Skip to content

Commit e611f14

Browse files
committed
Add support for multiple inferencepool backends
1 parent dbebd97 commit e611f14

File tree

10 files changed

+1049
-243
lines changed

10 files changed

+1049
-243
lines changed

internal/controller/nginx/config/generator.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ func (g GeneratorImpl) executeConfigTemplates(
173173
}
174174
}
175175

176+
// for fp, bytes := range fileBytes {
177+
// fmt.Println("Generated NGINX configuration file: " + fp)
178+
// fmt.Println(string(bytes))
179+
// }
180+
176181
var mgmtFiles []agent.File
177182
if g.plus {
178183
mgmtFiles = g.generateMgmtFiles(conf)

internal/controller/nginx/config/maps.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,21 @@ func createAddHeadersMap(name string) shared.Map {
185185

186186
// buildInferenceMaps creates maps for InferencePool Backends.
187187
func buildInferenceMaps(groups []dataplane.BackendGroup) []shared.Map {
188-
inferenceMaps := make([]shared.Map, 0, len(groups))
188+
uniqueMaps := make(map[string]shared.Map)
189189

190190
for _, group := range groups {
191191
for _, backend := range group.Backends {
192192
if backend.EndpointPickerConfig == nil || backend.EndpointPickerConfig.EndpointPickerRef == nil {
193193
continue
194194
}
195195

196+
backendVarName := strings.ReplaceAll(backend.UpstreamName, "-", "_")
197+
mapKey := backendVarName // Use this as the key to detect duplicates
198+
199+
// Skip if we've already processed this upstream
200+
if _, exists := uniqueMaps[mapKey]; exists {
201+
continue
202+
}
196203
// Decide what the map must return when the picker didn’t set a value.
197204
var defaultResult string
198205
switch backend.EndpointPickerConfig.EndpointPickerRef.FailureMode {
@@ -230,14 +237,18 @@ func buildInferenceMaps(groups []dataplane.BackendGroup) []shared.Map {
230237
Result: defaultResult,
231238
})
232239

233-
backendVarName := strings.ReplaceAll(backend.UpstreamName, "-", "_")
234-
235-
inferenceMaps = append(inferenceMaps, shared.Map{
240+
uniqueMaps[mapKey] = shared.Map{
236241
Source: `$inference_workload_endpoint`,
237242
Variable: fmt.Sprintf("$inference_backend_%s", backendVarName),
238243
Parameters: params,
239-
})
244+
}
240245
}
241246
}
247+
248+
inferenceMaps := make([]shared.Map, 0, len(uniqueMaps))
249+
for _, inferenceMap := range uniqueMaps {
250+
inferenceMaps = append(inferenceMaps, inferenceMap)
251+
}
252+
242253
return inferenceMaps
243254
}

internal/controller/nginx/config/maps_test.go

Lines changed: 167 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -397,55 +397,183 @@ func TestCreateStreamMapsWithEmpty(t *testing.T) {
397397

398398
func TestBuildInferenceMaps(t *testing.T) {
399399
t.Parallel()
400-
g := NewWithT(t)
401400

402-
group := dataplane.BackendGroup{
403-
Backends: []dataplane.Backend{
404-
{
405-
UpstreamName: "upstream1",
406-
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
407-
NsName: "default",
408-
EndpointPickerRef: &inference.EndpointPickerRef{
409-
FailureMode: inference.EndpointPickerFailClose,
401+
tests := []struct {
402+
expectedConfig map[string]struct {
403+
failureMode inference.EndpointPickerFailureMode
404+
defaultResult string
405+
}
406+
name string
407+
backendGroups []dataplane.BackendGroup
408+
expectedMaps int
409+
}{
410+
{
411+
name: "unique backends with different failure modes",
412+
backendGroups: []dataplane.BackendGroup{
413+
{
414+
Backends: []dataplane.Backend{
415+
{
416+
UpstreamName: "upstream1",
417+
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
418+
NsName: "default",
419+
EndpointPickerRef: &inference.EndpointPickerRef{
420+
FailureMode: inference.EndpointPickerFailClose,
421+
},
422+
},
423+
},
424+
{
425+
UpstreamName: "upstream2",
426+
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
427+
NsName: "default",
428+
EndpointPickerRef: &inference.EndpointPickerRef{
429+
FailureMode: inference.EndpointPickerFailOpen,
430+
},
431+
},
432+
},
433+
{
434+
UpstreamName: "upstream3",
435+
EndpointPickerConfig: nil,
436+
},
410437
},
411438
},
412439
},
413-
{
414-
UpstreamName: "upstream2",
415-
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
416-
NsName: "default",
417-
EndpointPickerRef: &inference.EndpointPickerRef{
418-
FailureMode: inference.EndpointPickerFailOpen,
440+
expectedMaps: 2,
441+
expectedConfig: map[string]struct {
442+
failureMode inference.EndpointPickerFailureMode
443+
defaultResult string
444+
}{
445+
"upstream1": {
446+
failureMode: inference.EndpointPickerFailClose,
447+
defaultResult: "invalid-backend-ref",
448+
},
449+
"upstream2": {
450+
failureMode: inference.EndpointPickerFailOpen,
451+
defaultResult: "upstream2",
452+
},
453+
},
454+
},
455+
{
456+
name: "duplicate upstreams should be deduplicated",
457+
backendGroups: []dataplane.BackendGroup{
458+
{
459+
Backends: []dataplane.Backend{
460+
{
461+
UpstreamName: "upstream1",
462+
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
463+
NsName: "default",
464+
EndpointPickerRef: &inference.EndpointPickerRef{
465+
FailureMode: inference.EndpointPickerFailClose,
466+
},
467+
},
468+
},
469+
{
470+
UpstreamName: "upstream1", // Duplicate
471+
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
472+
NsName: "default",
473+
EndpointPickerRef: &inference.EndpointPickerRef{
474+
FailureMode: inference.EndpointPickerFailClose,
475+
},
476+
},
477+
},
478+
},
479+
},
480+
{
481+
Backends: []dataplane.Backend{
482+
{
483+
UpstreamName: "upstream1", // Another duplicate
484+
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
485+
NsName: "default",
486+
EndpointPickerRef: &inference.EndpointPickerRef{
487+
FailureMode: inference.EndpointPickerFailClose,
488+
},
489+
},
490+
},
491+
{
492+
UpstreamName: "upstream2",
493+
EndpointPickerConfig: &dataplane.EndpointPickerConfig{
494+
NsName: "default",
495+
EndpointPickerRef: &inference.EndpointPickerRef{
496+
FailureMode: inference.EndpointPickerFailOpen,
497+
},
498+
},
499+
},
419500
},
420501
},
421502
},
422-
{
423-
UpstreamName: "upstream3",
424-
EndpointPickerConfig: nil,
503+
expectedMaps: 2, // Only 2 unique upstreams
504+
expectedConfig: map[string]struct {
505+
failureMode inference.EndpointPickerFailureMode
506+
defaultResult string
507+
}{
508+
"upstream1": {
509+
failureMode: inference.EndpointPickerFailClose,
510+
defaultResult: "invalid-backend-ref",
511+
},
512+
"upstream2": {
513+
failureMode: inference.EndpointPickerFailOpen,
514+
defaultResult: "upstream2",
515+
},
425516
},
426517
},
518+
{
519+
name: "no endpoint picker configs",
520+
backendGroups: []dataplane.BackendGroup{
521+
{
522+
Backends: []dataplane.Backend{
523+
{
524+
UpstreamName: "upstream1",
525+
EndpointPickerConfig: nil,
526+
},
527+
{
528+
UpstreamName: "upstream2",
529+
EndpointPickerConfig: nil,
530+
},
531+
},
532+
},
533+
},
534+
expectedMaps: 0,
535+
},
427536
}
428537

429-
maps := buildInferenceMaps([]dataplane.BackendGroup{group})
430-
g.Expect(maps).To(HaveLen(2))
431-
g.Expect(maps[0].Source).To(Equal("$inference_workload_endpoint"))
432-
g.Expect(maps[0].Variable).To(Equal("$inference_backend_upstream1"))
433-
g.Expect(maps[0].Parameters).To(HaveLen(3))
434-
g.Expect(maps[0].Parameters[0].Value).To(Equal("\"\""))
435-
g.Expect(maps[0].Parameters[0].Result).To(Equal("upstream1"))
436-
g.Expect(maps[0].Parameters[1].Value).To(Equal("~.+"))
437-
g.Expect(maps[0].Parameters[1].Result).To(Equal("$inference_workload_endpoint"))
438-
g.Expect(maps[0].Parameters[2].Value).To(Equal("default"))
439-
g.Expect(maps[0].Parameters[2].Result).To(Equal("invalid-backend-ref"))
538+
for _, tc := range tests {
539+
t.Run(tc.name, func(t *testing.T) {
540+
t.Parallel()
541+
g := NewWithT(t)
542+
543+
maps := buildInferenceMaps(tc.backendGroups)
544+
g.Expect(maps).To(HaveLen(tc.expectedMaps))
440545

441-
// Check the second map
442-
g.Expect(maps[1].Source).To(Equal("$inference_workload_endpoint"))
443-
g.Expect(maps[1].Variable).To(Equal("$inference_backend_upstream2"))
444-
g.Expect(maps[1].Parameters).To(HaveLen(3))
445-
g.Expect(maps[1].Parameters[0].Value).To(Equal("\"\""))
446-
g.Expect(maps[1].Parameters[0].Result).To(Equal("upstream2"))
447-
g.Expect(maps[1].Parameters[1].Value).To(Equal("~.+"))
448-
g.Expect(maps[1].Parameters[1].Result).To(Equal("$inference_workload_endpoint"))
449-
g.Expect(maps[1].Parameters[2].Value).To(Equal("default"))
450-
g.Expect(maps[1].Parameters[2].Result).To(Equal("upstream2"))
546+
// Verify each map has the correct structure
547+
seenUpstreams := make(map[string]bool)
548+
for _, m := range maps {
549+
g.Expect(m.Source).To(Equal("$inference_workload_endpoint"))
550+
g.Expect(m.Parameters).To(HaveLen(3))
551+
552+
// Extract upstream name from variable name
553+
varName := strings.TrimPrefix(m.Variable, "$inference_backend_")
554+
upstreamName := strings.ReplaceAll(varName, "_", "-")
555+
556+
// Verify we haven't seen this upstream before (no duplicates)
557+
g.Expect(seenUpstreams[upstreamName]).To(BeFalse(), "Duplicate upstream found: %s", upstreamName)
558+
seenUpstreams[upstreamName] = true
559+
560+
// Verify parameter structure
561+
g.Expect(m.Parameters[0].Value).To(Equal("\"\""))
562+
g.Expect(m.Parameters[0].Result).To(Equal(upstreamName))
563+
g.Expect(m.Parameters[1].Value).To(Equal("~.+"))
564+
g.Expect(m.Parameters[1].Result).To(Equal("$inference_workload_endpoint"))
565+
g.Expect(m.Parameters[2].Value).To(Equal("default"))
566+
567+
// Verify the default result matches expected failure mode
568+
if expectedConfig, exists := tc.expectedConfig[upstreamName]; exists {
569+
g.Expect(m.Parameters[2].Result).To(Equal(expectedConfig.defaultResult))
570+
}
571+
}
572+
573+
// Verify all expected upstreams are present
574+
for expectedUpstream := range tc.expectedConfig {
575+
g.Expect(seenUpstreams[expectedUpstream]).To(BeTrue(), "Expected upstream not found: %s", expectedUpstream)
576+
}
577+
})
578+
}
451579
}

0 commit comments

Comments
 (0)