api: tighten runtime CRD validation#382
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds/updates Kubebuilder CRD validations for runtime APIs (durations, ports, warm pool sizing, and cross-field constraints) and introduces a Go test to assert the generated CRD schemas contain the expected OpenAPI/CEL validation rules.
Changes:
- Add
Format=durationand positive-duration CEL validations forsessionTimeout/maxSessionDuration. - Add min/max/regex validations for ports, path prefix, and warm pool size; add cross-field
sessionTimeout <= maxSessionDurationvalidations. - Add tests that load the generated CRDs and assert the schema contains the expected validation constraints.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/apis/runtime/v1alpha1/crd_validation_test.go | New tests to verify generated CRD OpenAPI schema and CEL rules. |
| pkg/apis/runtime/v1alpha1/codeinterpreter_types.go | Adds Kubebuilder validation markers for CodeInterpreter spec fields and port fields. |
| pkg/apis/runtime/v1alpha1/agent_type.go | Adds Kubebuilder validation markers for AgentRuntime spec duration fields and cross-field constraint. |
| manifests/charts/base/crds/runtime.agentcube.volcano.sh_codeinterpreters.yaml | Regenerated CRD including the new schema constraints and CEL rules. |
| manifests/charts/base/crds/runtime.agentcube.volcano.sh_agentruntimes.yaml | Regenerated CRD including the new schema constraints and CEL rules. |
| name: "CodeInterpreter", | ||
| crdFile: "runtime.agentcube.volcano.sh_codeinterpreters.yaml", | ||
| ports: "ports", | ||
| rule: "!has(self.sessionTimeout) || !has(self.maxSessionDuration) || self.sessionTimeout <= self.maxSessionDuration", | ||
| }, |
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| specSchema := loadCRDSpecSchema(t, tt.crdFile) |
| path := filepath.Join("..", "..", "..", "..", "manifests", "charts", "base", "crds", crdFile) | ||
| data, err := os.ReadFile(path) |
There was a problem hiding this comment.
Code Review
This pull request adds OpenAPI validation rules and CEL validations to the AgentRuntime and CodeInterpreter CRDs, ensuring correct port ranges, path prefixes, and session duration limits, along with a test suite to verify the generated schemas. However, the CEL validation rules contain critical bugs: comparing metav1.Duration fields (which are serialized as strings) directly using operators like <= or > results in lexicographical string comparisons or runtime type mismatch errors. To fix this, all duration fields must be explicitly wrapped in the duration() function (e.g., duration(self)) in the Go API definitions, the generated YAML manifests, and the corresponding unit tests.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| } | ||
|
|
||
| // AgentRuntimeSpec describes how to create and manage agent runtime sandboxes. | ||
| // +kubebuilder:validation:XValidation:rule="self.sessionTimeout <= self.maxSessionDuration",message="sessionTimeout must be less than or equal to maxSessionDuration" |
There was a problem hiding this comment.
In CEL, comparing string fields (which metav1.Duration is represented as in OpenAPI/JSON) directly using <= performs a lexicographical string comparison instead of a duration comparison. For example, "10h" <= "8h" would evaluate to true because "1" is lexicographically less than "8". To compare them correctly as durations, they must be parsed using the duration() function.
| // +kubebuilder:validation:XValidation:rule="self.sessionTimeout <= self.maxSessionDuration",message="sessionTimeout must be less than or equal to maxSessionDuration" | |
| // +kubebuilder:validation:XValidation:rule="duration(self.sessionTimeout) <= duration(self.maxSessionDuration)",message="sessionTimeout must be less than or equal to maxSessionDuration" |
| // SessionTimeout describes the duration after which an inactive session will be terminated. | ||
| // +kubebuilder:validation:Required | ||
| // +kubebuilder:validation:Format=duration | ||
| // +kubebuilder:validation:XValidation:rule="self > duration('0s')",message="sessionTimeout must be greater than 0" |
There was a problem hiding this comment.
Since self is a string representing the duration, comparing it directly with a duration object (duration('0s')) will cause a CEL type mismatch runtime error, which will reject all API requests. We must parse self using duration(self).
| // +kubebuilder:validation:XValidation:rule="self > duration('0s')",message="sessionTimeout must be greater than 0" | |
| // +kubebuilder:validation:XValidation:rule="duration(self) > duration('0s')",message="sessionTimeout must be greater than 0" |
| // After this duration, the session will be terminated no matter active or inactive. | ||
| // +kubebuilder:validation:Required | ||
| // +kubebuilder:validation:Format=duration | ||
| // +kubebuilder:validation:XValidation:rule="self > duration('0s')",message="maxSessionDuration must be greater than 0" |
There was a problem hiding this comment.
Since self is a string representing the duration, comparing it directly with a duration object (duration('0s')) will cause a CEL type mismatch runtime error, which will reject all API requests. We must parse self using duration(self).
| // +kubebuilder:validation:XValidation:rule="self > duration('0s')",message="maxSessionDuration must be greater than 0" | |
| // +kubebuilder:validation:XValidation:rule="duration(self) > duration('0s')",message="maxSessionDuration must be greater than 0" |
| } | ||
|
|
||
| // CodeInterpreterSpec describes how to create and manage code-interpreter sandboxes. | ||
| // +kubebuilder:validation:XValidation:rule="!has(self.sessionTimeout) || !has(self.maxSessionDuration) || self.sessionTimeout <= self.maxSessionDuration",message="sessionTimeout must be less than or equal to maxSessionDuration" |
There was a problem hiding this comment.
In CEL, comparing string fields (which metav1.Duration is represented as in OpenAPI/JSON) directly using <= performs a lexicographical string comparison instead of a duration comparison. To compare them correctly as durations, they must be parsed using the duration() function.
| // +kubebuilder:validation:XValidation:rule="!has(self.sessionTimeout) || !has(self.maxSessionDuration) || self.sessionTimeout <= self.maxSessionDuration",message="sessionTimeout must be less than or equal to maxSessionDuration" | |
| // +kubebuilder:validation:XValidation:rule="!has(self.sessionTimeout) || !has(self.maxSessionDuration) || duration(self.sessionTimeout) <= duration(self.maxSessionDuration)",message="sessionTimeout must be less than or equal to maxSessionDuration" |
| // session will be terminated. Any sandbox that has not received requests within | ||
| // this duration is eligible for cleanup. | ||
| // +kubebuilder:validation:Format=duration | ||
| // +kubebuilder:validation:XValidation:rule="self > duration('0s')",message="sessionTimeout must be greater than 0" |
There was a problem hiding this comment.
Since self is a string representing the duration, comparing it directly with a duration object (duration('0s')) will cause a CEL type mismatch runtime error, which will reject all API requests. We must parse self using duration(self).
| // +kubebuilder:validation:XValidation:rule="self > duration('0s')",message="sessionTimeout must be greater than 0" | |
| // +kubebuilder:validation:XValidation:rule="duration(self) > duration('0s')",message="sessionTimeout must be greater than 0" |
| x-kubernetes-validations: | ||
| - message: sessionTimeout must be less than or equal to maxSessionDuration | ||
| rule: '!has(self.sessionTimeout) || !has(self.maxSessionDuration) || | ||
| self.sessionTimeout <= self.maxSessionDuration' |
There was a problem hiding this comment.
| name: "AgentRuntime", | ||
| crdFile: "runtime.agentcube.volcano.sh_agentruntimes.yaml", | ||
| ports: "targetPort", | ||
| rule: "self.sessionTimeout <= self.maxSessionDuration", |
| name: "CodeInterpreter", | ||
| crdFile: "runtime.agentcube.volcano.sh_codeinterpreters.yaml", | ||
| ports: "ports", | ||
| rule: "!has(self.sessionTimeout) || !has(self.maxSessionDuration) || self.sessionTimeout <= self.maxSessionDuration", |
There was a problem hiding this comment.
Update the expected validation rule to match the correct duration-based comparison.
| rule: "!has(self.sessionTimeout) || !has(self.maxSessionDuration) || self.sessionTimeout <= self.maxSessionDuration", | |
| rule: "!has(self.sessionTimeout) || !has(self.maxSessionDuration) || duration(self.sessionTimeout) <= duration(self.maxSessionDuration)", |
|
|
||
| sessionTimeoutSchema := nestedMap(t, specSchema, "properties", "sessionTimeout") | ||
| assertString(t, sessionTimeoutSchema, "format", "duration") | ||
| assertHasValidationRule(t, sessionTimeoutSchema, "self > duration('0s')") |
|
|
||
| maxSessionDurationSchema := nestedMap(t, specSchema, "properties", "maxSessionDuration") | ||
| assertString(t, maxSessionDurationSchema, "format", "duration") | ||
| assertHasValidationRule(t, maxSessionDurationSchema, "self > duration('0s')") |
There was a problem hiding this comment.
ee6a37f to
f5ec701
Compare
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #382 +/- ##
===========================================
+ Coverage 47.57% 57.96% +10.39%
===========================================
Files 30 34 +4
Lines 2819 3181 +362
===========================================
+ Hits 1341 1844 +503
+ Misses 1338 1153 -185
- Partials 140 184 +44
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
f5ec701 to
c608717
Compare
| return nestedMap(t, crd, | ||
| "spec", "versions", "0", "schema", "openAPIV3Schema", "properties", "spec") |
| } | ||
|
|
||
| // AgentRuntimeSpec describes how to create and manage agent runtime sandboxes. | ||
| // +kubebuilder:validation:XValidation:rule="duration(self.sessionTimeout) <= duration(self.maxSessionDuration)",message="sessionTimeout must be less than or equal to maxSessionDuration" |
Signed-off-by: CYJiang <googs1025@gmail.com>
c608717 to
311d738
Compare
Summary
Test Plan