diff --git a/.gitignore b/.gitignore index 60ad705f84..82137583f5 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ inference-extension-conformance-test-report.yaml site/.cursorrules /.cursor .worktrees/ +tools/tmp/ diff --git a/Makefile b/Makefile index ae82600d75..660c2498ab 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,12 @@ lint: ## This runs the linter on the codebase. @echo "actionlint => ./..." @$(GO_TOOL) actionlint -shellcheck="" # Disabling shellcheck as it requires additional host dependencies. +.PHONY: lint-api +lint-api: ## Run kube-api-linter on API directories to validate CRDs against Kubernetes API conventions. + @echo "kube-api-linter => ./api/..." + @test -f tools/tmp/bin/golangci-kube-api-linter || (cd tools && go tool -modfile go.mod golangci-lint custom) + @tools/tmp/bin/golangci-kube-api-linter run --config tools/.golangci-kal.yml ./api/... + .PHONY: spellcheck spellcheck: ## Spell check the codebase. @echo "misspell => ./..." diff --git a/api/v1alpha1/status.go b/api/v1alpha1/status.go index 4874d1b9b8..e05fe84659 100644 --- a/api/v1alpha1/status.go +++ b/api/v1alpha1/status.go @@ -16,47 +16,71 @@ const ( ConditionTypeNotAccepted = "NotAccepted" ) +// Condition contains details for one aspect of the current state of this API Resource. +type Condition struct { + // type of condition in CamelCase or in foo.example.com/CamelCase. + // + // +kubebuilder:validation:Enum=Accepted;NotAccepted + // +kubebuilder:validation:MaxLength=316 + Type string `json:"type"` + + // status of the condition, one of True, False, Unknown. + // + // +kubebuilder:validation:Enum=True;False;Unknown + Status metav1.ConditionStatus `json:"status"` + + // observedGeneration represents the .metadata.generation that the condition was set based upon. + // + // +kubebuilder:validation:Minimum=0 + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // lastTransitionTime is the last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime"` + + // reason contains a programmatic identifier indicating the reason for the condition's last transition. + // + // +kubebuilder:validation:MaxLength=1024 + // +kubebuilder:validation:MinLength=1 + Reason string `json:"reason"` + + // message is a human readable message indicating details about the transition. + // + // +kubebuilder:validation:MaxLength=32768 + Message string `json:"message"` +} + // AIGatewayRouteStatus contains the conditions by the reconciliation result. type AIGatewayRouteStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // AIServiceBackendStatus contains the conditions by the reconciliation result. type AIServiceBackendStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // BackendSecurityPolicyStatus contains the conditions by the reconciliation result. type BackendSecurityPolicyStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // MCPRouteStatus contains the conditions by the reconciliation result. type MCPRouteStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // QuotaPolicyStatus contains the conditions by the reconciliation result. type QuotaPolicyStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 3c44727050..29152bc3be 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -247,7 +247,7 @@ func (in *AIGatewayRouteStatus) DeepCopyInto(out *AIGatewayRouteStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -355,7 +355,7 @@ func (in *AIServiceBackendStatus) DeepCopyInto(out *AIServiceBackendStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -689,7 +689,7 @@ func (in *BackendSecurityPolicyStatus) DeepCopyInto(out *BackendSecurityPolicySt *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -706,6 +706,22 @@ func (in *BackendSecurityPolicyStatus) DeepCopy() *BackendSecurityPolicyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GCPCredentialsFile) DeepCopyInto(out *GCPCredentialsFile) { *out = *in @@ -1558,7 +1574,7 @@ func (in *MCPRouteStatus) DeepCopyInto(out *MCPRouteStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -1791,7 +1807,7 @@ func (in *QuotaPolicyStatus) DeepCopyInto(out *QuotaPolicyStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/api/v1beta1/status.go b/api/v1beta1/status.go index 09dafe7376..3b15743b3a 100644 --- a/api/v1beta1/status.go +++ b/api/v1beta1/status.go @@ -16,38 +16,64 @@ const ( ConditionTypeNotAccepted = "NotAccepted" ) +// Condition contains details for one aspect of the current state of this API Resource. +type Condition struct { + // type of condition in CamelCase or in foo.example.com/CamelCase. + // + // +kubebuilder:validation:Enum=Accepted;NotAccepted + // +kubebuilder:validation:MaxLength=316 + Type string `json:"type"` + + // status of the condition, one of True, False, Unknown. + // + // +kubebuilder:validation:Enum=True;False;Unknown + Status metav1.ConditionStatus `json:"status"` + + // observedGeneration represents the .metadata.generation that the condition was set based upon. + // + // +kubebuilder:validation:Minimum=0 + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // lastTransitionTime is the last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime"` + + // reason contains a programmatic identifier indicating the reason for the condition's last transition. + // + // +kubebuilder:validation:MaxLength=1024 + // +kubebuilder:validation:MinLength=1 + Reason string `json:"reason"` + + // message is a human readable message indicating details about the transition. + // + // +kubebuilder:validation:MaxLength=32768 + Message string `json:"message"` +} + // AIGatewayRouteStatus contains the conditions by the reconciliation result. type AIGatewayRouteStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // AIServiceBackendStatus contains the conditions by the reconciliation result. type AIServiceBackendStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // BackendSecurityPolicyStatus contains the conditions by the reconciliation result. type BackendSecurityPolicyStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } // MCPRouteStatus contains the conditions by the reconciliation result. type MCPRouteStatus struct { // Conditions is the list of conditions by the reconciliation result. // Currently, at most one condition is set. - // - // Known .status.conditions.type are: "Accepted", "NotAccepted". - Conditions []metav1.Condition `json:"conditions,omitempty"` + Conditions []Condition `json:"conditions"` } diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 10823978cf..0ee3acd3df 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -247,7 +247,7 @@ func (in *AIGatewayRouteStatus) DeepCopyInto(out *AIGatewayRouteStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -362,7 +362,7 @@ func (in *AIServiceBackendStatus) DeepCopyInto(out *AIServiceBackendStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -726,7 +726,7 @@ func (in *BackendSecurityPolicyStatus) DeepCopyInto(out *BackendSecurityPolicySt *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -743,6 +743,22 @@ func (in *BackendSecurityPolicyStatus) DeepCopy() *BackendSecurityPolicyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CredentialOverrideFromDynamicMetadata) DeepCopyInto(out *CredentialOverrideFromDynamicMetadata) { *out = *in @@ -1715,7 +1731,7 @@ func (in *MCPRouteStatus) DeepCopyInto(out *MCPRouteStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 70ba755ed4..23f2d3e43c 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -548,8 +548,8 @@ func referenceGrantToTargetKindIndexFunc(o client.Object) []string { // // Currently, we only set one condition at a time either "Accepted" or "NotAccepted". // In the future, if we can have multiple conditions like multiple errors, we can make changes here. -func newConditions(conditionType, message string) []metav1.Condition { - condition := metav1.Condition{Message: message, LastTransitionTime: metav1.Now()} +func newConditions(conditionType, message string) []aigv1b1.Condition { + condition := aigv1b1.Condition{Message: message, LastTransitionTime: metav1.Now()} // Note: we use the fixed reason for now since the message is enough to describe the error and // reason doesn't fit the entire message. switch conditionType { @@ -562,7 +562,23 @@ func newConditions(conditionType, message string) []metav1.Condition { condition.Status = metav1.ConditionFalse condition.Reason = "ReconciliationFailed" } - return []metav1.Condition{condition} + return []aigv1b1.Condition{condition} +} + +// newConditionsV1Alpha1 creates new conditions for v1alpha1 resources. +func newConditionsV1Alpha1(conditionType, message string) []aigv1a1.Condition { + condition := aigv1a1.Condition{Message: message, LastTransitionTime: metav1.Now()} + switch conditionType { + case aigv1a1.ConditionTypeAccepted: + condition.Type = aigv1a1.ConditionTypeAccepted + condition.Status = metav1.ConditionTrue + condition.Reason = "ReconciliationSucceeded" + case aigv1a1.ConditionTypeNotAccepted: + condition.Type = aigv1a1.ConditionTypeNotAccepted + condition.Status = metav1.ConditionFalse + condition.Reason = "ReconciliationFailed" + } + return []aigv1a1.Condition{condition} } // aiGatewayControllerFinalizer is the name of the finalizer added to various AI Gateway resources. diff --git a/internal/controller/quota_policy.go b/internal/controller/quota_policy.go index 9be168e842..012ea4e52c 100644 --- a/internal/controller/quota_policy.go +++ b/internal/controller/quota_policy.go @@ -257,7 +257,7 @@ func (c *QuotaPolicyController) updateQuotaPolicyStatus(ctx context.Context, pol } return err } - policy.Status.Conditions = newConditions(conditionType, message) + policy.Status.Conditions = newConditionsV1Alpha1(conditionType, message) return c.client.Status().Update(ctx, policy) }) if err != nil { diff --git a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aigatewayroutes.yaml b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aigatewayroutes.yaml index b31f78108c..44e9b8252b 100644 --- a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aigatewayroutes.yaml +++ b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aigatewayroutes.yaml @@ -888,42 +888,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -934,8 +923,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -945,6 +936,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true @@ -1817,42 +1810,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -1863,8 +1845,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -1874,6 +1858,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true diff --git a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aiservicebackends.yaml b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aiservicebackends.yaml index 64a3fa17c1..e7a4ebc2ab 100644 --- a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aiservicebackends.yaml +++ b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_aiservicebackends.yaml @@ -376,42 +376,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -422,8 +411,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -433,6 +424,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true @@ -851,42 +844,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -897,8 +879,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -908,6 +892,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true diff --git a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_backendsecuritypolicies.yaml b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_backendsecuritypolicies.yaml index e1dc2d6f41..a2ef4e253a 100644 --- a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_backendsecuritypolicies.yaml +++ b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_backendsecuritypolicies.yaml @@ -6397,42 +6397,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -6443,8 +6432,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -6454,6 +6445,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true @@ -12916,42 +12909,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -12962,8 +12944,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -12973,6 +12957,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true diff --git a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_mcproutes.yaml b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_mcproutes.yaml index 9129059b5b..42642da1e3 100644 --- a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_mcproutes.yaml +++ b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_mcproutes.yaml @@ -6128,42 +6128,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -6174,8 +6163,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -6185,6 +6176,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true @@ -12386,42 +12379,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -12432,8 +12414,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -12443,6 +12427,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true diff --git a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_quotapolicies.yaml b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_quotapolicies.yaml index 6250bd161e..c6ab81d847 100644 --- a/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_quotapolicies.yaml +++ b/manifests/charts/ai-gateway-crds-helm/templates/aigateway.envoyproxy.io_quotapolicies.yaml @@ -453,42 +453,31 @@ spec: description: |- Conditions is the list of conditions by the reconciliation result. Currently, at most one condition is set. - - Known .status.conditions.type are: "Accepted", "NotAccepted". items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. format: date-time type: string message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. + description: message is a human readable message indicating + details about the transition. maxLength: 32768 type: string observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. format: int64 minimum: 0 type: integer reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. maxLength: 1024 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. @@ -499,8 +488,10 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. + enum: + - Accepted + - NotAccepted maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime @@ -510,6 +501,8 @@ spec: - type type: object type: array + required: + - conditions type: object type: object served: true diff --git a/site/docs/api/api.mdx b/site/docs/api/api.mdx index b559cebd54..798ed6cb2a 100644 --- a/site/docs/api/api.mdx +++ b/site/docs/api/api.mdx @@ -579,6 +579,7 @@ QuotaPolicyList contains a list of QuotaPolicy - [BackendSecurityPolicySpec](#github-com-envoyproxy-ai-gateway-api-v1alpha1-backendsecuritypolicyspec) - [BackendSecurityPolicyStatus](#github-com-envoyproxy-ai-gateway-api-v1alpha1-backendsecuritypolicystatus) - [BackendSecurityPolicyType](#github-com-envoyproxy-ai-gateway-api-v1alpha1-backendsecuritypolicytype) +- [Condition](#github-com-envoyproxy-ai-gateway-api-v1alpha1-condition) - [GCPCredentialsFile](#github-com-envoyproxy-ai-gateway-api-v1alpha1-gcpcredentialsfile) - [GCPOIDCExchangeToken](#github-com-envoyproxy-ai-gateway-api-v1alpha1-gcpoidcexchangetoken) - [GCPServiceAccountImpersonationConfig](#github-com-envoyproxy-ai-gateway-api-v1alpha1-gcpserviceaccountimpersonationconfig) @@ -812,9 +813,9 @@ AIGatewayRouteStatus contains the conditions by the reconciliation result. @@ -869,9 +870,9 @@ AIServiceBackendStatus contains the conditions by the reconciliation result. @@ -1326,9 +1327,9 @@ BackendSecurityPolicyStatus contains the conditions by the reconciliation result @@ -1376,6 +1377,56 @@ BackendSecurityPolicyType specifies the type of auth mechanism used to access a required="false" description="" /> +#### Condition + + + +**Appears in:** +- [AIGatewayRouteStatus](#github-com-envoyproxy-ai-gateway-api-v1alpha1-aigatewayroutestatus) +- [AIServiceBackendStatus](#github-com-envoyproxy-ai-gateway-api-v1alpha1-aiservicebackendstatus) +- [BackendSecurityPolicyStatus](#github-com-envoyproxy-ai-gateway-api-v1alpha1-backendsecuritypolicystatus) +- [MCPRouteStatus](#github-com-envoyproxy-ai-gateway-api-v1alpha1-mcproutestatus) +- [QuotaPolicyStatus](#github-com-envoyproxy-ai-gateway-api-v1alpha1-quotapolicystatus) + +Condition contains details for one aspect of the current state of this API Resource. + +##### Fields + + + + + + #### GCPCredentialsFile @@ -2286,9 +2337,9 @@ MCPRouteStatus contains the conditions by the reconciliation result. @@ -2509,9 +2560,9 @@ QuotaPolicyStatus contains the conditions by the reconciliation result. @@ -3150,6 +3201,7 @@ MCPRouteList contains a list of MCPRoute. - [BackendSecurityPolicySpec](#github-com-envoyproxy-ai-gateway-api-v1beta1-backendsecuritypolicyspec) - [BackendSecurityPolicyStatus](#github-com-envoyproxy-ai-gateway-api-v1beta1-backendsecuritypolicystatus) - [BackendSecurityPolicyType](#github-com-envoyproxy-ai-gateway-api-v1beta1-backendsecuritypolicytype) +- [Condition](#github-com-envoyproxy-ai-gateway-api-v1beta1-condition) - [CredentialOverrideFromDynamicMetadata](#github-com-envoyproxy-ai-gateway-api-v1beta1-credentialoverridefromdynamicmetadata) - [CredentialOverrideFromRequestHeaders](#github-com-envoyproxy-ai-gateway-api-v1beta1-credentialoverridefromrequestheaders) - [GCPCredentialsFile](#github-com-envoyproxy-ai-gateway-api-v1beta1-gcpcredentialsfile) @@ -3382,9 +3434,9 @@ AIGatewayRouteStatus contains the conditions by the reconciliation result. @@ -3444,9 +3496,9 @@ AIServiceBackendStatus contains the conditions by the reconciliation result. @@ -3939,9 +3991,9 @@ BackendSecurityPolicyStatus contains the conditions by the reconciliation result @@ -3989,6 +4041,55 @@ BackendSecurityPolicyType specifies the type of auth mechanism used to access a required="false" description="" /> +#### Condition + + + +**Appears in:** +- [AIGatewayRouteStatus](#github-com-envoyproxy-ai-gateway-api-v1beta1-aigatewayroutestatus) +- [AIServiceBackendStatus](#github-com-envoyproxy-ai-gateway-api-v1beta1-aiservicebackendstatus) +- [BackendSecurityPolicyStatus](#github-com-envoyproxy-ai-gateway-api-v1beta1-backendsecuritypolicystatus) +- [MCPRouteStatus](#github-com-envoyproxy-ai-gateway-api-v1beta1-mcproutestatus) + +Condition contains details for one aspect of the current state of this API Resource. + +##### Fields + + + + + + #### CredentialOverrideFromDynamicMetadata @@ -5144,9 +5245,9 @@ MCPRouteStatus contains the conditions by the reconciliation result. diff --git a/tools/.custom-gcl.yaml b/tools/.custom-gcl.yaml new file mode 100644 index 0000000000..2a25837b21 --- /dev/null +++ b/tools/.custom-gcl.yaml @@ -0,0 +1,16 @@ +# Copyright Envoy AI Gateway Authors +# SPDX-License-Identifier: Apache-2.0 +# The full text of the Apache license is available in the LICENSE file at +# the root of the repo. + +# Custom golangci-lint build configuration for kube-api-linter. +# +# This builds a custom golangci-lint binary that includes the kube-api-linter +# plugin. The custom binary is used to lint CRDs for Kubernetes API convention +# compliance without affecting the main golangci-lint version used elsewhere. +version: v2.13.2 +name: golangci-kube-api-linter +destination: ./tmp/bin/ +plugins: + - module: "sigs.k8s.io/kube-api-linter" + version: v0.0.0-20260716143926-092fe0c72997 diff --git a/tools/.golangci-kal.yml b/tools/.golangci-kal.yml new file mode 100644 index 0000000000..9a10c8bbbb --- /dev/null +++ b/tools/.golangci-kal.yml @@ -0,0 +1,71 @@ +# Copyright Envoy AI Gateway Authors +# SPDX-License-Identifier: Apache-2.0 +# The full text of the Apache license is available in the LICENSE file at +# the root of the repo. + +# This file holds the Kube-API-Linter (KAL) configuration. +# KAL ensures that our Custom Resource Definitions meet the Kubernetes API +# conventions (as much as possible). +# +# The linters documentation is at +# https://github.com/kubernetes-sigs/kube-api-linter/blob/main/docs/linters.md +version: "2" +run: + go: "1.27" + allow-parallel-runners: true +linters: + default: none + enable: + - kubeapilinter + settings: + custom: + kubeapilinter: + type: module + description: KAL is the Kube-API-Linter and lints Custom Resource Definitions (CRDs) based on API conventions and best practices. + settings: + linters: + # We configure the list of enabled linters explicitly. + # Whenever a new linter is added, it should not break the backward + # compatibility of existing APIs (at least for v1 APIs). + enable: + - "conflictingmarkers" + - "duplicatemarkers" + - "nofloats" + - "uniquemarkers" + - "nophase" + - "nonullable" + - "forbiddenmarkers" + - "nomaps" + disable: + - "*" + lintersConfig: + conflictingmarkers: + conflicts: + - name: "default_vs_required" + sets: + - ["default", "kubebuilder:default"] + - [ + "required", + "kubebuilder:validation:Required", + "k8s:required", + ] + description: "A field with a default value cannot be required" + forbiddenmarkers: + markers: + - identifier: "+kubebuilder:pruning:PreserveUnknownFields" + - identifier: "+kubebuilder:validation:XPreserveUnknownFields" + - identifier: "+kubebuilder:validation:items:XPreserveUnknownFields" + - identifier: "+kubebuilder:validation:EmbeddedResource" + - identifier: "+kubebuilder:validation:XEmbeddedResource" + - identifier: "+kubebuilder:validation:items:XEmbeddedResource" + exclusions: + generated: strict + paths: + # Exclude test files and generated code. + - _test\.go + - zz_generated.*\.go$ + rules: + # KAL only validates the API folders for CRD client generation. + - path-except: "api/.*" + linters: + - kubeapilinter diff --git a/tools/go.mod b/tools/go.mod index 44bdf56b76..df4323aee6 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -31,6 +31,7 @@ tool ( sigs.k8s.io/controller-runtime/tools/setup-envtest sigs.k8s.io/controller-tools/cmd/controller-gen sigs.k8s.io/kind + sigs.k8s.io/kube-api-linter/cmd/golangci-lint-kube-api-linter ) require github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 @@ -395,6 +396,7 @@ require ( sigs.k8s.io/controller-tools v0.20.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kind v0.33.0 // indirect + sigs.k8s.io/kube-api-linter v0.0.0-20260716143926-092fe0c72997 // indirect sigs.k8s.io/kustomize/api v0.20.1 // indirect sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 9c5768046c..ae4e54e393 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -1044,6 +1044,8 @@ sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5E sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kind v0.33.0 h1:AjvDv3vOygb/VKLVQW87lfktIBzkxR8Ump9DjxC8+Lk= sigs.k8s.io/kind v0.33.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= +sigs.k8s.io/kube-api-linter v0.0.0-20260716143926-092fe0c72997 h1:u5/qbXZq7YIrurEE6HiJKtjQ9ILcFPJJSHNY0Q1ZRhU= +sigs.k8s.io/kube-api-linter v0.0.0-20260716143926-092fe0c72997/go.mod h1:5mP60UakkCye+eOcZ5p98VnV2O49qreW1gq9TdsUf7Q= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78=