diff --git a/apisx/v1alpha1/backendtrafficpolicy_types.go b/apisx/v1alpha1/backendtrafficpolicy_types.go new file mode 100644 index 0000000000..802decd4bd --- /dev/null +++ b/apisx/v1alpha1/backendtrafficpolicy_types.go @@ -0,0 +1,159 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:resource:categories=gateway-api,shortName=btrafficpolicy +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +// +// BackendTrafficPolicy is a Direct Attached Policy. +// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=Direct" + +// BackendTrafficPolicy defines the configuration for how traffic to a +// target backend should be handled. +type BackendTrafficPolicy struct { + // Support: Extended + // + // +optional + + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of BackendTrafficPolicy. + Spec BackendTrafficPolicySpec `json:"spec"` + + // Status defines the current state of BackendTrafficPolicy. + Status PolicyStatus `json:"status,omitempty"` +} + +// BackendTrafficPolicyList contains a list of BackendTrafficPolicies +// +kubebuilder:object:root=true +type BackendTrafficPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BackendTrafficPolicy `json:"items"` +} + +// BackendTrafficPolicySpec define the desired state of BackendTrafficPolicy +// Note: there is no Override or Default policy configuration. +type BackendTrafficPolicySpec struct { + // TargetRefs identifies API object(s) to apply this policy to. + // Currently, Backends (A grouping of like endpoints such as Service, + // ServiceImport, or any implementation-specific backendRef) are the only + // valid API target references. + // + // Currently, a TargetRef can not be scoped to a specific port on a + // Service. + // + // +listType=map + // +listMapKey=group + // +listMapKey=kind + // +listMapKey=name + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=16 + TargetRefs []LocalPolicyTargetReference `json:"targetRefs"` + + // RetryConstraint defines the configuration for when to allow or prevent + // further retries to a target backend, by dynamically calculating a 'retry + // budget'. This budget is calculated based on the percentage of incoming + // traffic composed of retries over a given time interval. Once the budget + // is exceeded, additional retries will be rejected. + // + // For example, if the retry budget interval is 10 seconds, there have been + // 1000 active requests in the past 10 seconds, and the allowed percentage + // of requests that can be retried is 20% (the default), then 200 of those + // requests may be composed of retries. Active requests will only be + // considered for the duration of the interval when calculating the retry + // budget. Retrying the same original request multiple times within the + // retry budget interval will lead to each retry being counted towards + // calculating the budget. + // + // Configuring a RetryConstraint in BackendTrafficPolicy is compatible with + // HTTPRoute Retry settings for each HTTPRouteRule that targets the same + // backend. While the HTTPRouteRule Retry stanza can specify whether a + // request will be retried, and the number of retry attempts each client + // may perform, RetryConstraint helps prevent cascading failures such as + // retry storms during periods of consistent failures. + // + // After the retry budget has been exceeded, additional retries to the + // backend MUST return a 503 response to the client. + // + // Additional configurations for defining a constraint on retries MAY be + // defined in the future. + // + // Support: Extended + // + // +optional + // + RetryConstraint *RetryConstraint `json:"retryConstraint,omitempty"` + + // SessionPersistence defines and configures session persistence + // for the backend. + // + // Support: Extended + // + // +optional + SessionPersistence *SessionPersistence `json:"sessionPersistence,omitempty"` +} + +// RetryConstraint defines the configuration for when to retry a request. +type RetryConstraint struct { + // BudgetPercent defines the maximum percentage of active requests that may + // be made up of retries. + // + // Support: Extended + // + // +optional + // +kubebuilder:default=20 + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=100 + BudgetPercent *int `json:"budgetPercent,omitempty"` + + // BudgetInterval defines the duration in which requests will be considered + // for calculating the budget for retries. + // + // Support: Extended + // + // +optional + // +kubebuilder:default="10s" + // +kubebuilder:validation:XValidation:message="budgetInterval can not be greater than one hour or less than one second",rule="!(duration(self) < duration('1s') || duration(self) > duration('1h'))" + BudgetInterval *Duration `json:"budgetInterval,omitempty"` + + // MinRetryRate defines the minimum rate of retries that will be allowable + // over a specified duration of time. + // + // The effective overall minimum rate of retries targeting the backend + // service may be much higher, as there can be any number of clients which + // are applying this setting locally. + // + // This ensures that requests can still be retried during periods of low + // traffic, where the budget for retries may be calculated as a very low + // value. + // + // Support: Extended + // + // +optional + // +kubebuilder:default={count: 10, interval: "1s"} + MinRetryRate *RequestRate `json:"minRetryRate,omitempty"` +} diff --git a/apisx/v1alpha1/shared_types.go b/apisx/v1alpha1/shared_types.go index 190eeaecb7..f811ace877 100644 --- a/apisx/v1alpha1/shared_types.go +++ b/apisx/v1alpha1/shared_types.go @@ -16,7 +16,10 @@ limitations under the License. package v1alpha1 -import v1 "sigs.k8s.io/gateway-api/apis/v1" +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" + v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) type ( // +k8s:deepcopy-gen=false @@ -41,6 +44,14 @@ type ( SectionName = v1.SectionName // +k8s:deepcopy-gen=false Namespace = v1.Namespace + // +k8s:deepcopy-gen=false + Duration = v1.Duration + // +k8s:deepcopy-gen=false + PolicyStatus = v1alpha2.PolicyStatus + // +k8s:deepcopy-gen=false + LocalPolicyTargetReference = v1alpha2.LocalPolicyTargetReference + // +k8s:deepcopy-gen=false + SessionPersistence = v1.SessionPersistence ) // ParentGatewayReference identifies an API object including its namespace, @@ -68,3 +79,20 @@ type ParentGatewayReference struct { // +optional Namespace *Namespace `json:"namespace,omitempty"` } + +// RequestRate expresses a rate of requests over a given period of time. +type RequestRate struct { + // Count specifies the number of requests per time interval. + // + // Support: Extended + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=1000000 + Count *int `json:"count,omitempty"` + + // Interval specifies the divisor of the rate of requests, the amount of + // time during which the given count of requests occur. + // + // Support: Extended + // +kubebuilder:validation:XValidation:message="interval can not be greater than one hour",rule="!(duration(self) == duration('0s') || duration(self) > duration('1h'))" + Interval *Duration `json:"interval,omitempty"` +} diff --git a/apisx/v1alpha1/zz_generated.deepcopy.go b/apisx/v1alpha1/zz_generated.deepcopy.go index c3a547570d..985d4b3a2f 100644 --- a/apisx/v1alpha1/zz_generated.deepcopy.go +++ b/apisx/v1alpha1/zz_generated.deepcopy.go @@ -24,8 +24,98 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/apis/v1alpha2" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendTrafficPolicy) DeepCopyInto(out *BackendTrafficPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicy. +func (in *BackendTrafficPolicy) DeepCopy() *BackendTrafficPolicy { + if in == nil { + return nil + } + out := new(BackendTrafficPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackendTrafficPolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendTrafficPolicyList) DeepCopyInto(out *BackendTrafficPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackendTrafficPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicyList. +func (in *BackendTrafficPolicyList) DeepCopy() *BackendTrafficPolicyList { + if in == nil { + return nil + } + out := new(BackendTrafficPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackendTrafficPolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec) { + *out = *in + if in.TargetRefs != nil { + in, out := &in.TargetRefs, &out.TargetRefs + *out = make([]v1alpha2.LocalPolicyTargetReference, len(*in)) + copy(*out, *in) + } + if in.RetryConstraint != nil { + in, out := &in.RetryConstraint, &out.RetryConstraint + *out = new(RetryConstraint) + (*in).DeepCopyInto(*out) + } + if in.SessionPersistence != nil { + in, out := &in.SessionPersistence, &out.SessionPersistence + *out = new(v1.SessionPersistence) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicySpec. +func (in *BackendTrafficPolicySpec) DeepCopy() *BackendTrafficPolicySpec { + if in == nil { + return nil + } + out := new(BackendTrafficPolicySpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ListenerEntry) DeepCopyInto(out *ListenerEntry) { *out = *in @@ -225,3 +315,58 @@ func (in *ParentGatewayReference) DeepCopy() *ParentGatewayReference { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestRate) DeepCopyInto(out *RequestRate) { + *out = *in + if in.Count != nil { + in, out := &in.Count, &out.Count + *out = new(int) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestRate. +func (in *RequestRate) DeepCopy() *RequestRate { + if in == nil { + return nil + } + out := new(RequestRate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RetryConstraint) DeepCopyInto(out *RetryConstraint) { + *out = *in + if in.BudgetPercent != nil { + in, out := &in.BudgetPercent, &out.BudgetPercent + *out = new(int) + **out = **in + } + if in.BudgetInterval != nil { + in, out := &in.BudgetInterval, &out.BudgetInterval + *out = new(v1.Duration) + **out = **in + } + if in.MinRetryRate != nil { + in, out := &in.MinRetryRate, &out.MinRetryRate + *out = new(RequestRate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryConstraint. +func (in *RetryConstraint) DeepCopy() *RetryConstraint { + if in == nil { + return nil + } + out := new(RetryConstraint) + in.DeepCopyInto(out) + return out +} diff --git a/apisx/v1alpha1/zz_generated.register.go b/apisx/v1alpha1/zz_generated.register.go index 9f76164bd0..ecbd109235 100644 --- a/apisx/v1alpha1/zz_generated.register.go +++ b/apisx/v1alpha1/zz_generated.register.go @@ -61,6 +61,8 @@ func init() { // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, + &BackendTrafficPolicy{}, + &BackendTrafficPolicyList{}, &ListenerSet{}, &ListenerSetList{}, ) diff --git a/applyconfiguration/apisx/v1alpha1/backendtrafficpolicy.go b/applyconfiguration/apisx/v1alpha1/backendtrafficpolicy.go new file mode 100644 index 0000000000..49288efca8 --- /dev/null +++ b/applyconfiguration/apisx/v1alpha1/backendtrafficpolicy.go @@ -0,0 +1,265 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" + apisxv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" + v1alpha2 "sigs.k8s.io/gateway-api/applyconfiguration/apis/v1alpha2" + internal "sigs.k8s.io/gateway-api/applyconfiguration/internal" +) + +// BackendTrafficPolicyApplyConfiguration represents a declarative configuration of the BackendTrafficPolicy type for use +// with apply. +type BackendTrafficPolicyApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *BackendTrafficPolicySpecApplyConfiguration `json:"spec,omitempty"` + Status *v1alpha2.PolicyStatusApplyConfiguration `json:"status,omitempty"` +} + +// BackendTrafficPolicy constructs a declarative configuration of the BackendTrafficPolicy type for use with +// apply. +func BackendTrafficPolicy(name, namespace string) *BackendTrafficPolicyApplyConfiguration { + b := &BackendTrafficPolicyApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("BackendTrafficPolicy") + b.WithAPIVersion("gateway.networking.x-k8s.io/v1alpha1") + return b +} + +// ExtractBackendTrafficPolicy extracts the applied configuration owned by fieldManager from +// backendTrafficPolicy. If no managedFields are found in backendTrafficPolicy for fieldManager, a +// BackendTrafficPolicyApplyConfiguration is returned with only the Name, Namespace (if applicable), +// APIVersion and Kind populated. It is possible that no managed fields were found for because other +// field managers have taken ownership of all the fields previously owned by fieldManager, or because +// the fieldManager never owned fields any fields. +// backendTrafficPolicy must be a unmodified BackendTrafficPolicy API object that was retrieved from the Kubernetes API. +// ExtractBackendTrafficPolicy provides a way to perform a extract/modify-in-place/apply workflow. +// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously +// applied if another fieldManager has updated or force applied any of the previously applied fields. +// Experimental! +func ExtractBackendTrafficPolicy(backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicy, fieldManager string) (*BackendTrafficPolicyApplyConfiguration, error) { + return extractBackendTrafficPolicy(backendTrafficPolicy, fieldManager, "") +} + +// ExtractBackendTrafficPolicyStatus is the same as ExtractBackendTrafficPolicy except +// that it extracts the status subresource applied configuration. +// Experimental! +func ExtractBackendTrafficPolicyStatus(backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicy, fieldManager string) (*BackendTrafficPolicyApplyConfiguration, error) { + return extractBackendTrafficPolicy(backendTrafficPolicy, fieldManager, "status") +} + +func extractBackendTrafficPolicy(backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicy, fieldManager string, subresource string) (*BackendTrafficPolicyApplyConfiguration, error) { + b := &BackendTrafficPolicyApplyConfiguration{} + err := managedfields.ExtractInto(backendTrafficPolicy, internal.Parser().Type("io.k8s.sigs.gateway-api.apisx.v1alpha1.BackendTrafficPolicy"), fieldManager, b, subresource) + if err != nil { + return nil, err + } + b.WithName(backendTrafficPolicy.Name) + b.WithNamespace(backendTrafficPolicy.Namespace) + + b.WithKind("BackendTrafficPolicy") + b.WithAPIVersion("gateway.networking.x-k8s.io/v1alpha1") + return b, nil +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithKind(value string) *BackendTrafficPolicyApplyConfiguration { + b.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithAPIVersion(value string) *BackendTrafficPolicyApplyConfiguration { + b.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithName(value string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithGenerateName(value string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithNamespace(value string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithUID(value types.UID) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithResourceVersion(value string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithGeneration(value int64) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithCreationTimestamp(value metav1.Time) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *BackendTrafficPolicyApplyConfiguration) WithLabels(entries map[string]string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.Labels == nil && len(entries) > 0 { + b.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *BackendTrafficPolicyApplyConfiguration) WithAnnotations(entries map[string]string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.Annotations == nil && len(entries) > 0 { + b.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *BackendTrafficPolicyApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.OwnerReferences = append(b.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *BackendTrafficPolicyApplyConfiguration) WithFinalizers(values ...string) *BackendTrafficPolicyApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.Finalizers = append(b.Finalizers, values[i]) + } + return b +} + +func (b *BackendTrafficPolicyApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithSpec(value *BackendTrafficPolicySpecApplyConfiguration) *BackendTrafficPolicyApplyConfiguration { + b.Spec = value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *BackendTrafficPolicyApplyConfiguration) WithStatus(value *v1alpha2.PolicyStatusApplyConfiguration) *BackendTrafficPolicyApplyConfiguration { + b.Status = value + return b +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *BackendTrafficPolicyApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.Name +} diff --git a/applyconfiguration/apisx/v1alpha1/backendtrafficpolicyspec.go b/applyconfiguration/apisx/v1alpha1/backendtrafficpolicyspec.go new file mode 100644 index 0000000000..205609b882 --- /dev/null +++ b/applyconfiguration/apisx/v1alpha1/backendtrafficpolicyspec.go @@ -0,0 +1,67 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "sigs.k8s.io/gateway-api/applyconfiguration/apis/v1" + v1alpha2 "sigs.k8s.io/gateway-api/applyconfiguration/apis/v1alpha2" +) + +// BackendTrafficPolicySpecApplyConfiguration represents a declarative configuration of the BackendTrafficPolicySpec type for use +// with apply. +type BackendTrafficPolicySpecApplyConfiguration struct { + TargetRefs []v1alpha2.LocalPolicyTargetReferenceApplyConfiguration `json:"targetRefs,omitempty"` + RetryConstraint *RetryConstraintApplyConfiguration `json:"retryConstraint,omitempty"` + SessionPersistence *v1.SessionPersistenceApplyConfiguration `json:"sessionPersistence,omitempty"` +} + +// BackendTrafficPolicySpecApplyConfiguration constructs a declarative configuration of the BackendTrafficPolicySpec type for use with +// apply. +func BackendTrafficPolicySpec() *BackendTrafficPolicySpecApplyConfiguration { + return &BackendTrafficPolicySpecApplyConfiguration{} +} + +// WithTargetRefs adds the given value to the TargetRefs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the TargetRefs field. +func (b *BackendTrafficPolicySpecApplyConfiguration) WithTargetRefs(values ...*v1alpha2.LocalPolicyTargetReferenceApplyConfiguration) *BackendTrafficPolicySpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithTargetRefs") + } + b.TargetRefs = append(b.TargetRefs, *values[i]) + } + return b +} + +// WithRetryConstraint sets the RetryConstraint field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RetryConstraint field is set to the value of the last call. +func (b *BackendTrafficPolicySpecApplyConfiguration) WithRetryConstraint(value *RetryConstraintApplyConfiguration) *BackendTrafficPolicySpecApplyConfiguration { + b.RetryConstraint = value + return b +} + +// WithSessionPersistence sets the SessionPersistence field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SessionPersistence field is set to the value of the last call. +func (b *BackendTrafficPolicySpecApplyConfiguration) WithSessionPersistence(value *v1.SessionPersistenceApplyConfiguration) *BackendTrafficPolicySpecApplyConfiguration { + b.SessionPersistence = value + return b +} diff --git a/applyconfiguration/apisx/v1alpha1/requestrate.go b/applyconfiguration/apisx/v1alpha1/requestrate.go new file mode 100644 index 0000000000..a5c874a43c --- /dev/null +++ b/applyconfiguration/apisx/v1alpha1/requestrate.go @@ -0,0 +1,52 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// RequestRateApplyConfiguration represents a declarative configuration of the RequestRate type for use +// with apply. +type RequestRateApplyConfiguration struct { + Count *int `json:"count,omitempty"` + Interval *v1.Duration `json:"interval,omitempty"` +} + +// RequestRateApplyConfiguration constructs a declarative configuration of the RequestRate type for use with +// apply. +func RequestRate() *RequestRateApplyConfiguration { + return &RequestRateApplyConfiguration{} +} + +// WithCount sets the Count field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Count field is set to the value of the last call. +func (b *RequestRateApplyConfiguration) WithCount(value int) *RequestRateApplyConfiguration { + b.Count = &value + return b +} + +// WithInterval sets the Interval field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Interval field is set to the value of the last call. +func (b *RequestRateApplyConfiguration) WithInterval(value v1.Duration) *RequestRateApplyConfiguration { + b.Interval = &value + return b +} diff --git a/applyconfiguration/apisx/v1alpha1/retryconstraint.go b/applyconfiguration/apisx/v1alpha1/retryconstraint.go new file mode 100644 index 0000000000..4248ea83fc --- /dev/null +++ b/applyconfiguration/apisx/v1alpha1/retryconstraint.go @@ -0,0 +1,61 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// RetryConstraintApplyConfiguration represents a declarative configuration of the RetryConstraint type for use +// with apply. +type RetryConstraintApplyConfiguration struct { + BudgetPercent *int `json:"budgetPercent,omitempty"` + BudgetInterval *v1.Duration `json:"budgetInterval,omitempty"` + MinRetryRate *RequestRateApplyConfiguration `json:"minRetryRate,omitempty"` +} + +// RetryConstraintApplyConfiguration constructs a declarative configuration of the RetryConstraint type for use with +// apply. +func RetryConstraint() *RetryConstraintApplyConfiguration { + return &RetryConstraintApplyConfiguration{} +} + +// WithBudgetPercent sets the BudgetPercent field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the BudgetPercent field is set to the value of the last call. +func (b *RetryConstraintApplyConfiguration) WithBudgetPercent(value int) *RetryConstraintApplyConfiguration { + b.BudgetPercent = &value + return b +} + +// WithBudgetInterval sets the BudgetInterval field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the BudgetInterval field is set to the value of the last call. +func (b *RetryConstraintApplyConfiguration) WithBudgetInterval(value v1.Duration) *RetryConstraintApplyConfiguration { + b.BudgetInterval = &value + return b +} + +// WithMinRetryRate sets the MinRetryRate field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MinRetryRate field is set to the value of the last call. +func (b *RetryConstraintApplyConfiguration) WithMinRetryRate(value *RequestRateApplyConfiguration) *RetryConstraintApplyConfiguration { + b.MinRetryRate = value + return b +} diff --git a/applyconfiguration/internal/internal.go b/applyconfiguration/internal/internal.go index e1856e547e..c47f0b1064 100644 --- a/applyconfiguration/internal/internal.go +++ b/applyconfiguration/internal/internal.go @@ -1711,6 +1711,46 @@ var schemaYAML = typed.YAMLObject(`types: - name: name type: scalar: string +- name: io.k8s.sigs.gateway-api.apisx.v1alpha1.BackendTrafficPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + default: {} + - name: spec + type: + namedType: io.k8s.sigs.gateway-api.apisx.v1alpha1.BackendTrafficPolicySpec + default: {} + - name: status + type: + namedType: io.k8s.sigs.gateway-api.apis.v1alpha2.PolicyStatus + default: {} +- name: io.k8s.sigs.gateway-api.apisx.v1alpha1.BackendTrafficPolicySpec + map: + fields: + - name: retryConstraint + type: + namedType: io.k8s.sigs.gateway-api.apisx.v1alpha1.RetryConstraint + - name: sessionPersistence + type: + namedType: io.k8s.sigs.gateway-api.apis.v1.SessionPersistence + - name: targetRefs + type: + list: + elementType: + namedType: io.k8s.sigs.gateway-api.apis.v1alpha2.LocalPolicyTargetReference + elementRelationship: associative + keys: + - group + - kind + - name - name: io.k8s.sigs.gateway-api.apisx.v1alpha1.ListenerEntry map: fields: @@ -1834,6 +1874,27 @@ var schemaYAML = typed.YAMLObject(`types: - name: namespace type: scalar: string +- name: io.k8s.sigs.gateway-api.apisx.v1alpha1.RequestRate + map: + fields: + - name: count + type: + scalar: numeric + - name: interval + type: + scalar: string +- name: io.k8s.sigs.gateway-api.apisx.v1alpha1.RetryConstraint + map: + fields: + - name: budgetInterval + type: + scalar: string + - name: budgetPercent + type: + scalar: numeric + - name: minRetryRate + type: + namedType: io.k8s.sigs.gateway-api.apisx.v1alpha1.RequestRate - name: __untyped_atomic_ scalar: untyped list: diff --git a/applyconfiguration/utils.go b/applyconfiguration/utils.go index fa12ea3138..05210f8807 100644 --- a/applyconfiguration/utils.go +++ b/applyconfiguration/utils.go @@ -232,6 +232,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apisv1beta1.ReferenceGrantToApplyConfiguration{} // Group=gateway.networking.x-k8s.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithKind("BackendTrafficPolicy"): + return &apisxv1alpha1.BackendTrafficPolicyApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("BackendTrafficPolicySpec"): + return &apisxv1alpha1.BackendTrafficPolicySpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ListenerEntry"): return &apisxv1alpha1.ListenerEntryApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ListenerEntryStatus"): @@ -244,6 +248,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apisxv1alpha1.ListenerSetStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ParentGatewayReference"): return &apisxv1alpha1.ParentGatewayReferenceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("RequestRate"): + return &apisxv1alpha1.RequestRateApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("RetryConstraint"): + return &apisxv1alpha1.RetryConstraintApplyConfiguration{} } return nil diff --git a/config/crd/experimental/gateway.networking.k8s.io_backendtrafficpolicies.yaml b/config/crd/experimental/gateway.networking.k8s.io_backendtrafficpolicies.yaml new file mode 100644 index 0000000000..8e9b185c46 --- /dev/null +++ b/config/crd/experimental/gateway.networking.k8s.io_backendtrafficpolicies.yaml @@ -0,0 +1,535 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: backendtrafficpolicies.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + kind: BackendTrafficPolicy + listKind: BackendTrafficPolicyList + plural: backendtrafficpolicies + singular: backendtrafficpolicy + scope: Namespaced + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendTrafficPolicy. + properties: + retry: + description: |- + Retry defines the configuration for when to retry a request to a target + backend. + + Implementations SHOULD retry on connection errors (disconnect, reset, timeout, + TCP failure) if a retry stanza is configured. + + Support: Extended + properties: + budgetInterval: + description: |- + BudgetInterval defines the duration in which requests will be considered + for calculating the budget for retries. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + budgetPercent: + description: |- + BudgetPercent defines the maximum percentage of active requests that may + be made up of retries. + + Support: Extended + type: integer + minRetryRate: + description: |- + MinRetryRate defines the minimum rate of retries that will be allowable + over a specified duration of time. + + Ensures that requests can still be retried during periods of low + traffic. + + Support: Extended + properties: + count: + description: |- + Count specifies the number of requests per time interval. + + Support: Extended + type: integer + interval: + description: |- + Interval specifies the divisor of the rate of requests, the amount of + time during which the given count of requests occr. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + type: object + type: object + sessionPersistence: + description: |- + SessionPersistence defines and configures session persistence + for the backend. + + Support: Extended + properties: + absoluteTimeout: + description: |- + AbsoluteTimeout defines the absolute timeout of the persistent + session. Once the AbsoluteTimeout duration has elapsed, the + session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + cookieConfig: + description: |- + CookieConfig provides configuration settings that are specific + to cookie-based session persistence. + + Support: Core + properties: + lifetimeType: + default: Session + description: |- + LifetimeType specifies whether the cookie has a permanent or + session-based lifetime. A permanent cookie persists until its + specified expiry time, defined by the Expires or Max-Age cookie + attributes, while a session cookie is deleted when the current + session ends. + + When set to "Permanent", AbsoluteTimeout indicates the + cookie's lifetime via the Expires or Max-Age cookie attributes + and is required. + + When set to "Session", AbsoluteTimeout indicates the + absolute lifetime of the cookie tracked by the gateway and + is optional. + + Defaults to "Session". + + Support: Core for "Session" type + + Support: Extended for "Permanent" type + enum: + - Permanent + - Session + type: string + type: object + idleTimeout: + description: |- + IdleTimeout defines the idle timeout of the persistent session. + Once the session has been idle for more than the specified + IdleTimeout duration, the session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + sessionName: + description: |- + SessionName defines the name of the persistent session token + which may be reflected in the cookie or the header. Users + should avoid reusing session names to prevent unintended + consequences, such as rejection or unpredictable behavior. + + Support: Implementation-specific + maxLength: 128 + type: string + type: + default: Cookie + description: |- + Type defines the type of session persistence such as through + the use a header or cookie. Defaults to cookie based session + persistence. + + Support: Core for "Cookie" type + + Support: Extended for "Header" type + enum: + - Cookie + - Header + type: string + type: object + x-kubernetes-validations: + - message: AbsoluteTimeout must be specified when cookie lifetimeType + is Permanent + rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) + || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' + targetRefs: + description: |- + TargetRef identifies an API object to apply policy to. + Currently, Backends (i.e. Service, ServiceImport, or any + implementation-specific backendRef) are the only valid API + target references. + items: + description: |- + LocalPolicyTargetReference identifies an API object to apply a direct or + inherited policy to. This should be used as part of Policy resources + that can target Gateway API resources. For more information on how this + policy attachment model works, and a sample Policy resource, refer to + the policy attachment documentation for Gateway API. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - group + - kind + - name + x-kubernetes-list-type: map + required: + - targetRefs + type: object + status: + description: Status defines the current state of BackendTrafficPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + 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. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + 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. + 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. + 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. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + 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 + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/config/crd/experimental/gateway.networking.x-k8s.io_backendtrafficpolicies.yaml b/config/crd/experimental/gateway.networking.x-k8s.io_backendtrafficpolicies.yaml new file mode 100644 index 0000000000..f1bc424579 --- /dev/null +++ b/config/crd/experimental/gateway.networking.x-k8s.io_backendtrafficpolicies.yaml @@ -0,0 +1,598 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + gateway.networking.k8s.io/policy: Direct + name: backendtrafficpolicies.gateway.networking.x-k8s.io +spec: + group: gateway.networking.x-k8s.io + names: + categories: + - gateway-api + kind: BackendTrafficPolicy + listKind: BackendTrafficPolicyList + plural: backendtrafficpolicies + shortNames: + - btrafficpolicy + singular: backendtrafficpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + BackendTrafficPolicy defines the configuration for how traffic to a + target backend should be handled. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendTrafficPolicy. + properties: + retryConstraint: + description: |- + RetryConstraint defines the configuration for when to allow or prevent + further retries to a target backend, by dynamically calculating a 'retry + budget'. This budget is calculated based on the percentage of incoming + traffic composed of retries over a given time interval. Once the budget + is exceeded, additional retries will be rejected. + + For example, if the retry budget interval is 10 seconds, there have been + 1000 active requests in the past 10 seconds, and the allowed percentage + of requests that can be retried is 20% (the default), then 200 of those + requests may be composed of retries. Active requests will only be + considered for the duration of the interval when calculating the retry + budget. Retrying the same original request multiple times within the + retry budget interval will lead to each retry being counted towards + calculating the budget. + + Configuring a RetryConstraint in BackendTrafficPolicy is compatible with + HTTPRoute Retry settings for each HTTPRouteRule that targets the same + backend. While the HTTPRouteRule Retry stanza can specify whether a + request will be retried, and the number of retry attempts each client + may perform, RetryConstraint helps prevent cascading failures such as + retry storms during periods of consistent failures. + + After the retry budget has been exceeded, additional retries to the + backend MUST return a 503 response to the client. + + Additional configurations for defining a constraint on retries MAY be + defined in the future. + + Support: Extended + properties: + budgetInterval: + default: 10s + description: |- + BudgetInterval defines the duration in which requests will be considered + for calculating the budget for retries. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + x-kubernetes-validations: + - message: budgetInterval can not be greater than one hour or + less than one second + rule: '!(duration(self) < duration(''1s'') || duration(self) + > duration(''1h''))' + budgetPercent: + default: 20 + description: |- + BudgetPercent defines the maximum percentage of active requests that may + be made up of retries. + + Support: Extended + maximum: 100 + minimum: 0 + type: integer + minRetryRate: + default: + count: 10 + interval: 1s + description: |- + MinRetryRate defines the minimum rate of retries that will be allowable + over a specified duration of time. + + The effective overall minimum rate of retries targeting the backend + service may be much higher, as there can be any number of clients which + are applying this setting locally. + + This ensures that requests can still be retried during periods of low + traffic, where the budget for retries may be calculated as a very low + value. + + Support: Extended + properties: + count: + description: |- + Count specifies the number of requests per time interval. + + Support: Extended + maximum: 1000000 + minimum: 1 + type: integer + interval: + description: |- + Interval specifies the divisor of the rate of requests, the amount of + time during which the given count of requests occur. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + x-kubernetes-validations: + - message: interval can not be greater than one hour + rule: '!(duration(self) == duration(''0s'') || duration(self) + > duration(''1h''))' + type: object + type: object + sessionPersistence: + description: |- + SessionPersistence defines and configures session persistence + for the backend. + + Support: Extended + properties: + absoluteTimeout: + description: |- + AbsoluteTimeout defines the absolute timeout of the persistent + session. Once the AbsoluteTimeout duration has elapsed, the + session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + cookieConfig: + description: |- + CookieConfig provides configuration settings that are specific + to cookie-based session persistence. + + Support: Core + properties: + lifetimeType: + default: Session + description: |- + LifetimeType specifies whether the cookie has a permanent or + session-based lifetime. A permanent cookie persists until its + specified expiry time, defined by the Expires or Max-Age cookie + attributes, while a session cookie is deleted when the current + session ends. + + When set to "Permanent", AbsoluteTimeout indicates the + cookie's lifetime via the Expires or Max-Age cookie attributes + and is required. + + When set to "Session", AbsoluteTimeout indicates the + absolute lifetime of the cookie tracked by the gateway and + is optional. + + Defaults to "Session". + + Support: Core for "Session" type + + Support: Extended for "Permanent" type + enum: + - Permanent + - Session + type: string + type: object + idleTimeout: + description: |- + IdleTimeout defines the idle timeout of the persistent session. + Once the session has been idle for more than the specified + IdleTimeout duration, the session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + sessionName: + description: |- + SessionName defines the name of the persistent session token + which may be reflected in the cookie or the header. Users + should avoid reusing session names to prevent unintended + consequences, such as rejection or unpredictable behavior. + + Support: Implementation-specific + maxLength: 128 + type: string + type: + default: Cookie + description: |- + Type defines the type of session persistence such as through + the use a header or cookie. Defaults to cookie based session + persistence. + + Support: Core for "Cookie" type + + Support: Extended for "Header" type + enum: + - Cookie + - Header + type: string + type: object + x-kubernetes-validations: + - message: AbsoluteTimeout must be specified when cookie lifetimeType + is Permanent + rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) + || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' + targetRefs: + description: |- + TargetRefs identifies API object(s) to apply this policy to. + Currently, Backends (A grouping of like endpoints such as Service, + ServiceImport, or any implementation-specific backendRef) are the only + valid API target references. + + Currently, a TargetRef can not be scoped to a specific port on a + Service. + items: + description: |- + LocalPolicyTargetReference identifies an API object to apply a direct or + inherited policy to. This should be used as part of Policy resources + that can target Gateway API resources. For more information on how this + policy attachment model works, and a sample Policy resource, refer to + the policy attachment documentation for Gateway API. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - group + - kind + - name + x-kubernetes-list-type: map + required: + - targetRefs + type: object + status: + description: Status defines the current state of BackendTrafficPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + 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. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + 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. + 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. + 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. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + 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 + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/geps/gep-3388/index.md b/geps/gep-3388/index.md index 8f63436739..23d4763912 100644 --- a/geps/gep-3388/index.md +++ b/geps/gep-3388/index.md @@ -1,7 +1,7 @@ # GEP-3388: Retry Budgets * Issue: [#3388](https://github.com/kubernetes-sigs/gateway-api/issues/3388) -* Status: Implementable +* Status: Experimental (See status definitions [here](/geps/overview/#gep-states).) diff --git a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/apisx_client.go b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/apisx_client.go index 056800e4ac..9cbdd89238 100644 --- a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/apisx_client.go +++ b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/apisx_client.go @@ -28,6 +28,7 @@ import ( type ExperimentalV1alpha1Interface interface { RESTClient() rest.Interface + BackendTrafficPoliciesGetter ListenerSetsGetter } @@ -36,6 +37,10 @@ type ExperimentalV1alpha1Client struct { restClient rest.Interface } +func (c *ExperimentalV1alpha1Client) BackendTrafficPolicies(namespace string) BackendTrafficPolicyInterface { + return newBackendTrafficPolicies(c, namespace) +} + func (c *ExperimentalV1alpha1Client) ListenerSets(namespace string) ListenerSetInterface { return newListenerSets(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/backendtrafficpolicy.go b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/backendtrafficpolicy.go new file mode 100644 index 0000000000..74455d9064 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/backendtrafficpolicy.go @@ -0,0 +1,73 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" + v1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" + apisxv1alpha1 "sigs.k8s.io/gateway-api/applyconfiguration/apisx/v1alpha1" + scheme "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/scheme" +) + +// BackendTrafficPoliciesGetter has a method to return a BackendTrafficPolicyInterface. +// A group's client should implement this interface. +type BackendTrafficPoliciesGetter interface { + BackendTrafficPolicies(namespace string) BackendTrafficPolicyInterface +} + +// BackendTrafficPolicyInterface has methods to work with BackendTrafficPolicy resources. +type BackendTrafficPolicyInterface interface { + Create(ctx context.Context, backendTrafficPolicy *v1alpha1.BackendTrafficPolicy, opts v1.CreateOptions) (*v1alpha1.BackendTrafficPolicy, error) + Update(ctx context.Context, backendTrafficPolicy *v1alpha1.BackendTrafficPolicy, opts v1.UpdateOptions) (*v1alpha1.BackendTrafficPolicy, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, backendTrafficPolicy *v1alpha1.BackendTrafficPolicy, opts v1.UpdateOptions) (*v1alpha1.BackendTrafficPolicy, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.BackendTrafficPolicy, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.BackendTrafficPolicyList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.BackendTrafficPolicy, err error) + Apply(ctx context.Context, backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.BackendTrafficPolicy, err error) + // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). + ApplyStatus(ctx context.Context, backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.BackendTrafficPolicy, err error) + BackendTrafficPolicyExpansion +} + +// backendTrafficPolicies implements BackendTrafficPolicyInterface +type backendTrafficPolicies struct { + *gentype.ClientWithListAndApply[*v1alpha1.BackendTrafficPolicy, *v1alpha1.BackendTrafficPolicyList, *apisxv1alpha1.BackendTrafficPolicyApplyConfiguration] +} + +// newBackendTrafficPolicies returns a BackendTrafficPolicies +func newBackendTrafficPolicies(c *ExperimentalV1alpha1Client, namespace string) *backendTrafficPolicies { + return &backendTrafficPolicies{ + gentype.NewClientWithListAndApply[*v1alpha1.BackendTrafficPolicy, *v1alpha1.BackendTrafficPolicyList, *apisxv1alpha1.BackendTrafficPolicyApplyConfiguration]( + "backendtrafficpolicies", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1alpha1.BackendTrafficPolicy { return &v1alpha1.BackendTrafficPolicy{} }, + func() *v1alpha1.BackendTrafficPolicyList { return &v1alpha1.BackendTrafficPolicyList{} }), + } +} diff --git a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_apisx_client.go b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_apisx_client.go index 28f74e4041..12f35d1b13 100644 --- a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_apisx_client.go +++ b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_apisx_client.go @@ -28,6 +28,10 @@ type FakeExperimentalV1alpha1 struct { *testing.Fake } +func (c *FakeExperimentalV1alpha1) BackendTrafficPolicies(namespace string) v1alpha1.BackendTrafficPolicyInterface { + return &FakeBackendTrafficPolicies{c, namespace} +} + func (c *FakeExperimentalV1alpha1) ListenerSets(namespace string) v1alpha1.ListenerSetInterface { return &FakeListenerSets{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_backendtrafficpolicy.go b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_backendtrafficpolicy.go new file mode 100644 index 0000000000..905e295b85 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/fake/fake_backendtrafficpolicy.go @@ -0,0 +1,197 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + json "encoding/json" + "fmt" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" + apisxv1alpha1 "sigs.k8s.io/gateway-api/applyconfiguration/apisx/v1alpha1" +) + +// FakeBackendTrafficPolicies implements BackendTrafficPolicyInterface +type FakeBackendTrafficPolicies struct { + Fake *FakeExperimentalV1alpha1 + ns string +} + +var backendtrafficpoliciesResource = v1alpha1.SchemeGroupVersion.WithResource("backendtrafficpolicies") + +var backendtrafficpoliciesKind = v1alpha1.SchemeGroupVersion.WithKind("BackendTrafficPolicy") + +// Get takes name of the backendTrafficPolicy, and returns the corresponding backendTrafficPolicy object, and an error if there is any. +func (c *FakeBackendTrafficPolicies) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.BackendTrafficPolicy, err error) { + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(backendtrafficpoliciesResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} + +// List takes label and field selectors, and returns the list of BackendTrafficPolicies that match those selectors. +func (c *FakeBackendTrafficPolicies) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.BackendTrafficPolicyList, err error) { + emptyResult := &v1alpha1.BackendTrafficPolicyList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(backendtrafficpoliciesResource, backendtrafficpoliciesKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.BackendTrafficPolicyList{ListMeta: obj.(*v1alpha1.BackendTrafficPolicyList).ListMeta} + for _, item := range obj.(*v1alpha1.BackendTrafficPolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested backendTrafficPolicies. +func (c *FakeBackendTrafficPolicies) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(backendtrafficpoliciesResource, c.ns, opts)) + +} + +// Create takes the representation of a backendTrafficPolicy and creates it. Returns the server's representation of the backendTrafficPolicy, and an error, if there is any. +func (c *FakeBackendTrafficPolicies) Create(ctx context.Context, backendTrafficPolicy *v1alpha1.BackendTrafficPolicy, opts v1.CreateOptions) (result *v1alpha1.BackendTrafficPolicy, err error) { + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(backendtrafficpoliciesResource, c.ns, backendTrafficPolicy, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} + +// Update takes the representation of a backendTrafficPolicy and updates it. Returns the server's representation of the backendTrafficPolicy, and an error, if there is any. +func (c *FakeBackendTrafficPolicies) Update(ctx context.Context, backendTrafficPolicy *v1alpha1.BackendTrafficPolicy, opts v1.UpdateOptions) (result *v1alpha1.BackendTrafficPolicy, err error) { + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(backendtrafficpoliciesResource, c.ns, backendTrafficPolicy, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeBackendTrafficPolicies) UpdateStatus(ctx context.Context, backendTrafficPolicy *v1alpha1.BackendTrafficPolicy, opts v1.UpdateOptions) (result *v1alpha1.BackendTrafficPolicy, err error) { + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceActionWithOptions(backendtrafficpoliciesResource, "status", c.ns, backendTrafficPolicy, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} + +// Delete takes name of the backendTrafficPolicy and deletes it. Returns an error if one occurs. +func (c *FakeBackendTrafficPolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(backendtrafficpoliciesResource, c.ns, name, opts), &v1alpha1.BackendTrafficPolicy{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeBackendTrafficPolicies) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(backendtrafficpoliciesResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.BackendTrafficPolicyList{}) + return err +} + +// Patch applies the patch and returns the patched backendTrafficPolicy. +func (c *FakeBackendTrafficPolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.BackendTrafficPolicy, err error) { + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(backendtrafficpoliciesResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} + +// Apply takes the given apply declarative configuration, applies it and returns the applied backendTrafficPolicy. +func (c *FakeBackendTrafficPolicies) Apply(ctx context.Context, backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.BackendTrafficPolicy, err error) { + if backendTrafficPolicy == nil { + return nil, fmt.Errorf("backendTrafficPolicy provided to Apply must not be nil") + } + data, err := json.Marshal(backendTrafficPolicy) + if err != nil { + return nil, err + } + name := backendTrafficPolicy.Name + if name == nil { + return nil, fmt.Errorf("backendTrafficPolicy.Name must be provided to Apply") + } + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(backendtrafficpoliciesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} + +// ApplyStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). +func (c *FakeBackendTrafficPolicies) ApplyStatus(ctx context.Context, backendTrafficPolicy *apisxv1alpha1.BackendTrafficPolicyApplyConfiguration, opts v1.ApplyOptions) (result *v1alpha1.BackendTrafficPolicy, err error) { + if backendTrafficPolicy == nil { + return nil, fmt.Errorf("backendTrafficPolicy provided to Apply must not be nil") + } + data, err := json.Marshal(backendTrafficPolicy) + if err != nil { + return nil, err + } + name := backendTrafficPolicy.Name + if name == nil { + return nil, fmt.Errorf("backendTrafficPolicy.Name must be provided to Apply") + } + emptyResult := &v1alpha1.BackendTrafficPolicy{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(backendtrafficpoliciesResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions(), "status"), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1alpha1.BackendTrafficPolicy), err +} diff --git a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/generated_expansion.go index f42f0ed53e..ad8c5e1498 100644 --- a/pkg/client/clientset/versioned/typed/apisx/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/apisx/v1alpha1/generated_expansion.go @@ -18,4 +18,6 @@ limitations under the License. package v1alpha1 +type BackendTrafficPolicyExpansion interface{} + type ListenerSetExpansion interface{} diff --git a/pkg/client/informers/externalversions/apisx/v1alpha1/backendtrafficpolicy.go b/pkg/client/informers/externalversions/apisx/v1alpha1/backendtrafficpolicy.go new file mode 100644 index 0000000000..27a5e77fb9 --- /dev/null +++ b/pkg/client/informers/externalversions/apisx/v1alpha1/backendtrafficpolicy.go @@ -0,0 +1,90 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + apisxv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" + versioned "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" + internalinterfaces "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "sigs.k8s.io/gateway-api/pkg/client/listers/apisx/v1alpha1" +) + +// BackendTrafficPolicyInformer provides access to a shared informer and lister for +// BackendTrafficPolicies. +type BackendTrafficPolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.BackendTrafficPolicyLister +} + +type backendTrafficPolicyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewBackendTrafficPolicyInformer constructs a new informer for BackendTrafficPolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewBackendTrafficPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredBackendTrafficPolicyInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredBackendTrafficPolicyInformer constructs a new informer for BackendTrafficPolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredBackendTrafficPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ExperimentalV1alpha1().BackendTrafficPolicies(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ExperimentalV1alpha1().BackendTrafficPolicies(namespace).Watch(context.TODO(), options) + }, + }, + &apisxv1alpha1.BackendTrafficPolicy{}, + resyncPeriod, + indexers, + ) +} + +func (f *backendTrafficPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredBackendTrafficPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *backendTrafficPolicyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apisxv1alpha1.BackendTrafficPolicy{}, f.defaultInformer) +} + +func (f *backendTrafficPolicyInformer) Lister() v1alpha1.BackendTrafficPolicyLister { + return v1alpha1.NewBackendTrafficPolicyLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/apisx/v1alpha1/interface.go b/pkg/client/informers/externalversions/apisx/v1alpha1/interface.go index f5eaef9b76..b95538a998 100644 --- a/pkg/client/informers/externalversions/apisx/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/apisx/v1alpha1/interface.go @@ -24,6 +24,8 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // BackendTrafficPolicies returns a BackendTrafficPolicyInformer. + BackendTrafficPolicies() BackendTrafficPolicyInformer // ListenerSets returns a ListenerSetInformer. ListenerSets() ListenerSetInformer } @@ -39,6 +41,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// BackendTrafficPolicies returns a BackendTrafficPolicyInformer. +func (v *version) BackendTrafficPolicies() BackendTrafficPolicyInformer { + return &backendTrafficPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // ListenerSets returns a ListenerSetInformer. func (v *version) ListenerSets() ListenerSetInformer { return &listenerSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 19061e2ddf..d034a3b057 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -95,6 +95,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Gateway().V1beta1().ReferenceGrants().Informer()}, nil // Group=gateway.networking.x-k8s.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("backendtrafficpolicies"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Experimental().V1alpha1().BackendTrafficPolicies().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("listenersets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Experimental().V1alpha1().ListenerSets().Informer()}, nil diff --git a/pkg/client/listers/apisx/v1alpha1/backendtrafficpolicy.go b/pkg/client/listers/apisx/v1alpha1/backendtrafficpolicy.go new file mode 100644 index 0000000000..5f6d3b4e54 --- /dev/null +++ b/pkg/client/listers/apisx/v1alpha1/backendtrafficpolicy.go @@ -0,0 +1,70 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/listers" + "k8s.io/client-go/tools/cache" + v1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" +) + +// BackendTrafficPolicyLister helps list BackendTrafficPolicies. +// All objects returned here must be treated as read-only. +type BackendTrafficPolicyLister interface { + // List lists all BackendTrafficPolicies in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.BackendTrafficPolicy, err error) + // BackendTrafficPolicies returns an object that can list and get BackendTrafficPolicies. + BackendTrafficPolicies(namespace string) BackendTrafficPolicyNamespaceLister + BackendTrafficPolicyListerExpansion +} + +// backendTrafficPolicyLister implements the BackendTrafficPolicyLister interface. +type backendTrafficPolicyLister struct { + listers.ResourceIndexer[*v1alpha1.BackendTrafficPolicy] +} + +// NewBackendTrafficPolicyLister returns a new BackendTrafficPolicyLister. +func NewBackendTrafficPolicyLister(indexer cache.Indexer) BackendTrafficPolicyLister { + return &backendTrafficPolicyLister{listers.New[*v1alpha1.BackendTrafficPolicy](indexer, v1alpha1.Resource("backendtrafficpolicy"))} +} + +// BackendTrafficPolicies returns an object that can list and get BackendTrafficPolicies. +func (s *backendTrafficPolicyLister) BackendTrafficPolicies(namespace string) BackendTrafficPolicyNamespaceLister { + return backendTrafficPolicyNamespaceLister{listers.NewNamespaced[*v1alpha1.BackendTrafficPolicy](s.ResourceIndexer, namespace)} +} + +// BackendTrafficPolicyNamespaceLister helps list and get BackendTrafficPolicies. +// All objects returned here must be treated as read-only. +type BackendTrafficPolicyNamespaceLister interface { + // List lists all BackendTrafficPolicies in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.BackendTrafficPolicy, err error) + // Get retrieves the BackendTrafficPolicy from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.BackendTrafficPolicy, error) + BackendTrafficPolicyNamespaceListerExpansion +} + +// backendTrafficPolicyNamespaceLister implements the BackendTrafficPolicyNamespaceLister +// interface. +type backendTrafficPolicyNamespaceLister struct { + listers.ResourceIndexer[*v1alpha1.BackendTrafficPolicy] +} diff --git a/pkg/client/listers/apisx/v1alpha1/expansion_generated.go b/pkg/client/listers/apisx/v1alpha1/expansion_generated.go index 7c717d0c17..cfeb378ce7 100644 --- a/pkg/client/listers/apisx/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/apisx/v1alpha1/expansion_generated.go @@ -18,6 +18,14 @@ limitations under the License. package v1alpha1 +// BackendTrafficPolicyListerExpansion allows custom methods to be added to +// BackendTrafficPolicyLister. +type BackendTrafficPolicyListerExpansion interface{} + +// BackendTrafficPolicyNamespaceListerExpansion allows custom methods to be added to +// BackendTrafficPolicyNamespaceLister. +type BackendTrafficPolicyNamespaceListerExpansion interface{} + // ListenerSetListerExpansion allows custom methods to be added to // ListenerSetLister. type ListenerSetListerExpansion interface{} diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 3ea10c3f69..4477cf2bb6 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -191,6 +191,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/gateway-api/apis/v1beta1.ReferenceGrantList": schema_sigsk8sio_gateway_api_apis_v1beta1_ReferenceGrantList(ref), "sigs.k8s.io/gateway-api/apis/v1beta1.ReferenceGrantSpec": schema_sigsk8sio_gateway_api_apis_v1beta1_ReferenceGrantSpec(ref), "sigs.k8s.io/gateway-api/apis/v1beta1.ReferenceGrantTo": schema_sigsk8sio_gateway_api_apis_v1beta1_ReferenceGrantTo(ref), + "sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicy": schema_sigsk8sio_gateway_api_apisx_v1alpha1_BackendTrafficPolicy(ref), + "sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicyList": schema_sigsk8sio_gateway_api_apisx_v1alpha1_BackendTrafficPolicyList(ref), + "sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicySpec": schema_sigsk8sio_gateway_api_apisx_v1alpha1_BackendTrafficPolicySpec(ref), "sigs.k8s.io/gateway-api/apisx/v1alpha1.ListenerEntry": schema_sigsk8sio_gateway_api_apisx_v1alpha1_ListenerEntry(ref), "sigs.k8s.io/gateway-api/apisx/v1alpha1.ListenerEntryStatus": schema_sigsk8sio_gateway_api_apisx_v1alpha1_ListenerEntryStatus(ref), "sigs.k8s.io/gateway-api/apisx/v1alpha1.ListenerSet": schema_sigsk8sio_gateway_api_apisx_v1alpha1_ListenerSet(ref), @@ -198,6 +201,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "sigs.k8s.io/gateway-api/apisx/v1alpha1.ListenerSetSpec": schema_sigsk8sio_gateway_api_apisx_v1alpha1_ListenerSetSpec(ref), "sigs.k8s.io/gateway-api/apisx/v1alpha1.ListenerSetStatus": schema_sigsk8sio_gateway_api_apisx_v1alpha1_ListenerSetStatus(ref), "sigs.k8s.io/gateway-api/apisx/v1alpha1.ParentGatewayReference": schema_sigsk8sio_gateway_api_apisx_v1alpha1_ParentGatewayReference(ref), + "sigs.k8s.io/gateway-api/apisx/v1alpha1.RequestRate": schema_sigsk8sio_gateway_api_apisx_v1alpha1_RequestRate(ref), + "sigs.k8s.io/gateway-api/apisx/v1alpha1.RetryConstraint": schema_sigsk8sio_gateway_api_apisx_v1alpha1_RetryConstraint(ref), } } @@ -5646,8 +5651,7 @@ func schema_sigsk8sio_gateway_api_apis_v1_SessionPersistence(ref common.Referenc return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "SessionPersistence defines the desired state of SessionPersistence.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ "sessionName": { SchemaProps: spec.SchemaProps{ @@ -5977,8 +5981,7 @@ func schema_sigsk8sio_gateway_api_apis_v1alpha2_LocalPolicyTargetReference(ref c return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "LocalPolicyTargetReference identifies an API object to apply a direct or inherited policy to. This should be used as part of Policy resources that can target Gateway API resources. For more information on how this policy attachment model works, and a sample Policy resource, refer to the policy attachment documentation for Gateway API.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ "group": { SchemaProps: spec.SchemaProps{ @@ -6158,8 +6161,7 @@ func schema_sigsk8sio_gateway_api_apis_v1alpha2_PolicyStatus(ref common.Referenc return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "PolicyStatus defines the common attributes that all Policies should include within their status.", - Type: []string{"object"}, + Type: []string{"object"}, Properties: map[string]spec.Schema{ "ancestors": { SchemaProps: spec.SchemaProps{ @@ -7671,6 +7673,157 @@ func schema_sigsk8sio_gateway_api_apis_v1beta1_ReferenceGrantTo(ref common.Refer } } +func schema_sigsk8sio_gateway_api_apisx_v1alpha1_BackendTrafficPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BackendTrafficPolicy defines the configuration for how traffic to a target backend should be handled.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the desired state of BackendTrafficPolicy.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicySpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status defines the current state of BackendTrafficPolicy.", + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/gateway-api/apis/v1alpha2.PolicyStatus"), + }, + }, + }, + Required: []string{"spec"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "sigs.k8s.io/gateway-api/apis/v1alpha2.PolicyStatus", "sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicySpec"}, + } +} + +func schema_sigsk8sio_gateway_api_apisx_v1alpha1_BackendTrafficPolicyList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BackendTrafficPolicyList contains a list of BackendTrafficPolicies", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicy"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "sigs.k8s.io/gateway-api/apisx/v1alpha1.BackendTrafficPolicy"}, + } +} + +func schema_sigsk8sio_gateway_api_apisx_v1alpha1_BackendTrafficPolicySpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BackendTrafficPolicySpec define the desired state of BackendTrafficPolicy Note: there is no Override or Default policy configuration.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetRefs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "group", + "kind", + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "TargetRefs identifies API object(s) to apply this policy to. Currently, Backends (A grouping of like endpoints such as Service, ServiceImport, or any implementation-specific backendRef) are the only valid API target references.\n\nCurrently, a TargetRef can not be scoped to a specific port on a Service.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("sigs.k8s.io/gateway-api/apis/v1alpha2.LocalPolicyTargetReference"), + }, + }, + }, + }, + }, + "retryConstraint": { + SchemaProps: spec.SchemaProps{ + Description: "RetryConstraint defines the configuration for when to allow or prevent further retries to a target backend, by dynamically calculating a 'retry budget'. This budget is calculated based on the percentage of incoming traffic composed of retries over a given time interval. Once the budget is exceeded, additional retries will be rejected.\n\nFor example, if the retry budget interval is 10 seconds, there have been 1000 active requests in the past 10 seconds, and the allowed percentage of requests that can be retried is 20% (the default), then 200 of those requests may be composed of retries. Active requests will only be considered for the duration of the interval when calculating the retry budget. Retrying the same original request multiple times within the retry budget interval will lead to each retry being counted towards calculating the budget.\n\nConfiguring a RetryConstraint in BackendTrafficPolicy is compatible with HTTPRoute Retry settings for each HTTPRouteRule that targets the same backend. While the HTTPRouteRule Retry stanza can specify whether a request will be retried, and the number of retry attempts each client may perform, RetryConstraint helps prevent cascading failures such as retry storms during periods of consistent failures.\n\nAfter the retry budget has been exceeded, additional retries to the backend MUST return a 503 response to the client.\n\nAdditional configurations for defining a constraint on retries MAY be defined in the future.\n\nSupport: Extended\n\n", + Ref: ref("sigs.k8s.io/gateway-api/apisx/v1alpha1.RetryConstraint"), + }, + }, + "sessionPersistence": { + SchemaProps: spec.SchemaProps{ + Description: "SessionPersistence defines and configures session persistence for the backend.\n\nSupport: Extended", + Ref: ref("sigs.k8s.io/gateway-api/apis/v1.SessionPersistence"), + }, + }, + }, + Required: []string{"targetRefs"}, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/gateway-api/apis/v1.SessionPersistence", "sigs.k8s.io/gateway-api/apis/v1alpha2.LocalPolicyTargetReference", "sigs.k8s.io/gateway-api/apisx/v1alpha1.RetryConstraint"}, + } +} + func schema_sigsk8sio_gateway_api_apisx_v1alpha1_ListenerEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -8047,3 +8200,65 @@ func schema_sigsk8sio_gateway_api_apisx_v1alpha1_ParentGatewayReference(ref comm }, } } + +func schema_sigsk8sio_gateway_api_apisx_v1alpha1_RequestRate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RequestRate expresses a rate of requests over a given period of time.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "count": { + SchemaProps: spec.SchemaProps{ + Description: "Count specifies the number of requests per time interval.\n\nSupport: Extended", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "interval": { + SchemaProps: spec.SchemaProps{ + Description: "Interval specifies the divisor of the rate of requests, the amount of time during which the given count of requests occur.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_sigsk8sio_gateway_api_apisx_v1alpha1_RetryConstraint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RetryConstraint defines the configuration for when to retry a request.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "budgetPercent": { + SchemaProps: spec.SchemaProps{ + Description: "BudgetPercent defines the maximum percentage of active requests that may be made up of retries.\n\nSupport: Extended", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "budgetInterval": { + SchemaProps: spec.SchemaProps{ + Description: "BudgetInterval defines the duration in which requests will be considered for calculating the budget for retries.\n\nSupport: Extended", + Type: []string{"string"}, + Format: "", + }, + }, + "minRetryRate": { + SchemaProps: spec.SchemaProps{ + Description: "MinRetryRate defines the minimum rate of retries that will be allowable over a specified duration of time.\n\nThe effective overall minimum rate of retries targeting the backend service may be much higher, as there can be any number of clients which are applying this setting locally.\n\nThis ensures that requests can still be retried during periods of low traffic, where the budget for retries may be calculated as a very low value.\n\nSupport: Extended", + Ref: ref("sigs.k8s.io/gateway-api/apisx/v1alpha1.RequestRate"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "sigs.k8s.io/gateway-api/apisx/v1alpha1.RequestRate"}, + } +} diff --git a/pkg/test/cel/backendtrafficpolicy_test.go b/pkg/test/cel/backendtrafficpolicy_test.go new file mode 100644 index 0000000000..ca7aee691f --- /dev/null +++ b/pkg/test/cel/backendtrafficpolicy_test.go @@ -0,0 +1,269 @@ +//go:build experimental +// +build experimental + +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1a2 "sigs.k8s.io/gateway-api/apisx/v1alpha1" +) + +func TestBackendTrafficPolicyConfig(t *testing.T) { + tests := []struct { + name string + wantErrors []string + sessionPersistence gatewayv1a2.SessionPersistence + retryConstraint gatewayv1a2.RetryConstraint + }{ + { + name: "valid BackendTrafficPolicyConfig no retryConstraint budgetPercent", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{}, + }, + { + name: "valid BackendTrafficPolicyConfig no retryConstraint budgetInterval", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{}, + }, + { + name: "valid BackendTrafficPolicyConfig no retryConstraint minRetryRate", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + }, + wantErrors: []string{}, + }, + { + name: "invalid BackendTrafficPolicyConfig budgetInterval too long", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("2h"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("10s"), + }), + }, + wantErrors: []string{"budgetInterval can not be greater than one hour or less than one second"}, + }, + { + name: "invalid BackendTrafficPolicyConfig budgetInterval too short", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("1ms"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("10s"), + }), + }, + wantErrors: []string{"budgetInterval can not be greater than one hour or less than one second"}, + }, + { + name: "invalid BackendTrafficPolicyConfig minRetryRate interval", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("2h"), + }), + }, + wantErrors: []string{"interval can not be greater than one hour"}, + }, + { + name: "valid BackendTrafficPolicyConfig no cookie lifetimeType", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{}, + }, + { + name: "valid BackendTrafficPolicyConfig session cookie", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + CookieConfig: &gatewayv1.CookieConfig{ + LifetimeType: ptrTo(gatewayv1.SessionCookieLifetimeType), + }, + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{}, + }, + { + name: "invalid BackendTrafficPolicyConfig permanent cookie", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + CookieConfig: &gatewayv1.CookieConfig{ + LifetimeType: ptrTo(gatewayv1.PermanentCookieLifetimeType), + }, + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{"AbsoluteTimeout must be specified when cookie lifetimeType is Permanent"}, + }, + { + name: "valid BackendTrafficPolicyConfig permanent cookie", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + AbsoluteTimeout: toDuration("1h"), + Type: ptrTo(gatewayv1.CookieBasedSessionPersistence), + CookieConfig: &gatewayv1.CookieConfig{ + LifetimeType: ptrTo(gatewayv1.PermanentCookieLifetimeType), + }, + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{}, + }, + { + name: "valid BackendTrafficPolicyConfig header-based session persistence", + sessionPersistence: gatewayv1a2.SessionPersistence{ + SessionName: ptrTo("foo"), + Type: ptrTo(gatewayv1.HeaderBasedSessionPersistence), + }, + retryConstraint: gatewayv1a2.RetryConstraint{ + BudgetPercent: ptrTo(20), + BudgetInterval: toDuration("10s"), + MinRetryRate: ptrTo(gatewayv1a2.RequestRate{ + Count: ptrTo(10), + Interval: toDuration("1s"), + }), + }, + wantErrors: []string{}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + trafficPolicy := &gatewayv1a2.BackendTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()), + Namespace: metav1.NamespaceDefault, + }, + Spec: gatewayv1a2.BackendTrafficPolicySpec{ + TargetRefs: []gatewayv1a2.LocalPolicyTargetReference{{ + Group: "group", + Kind: "kind", + Name: "name", + }}, + RetryConstraint: &tc.retryConstraint, + SessionPersistence: &tc.sessionPersistence, + }, + } + validateBackendTrafficPolicy(t, trafficPolicy, tc.wantErrors) + }) + } +} + +func validateBackendTrafficPolicy(t *testing.T, trafficPolicy *gatewayv1a2.BackendTrafficPolicy, wantErrors []string) { + t.Helper() + + ctx := context.Background() + err := k8sClient.Create(ctx, trafficPolicy) + + if (len(wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating BackendTrafficPolicy %q; got err=\n%v\n;want error=%v", fmt.Sprintf("%v/%v", trafficPolicy.Namespace, trafficPolicy.Name), err, wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range wantErrors { + if !celErrorStringMatches(err.Error(), wantError) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating BackendTrafficPolicy %q; got err=\n%v\n;missing strings within error=%q", fmt.Sprintf("%v/%v", trafficPolicy.Namespace, trafficPolicy.Name), err, missingErrorStrings) + } +} diff --git a/pkg/test/cel/main_test.go b/pkg/test/cel/main_test.go index 96502432ef..3ba7de1d20 100644 --- a/pkg/test/cel/main_test.go +++ b/pkg/test/cel/main_test.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1alpha3" "sigs.k8s.io/gateway-api/apis/v1beta1" + apisxv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" @@ -53,6 +54,7 @@ func TestMain(m *testing.M) { v1alpha2.Install(k8sClient.Scheme()) v1beta1.Install(k8sClient.Scheme()) v1.Install(k8sClient.Scheme()) + apisxv1alpha1.Install(k8sClient.Scheme()) os.Exit(m.Run()) }