Repository: envoyproxy/ai-gateway
Pull request #515 (2025-03-21 21:49:20)

extproc: set extProcImage before potentially returning
**Commit Message**

The scenario where `filterConfig` is nil will result in skipping the code: `d.Template.Spec.Containers[0].Image = c.extProcImage`. The container image should be updated regardless of filterConfig, so moving the logic up.

**Related Issues/PRs (if applicable)**

https://github.com/envoyproxy/ai-gateway/pull/447

mathetake: can you add a regression test? 

Patch:
diff --git a/go.mod b/go.mod
index 987acc3..c9c07f3 100644
--- a/go.mod
+++ b/go.mod
@@ -192,7 +192,7 @@ require (
 	github.com/goccy/go-yaml v1.15.13 // indirect
 	github.com/gofrs/flock v0.12.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
+	github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
 	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
diff --git a/go.sum b/go.sum
index 0ef3715..e19a80e 100644
--- a/go.sum
+++ b/go.sum
@@ -530,8 +530,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
-github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
+github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
diff --git a/internal/controller/ai_gateway_route.go b/internal/controller/ai_gateway_route.go
index 13278c0..e85be34 100644
--- a/internal/controller/ai_gateway_route.go
+++ b/internal/controller/ai_gateway_route.go
@@ -165,6 +165,7 @@ func extProcName(route *aigv1a1.AIGatewayRoute) string {
 }
 
 func (c *AIGatewayRouteController) applyExtProcDeploymentConfigUpdate(d *appsv1.DeploymentSpec, filterConfig *aigv1a1.AIGatewayFilterConfig) {
+	d.Template.Spec.Containers[0].Image = c.extProcImage
 	if filterConfig == nil || filterConfig.ExternalProcessor == nil {
 		d.Replicas = nil
 		d.Template.Spec.Containers[0].Resources = corev1.ResourceRequirements{}
@@ -176,7 +177,6 @@ func (c *AIGatewayRouteController) applyExtProcDeploymentConfigUpdate(d *appsv1.
 	} else {
 		d.Template.Spec.Containers[0].Resources = corev1.ResourceRequirements{}
 	}
-	d.Template.Spec.Containers[0].Image = c.extProcImage
 	d.Replicas = extProc.Replicas
 }
 
diff --git a/internal/extproc/server.go b/internal/extproc/server.go
index 56bcc5d..9754690 100644
--- a/internal/extproc/server.go
+++ b/internal/extproc/server.go
@@ -78,7 +78,9 @@ func (s *Server) LoadConfig(ctx context.Context, config *filterapi.Config) error
 		for _, h := range r.Headers {
 			// If explicitly set to something that is not an exact match, skip.
 			// If not set, we assume it's an exact match.
-			if h.Type != nil && *h.Type != gwapiv1.HeaderMatchExact {
+			//
+			// Also, we only care about the AIModel header to declare models.
+			if (h.Type != nil && *h.Type != gwapiv1.HeaderMatchExact) || string(h.Name) != config.ModelNameHeaderKey {
 				continue
 			}
 			declaredModels = append(declaredModels, h.Value)


Test patch:
diff --git a/internal/controller/ai_gateway_route_test.go b/internal/controller/ai_gateway_route_test.go
index e4792d1..7038b82 100644
--- a/internal/controller/ai_gateway_route_test.go
+++ b/internal/controller/ai_gateway_route_test.go
@@ -140,7 +140,9 @@ func Test_applyExtProcDeploymentConfigUpdate(t *testing.T) {
 	dep := &appsv1.DeploymentSpec{
 		Template: corev1.PodTemplateSpec{
 			Spec: corev1.PodSpec{
-				Containers: []corev1.Container{{}},
+				Containers: []corev1.Container{{
+					Image: "placeholderExtProc",
+				}},
 			},
 		},
 	}
@@ -148,10 +150,13 @@ func Test_applyExtProcDeploymentConfigUpdate(t *testing.T) {
 	c := &AIGatewayRouteController{client: fake.NewClientBuilder().WithScheme(Scheme).Build(), extProcImage: extProcImage}
 	t.Run("not panic", func(_ *testing.T) {
 		c.applyExtProcDeploymentConfigUpdate(dep, nil)
+		require.Equal(t, dep.Template.Spec.Containers[0].Image, extProcImage)
 		c.applyExtProcDeploymentConfigUpdate(dep, &aigv1a1.AIGatewayFilterConfig{})
+		require.Equal(t, dep.Template.Spec.Containers[0].Image, extProcImage)
 		c.applyExtProcDeploymentConfigUpdate(dep, &aigv1a1.AIGatewayFilterConfig{
 			ExternalProcessor: &aigv1a1.AIGatewayFilterConfigExternalProcessor{},
 		})
+		require.Equal(t, dep.Template.Spec.Containers[0].Image, extProcImage)
 	})
 	t.Run("update", func(t *testing.T) {
 		req := corev1.ResourceRequirements{
diff --git a/internal/extproc/server_test.go b/internal/extproc/server_test.go
index ea5f407..fc5159a 100644
--- a/internal/extproc/server_test.go
+++ b/internal/extproc/server_test.go
@@ -69,6 +69,10 @@ func TestServer_LoadConfig(t *testing.T) {
 							Name:  "x-model-name",
 							Value: "gpt4.4444",
 						},
+						{
+							Name:  "some-random-header",
+							Value: "some-random-value",
+						},
 					},
 				},
 			},
@@ -94,6 +98,7 @@ func TestServer_LoadConfig(t *testing.T) {
 		val, err := llmcostcel.EvaluateProgram(prog, "", "", 1, 1, 1)
 		require.NoError(t, err)
 		require.Equal(t, uint64(2), val)
+		require.Equal(t, []string{"llama3.3333", "gpt4.4444"}, s.config.declaredModels)
 	})
 }
 
diff --git a/tests/extproc/testupstream_test.go b/tests/extproc/testupstream_test.go
index 3bdb5ff..bcfe14d 100644
--- a/tests/extproc/testupstream_test.go
+++ b/tests/extproc/testupstream_test.go
@@ -60,15 +60,23 @@ func TestWithTestUpstream(t *testing.T) {
 				Backends: []filterapi.Backend{{Name: "testupstream", Schema: azureOpenAISchema}},
 				Headers:  []filterapi.HeaderMatch{{Name: "x-test-backend", Value: "azure-openai"}},
 			},
+			{
+				Backends: []filterapi.Backend{{Name: "testupstream", Schema: azureOpenAISchema}},
+				Headers: []filterapi.HeaderMatch{
+					{Name: "x-model-name", Value: "some-model1"},
+					{Name: "x-model-name", Value: "some-model2"},
+					{Name: "x-model-name", Value: "some-model3"},
+				},
+			},
 		},
 	})
 
 	expectedModels := openai.ModelList{
 		Object: "list",
 		Data: []openai.Model{
-			{ID: "openai", Object: "model", OwnedBy: "Envoy AI Gateway"},
-			{ID: "aws-bedrock", Object: "model", OwnedBy: "Envoy AI Gateway"},
-			{ID: "azure-openai", Object: "model", OwnedBy: "Envoy AI Gateway"},
+			{ID: "some-model1", Object: "model", OwnedBy: "Envoy AI Gateway"},
+			{ID: "some-model2", Object: "model", OwnedBy: "Envoy AI Gateway"},
+			{ID: "some-model3", Object: "model", OwnedBy: "Envoy AI Gateway"},
 		},
 	}