From a1d118b200033b8971ba6cf8f496d11594b92298 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Thu, 16 Jan 2025 16:41:17 -0800 Subject: [PATCH] api: adds cel validation on requiring at least one targetRefs (#114) Previously targetRefs can be empty. However, in reality, such AIGatewayRoute is useless while making it necessary for controller to handle the edge case. This sets the CEL validation rule to require at least one TargeRefs to exist. --------- Signed-off-by: Takeshi Yoneda --- api/v1alpha1/api.go | 4 ++-- ...gateway.envoyproxy.io_aigatewayroutes.yaml | 2 ++ tests/cel-validation/main_test.go | 4 ++++ .../testdata/aigatewayroutes/basic.yaml | 4 ++++ .../aigatewayroutes/no_target_refs.yaml | 20 +++++++++++++++++++ .../aigatewayroutes/non_openai_schema.yaml | 4 ++++ .../aigatewayroutes/unknown_schema.yaml | 4 ++++ .../aigatewayroutes/unsupported_match.yaml | 4 ++++ 8 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/cel-validation/testdata/aigatewayroutes/no_target_refs.yaml diff --git a/api/v1alpha1/api.go b/api/v1alpha1/api.go index a6ef2948..222fc91e 100644 --- a/api/v1alpha1/api.go +++ b/api/v1alpha1/api.go @@ -42,9 +42,9 @@ type AIGatewayRouteList struct { type AIGatewayRouteSpec struct { // TargetRefs are the names of the Gateway resources this AIGatewayRoute is being attached to. // - // +optional + // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=128 - TargetRefs []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName `json:"targetRefs,omitempty"` + TargetRefs []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName `json:"targetRefs"` // APISchema specifies the API schema of the input that the target Gateway(s) will receive. // Based on this schema, the ai-gateway will perform the necessary transformation to the // output schema specified in the selected AIServiceBackend during the routing process. diff --git a/manifests/charts/ai-gateway-helm/crds/aigateway.envoyproxy.io_aigatewayroutes.yaml b/manifests/charts/ai-gateway-helm/crds/aigateway.envoyproxy.io_aigatewayroutes.yaml index 2207e48e..50213fbf 100644 --- a/manifests/charts/ai-gateway-helm/crds/aigateway.envoyproxy.io_aigatewayroutes.yaml +++ b/manifests/charts/ai-gateway-helm/crds/aigateway.envoyproxy.io_aigatewayroutes.yaml @@ -355,10 +355,12 @@ spec: - name type: object maxItems: 128 + minItems: 1 type: array required: - inputSchema - rules + - targetRefs type: object type: object served: true diff --git a/tests/cel-validation/main_test.go b/tests/cel-validation/main_test.go index 0642e3b9..197768d9 100644 --- a/tests/cel-validation/main_test.go +++ b/tests/cel-validation/main_test.go @@ -41,6 +41,10 @@ func TestAIGatewayRoutes(t *testing.T) { name: "unsupported_match.yaml", expErr: "spec.rules[0].matches[0].headers: Invalid value: \"array\": currently only exact match is supported", }, + { + name: "no_target_refs.yaml", + expErr: `spec.targetRefs: Invalid value: 0: spec.targetRefs in body should have at least 1 items`, + }, } { t.Run(tc.name, func(t *testing.T) { data, err := testdata.ReadFile(path.Join("testdata/aigatewayroutes", tc.name)) diff --git a/tests/cel-validation/testdata/aigatewayroutes/basic.yaml b/tests/cel-validation/testdata/aigatewayroutes/basic.yaml index 01650577..22108031 100644 --- a/tests/cel-validation/testdata/aigatewayroutes/basic.yaml +++ b/tests/cel-validation/testdata/aigatewayroutes/basic.yaml @@ -4,6 +4,10 @@ metadata: name: apple namespace: default spec: + targetRefs: + - name: some-gateway + kind: Gateway + group: gateway.networking.k8s.io inputSchema: schema: OpenAI rules: diff --git a/tests/cel-validation/testdata/aigatewayroutes/no_target_refs.yaml b/tests/cel-validation/testdata/aigatewayroutes/no_target_refs.yaml new file mode 100644 index 00000000..ae7f356b --- /dev/null +++ b/tests/cel-validation/testdata/aigatewayroutes/no_target_refs.yaml @@ -0,0 +1,20 @@ +apiVersion: aigateway.envoyproxy.io/v1alpha1 +kind: AIGatewayRoute +metadata: + name: apple + namespace: default +spec: + targetRefs: [] + inputSchema: + schema: OpenAI + rules: + - matches: + - headers: + - type: Exact + name: x-envoy-ai-gateway-model + value: llama3-70b + backendRefs: + - name: kserve + weight: 20 + - name: aws-bedrock + weight: 80 diff --git a/tests/cel-validation/testdata/aigatewayroutes/non_openai_schema.yaml b/tests/cel-validation/testdata/aigatewayroutes/non_openai_schema.yaml index 9fa8a638..14713500 100644 --- a/tests/cel-validation/testdata/aigatewayroutes/non_openai_schema.yaml +++ b/tests/cel-validation/testdata/aigatewayroutes/non_openai_schema.yaml @@ -4,6 +4,10 @@ metadata: name: apple namespace: default spec: + targetRefs: + - name: some-gateway + kind: Gateway + group: gateway.networking.k8s.io inputSchema: # Input must be OpenAI schema at the moment, so this is invalid. schema: AWSBedrock diff --git a/tests/cel-validation/testdata/aigatewayroutes/unknown_schema.yaml b/tests/cel-validation/testdata/aigatewayroutes/unknown_schema.yaml index 71c12727..7219d8db 100644 --- a/tests/cel-validation/testdata/aigatewayroutes/unknown_schema.yaml +++ b/tests/cel-validation/testdata/aigatewayroutes/unknown_schema.yaml @@ -4,6 +4,10 @@ metadata: name: apple namespace: default spec: + targetRefs: + - name: some-gateway + kind: Gateway + group: gateway.networking.k8s.io inputSchema: # Schema must be OpenAI schema at the moment, so this is invalid. schema: SomeRandomVendor diff --git a/tests/cel-validation/testdata/aigatewayroutes/unsupported_match.yaml b/tests/cel-validation/testdata/aigatewayroutes/unsupported_match.yaml index e2245b54..90c3e245 100644 --- a/tests/cel-validation/testdata/aigatewayroutes/unsupported_match.yaml +++ b/tests/cel-validation/testdata/aigatewayroutes/unsupported_match.yaml @@ -4,6 +4,10 @@ metadata: name: apple namespace: default spec: + targetRefs: + - name: some-gateway + kind: Gateway + group: gateway.networking.k8s.io inputSchema: schema: OpenAI rules: