From 8a86aea1706d3aaf2b018c6cac6a7c2b18846dd6 Mon Sep 17 00:00:00 2001 From: zoumo Date: Tue, 15 Jul 2025 14:38:36 +0800 Subject: [PATCH 01/10] refactor: change traffic api --- apis/rollout/v1alpha1/rollout_gateway_api.go | 202 +- apis/rollout/v1alpha1/rolloutrun_types.go | 2 +- .../rollout/v1alpha1/rolloutstrategy_types.go | 4 +- apis/rollout/v1alpha1/traffic_route_types.go | 10 +- .../rollout/v1alpha1/validation/rolloutrun.go | 4 +- .../v1alpha1/validation/rolloutrun_test.go | 16 +- .../v1alpha1/validation/rolloutstrategy.go | 16 +- .../validation/rolloutstrategy_test.go | 50 +- .../rollout/v1alpha1/zz_generated.deepcopy.go | 78 +- apis/rollout/well_known_labels.go | 6 +- ...ollout.kusionstack.io_backendroutings.yaml | 2012 +++++++- .../rollout.kusionstack.io_rolloutruns.yaml | 4565 +++++++++++++++-- .../rollout.kusionstack.io_rollouts.yaml | 22 +- ...lout.kusionstack.io_rolloutstrategies.yaml | 4547 ++++++++++++++-- ...lout.kusionstack.io_traffictopologies.yaml | 22 +- config/kind/workload/bases/rollout.yaml | 7 +- config/kind/workload/bases/traffic.yaml | 2 + hack/make-rules/update-manifests.sh | 7 + pkg/backend/service/backend.go | 4 +- .../backendrouting_controller_test.go | 130 +- .../podcanarylabel/podcanarylabel.go | 12 +- pkg/controllers/rollout/utils.go | 8 +- pkg/controllers/rolloutrun/executor/canary.go | 4 +- pkg/route/ingress/route.go | 50 +- 24 files changed, 10390 insertions(+), 1390 deletions(-) diff --git a/apis/rollout/v1alpha1/rollout_gateway_api.go b/apis/rollout/v1alpha1/rollout_gateway_api.go index d60b346..171ba8d 100644 --- a/apis/rollout/v1alpha1/rollout_gateway_api.go +++ b/apis/rollout/v1alpha1/rollout_gateway_api.go @@ -21,6 +21,11 @@ import ( ) type HTTPRouteMatch struct { + // Path specifies a HTTP request path matcher. + // + // +optional + Path *gatewayapiv1.HTTPPathMatch `json:"path,omitempty"` + // Headers specifies HTTP request header matchers. Multiple match values are // ANDed together, meaning, a request must match all the specified headers // to select the route. @@ -43,19 +48,196 @@ type HTTPRouteMatch struct { QueryParams []gatewayapiv1.HTTPQueryParamMatch `json:"queryParams,omitempty"` } -type HTTPRouteFilter struct { - // RequestHeaderModifier defines a schema for a filter that modifies request - // headers. +type HTTPRouteRule struct { + // Matches define conditions used for matching the rule against incoming + // HTTP requests. Each match is independent, i.e. this rule will be matched + // if **any** one of the matches is satisfied. + // + // For example, take the following matches configuration: + // + // ``` + // matches: + // - path: + // value: "/foo" + // headers: + // - name: "version" + // value: "v2" + // - path: + // value: "/v2/foo" + // ``` + // + // For a request to match against this rule, a request must satisfy + // EITHER of the two conditions: + // + // - path prefixed with `/foo` AND contains the header `version: v2` + // - path prefix of `/v2/foo` + // + // See the documentation for HTTPRouteMatch on how to specify multiple + // match conditions that should be ANDed together. + // + // If no matches are specified, the default is a prefix + // path match on "/", which has the effect of matching every + // HTTP request. + // + // Proxy or Load Balancer routing configuration generated from HTTPRoutes + // MUST prioritize matches based on the following criteria, continuing on + // ties. Across all rules specified on applicable Routes, precedence must be + // given to the match having: + // + // * "Exact" path match. + // * "Prefix" path match with largest number of characters. + // * Method match. + // * Largest number of header matches. + // * Largest number of query param matches. + // + // Note: The precedence of RegularExpression path matches are implementation-specific. + // + // If ties still exist across multiple Routes, matching precedence MUST be + // determined in order of the following criteria, continuing on ties: + // + // * The oldest Route based on creation timestamp. + // * The Route appearing first in alphabetical order by + // "{namespace}/{name}". + // + // If ties still exist within an HTTPRoute, matching precedence MUST be granted + // to the FIRST matching rule (in list order) with a match meeting the above + // criteria. + // + // When no rules matching a request have been successfully attached to the + // parent a request is coming from, a HTTP 404 status code MUST be returned. + // + // +optional + // +kubebuilder:validation:MaxItems=8 + Matches []HTTPRouteMatch `json:"matches,omitempty"` + // Filters define the filters that are applied to requests that match + // this rule. + // + // The effects of ordering of multiple behaviors are currently unspecified. + // This can change in the future based on feedback during the alpha stage. + // + // Conformance-levels at this level are defined based on the type of filter: + // + // - ALL core filters MUST be supported by all implementations. + // - Implementers are encouraged to support extended filters. + // - Implementation-specific custom filters have no API guarantees across + // implementations. + // + // Specifying the same filter multiple times is not supported unless explicitly + // indicated in the filter. + // + // All filters are expected to be compatible with each other except for the + // URLRewrite and RequestRedirect filters, which may not be combined. If an + // implementation can not support other combinations of filters, they must clearly + // document that limitation. In cases where incompatible or unsupported + // filters are specified and cause the `Accepted` condition to be set to status + // `False`, implementations may use the `IncompatibleFilters` reason to specify + // this configuration error. // // Support: Core // // +optional - RequestHeaderModifier *gatewayapiv1.HTTPHeaderFilter `json:"requestHeaderModifier,omitempty"` + // +kubebuilder:validation:MaxItems=16 + // +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))" + // +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1" + // +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1" + // +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.filter(f, f.type == 'RequestRedirect').size() <= 1" + // +kubebuilder:validation:XValidation:message="URLRewrite filter cannot be repeated",rule="self.filter(f, f.type == 'URLRewrite').size() <= 1" + Filters []gatewayapiv1.HTTPRouteFilter `json:"filters,omitempty"` } -type HTTPRouteRule struct { - // Matches define conditions used for matching the incoming HTTP requests to canary service. - Matches []HTTPRouteMatch `json:"matches,omitempty"` - // Filter defines a filter for the canary service. - Filter HTTPRouteFilter `json:"filter,omitempty"` -} +// type BaseHTTPRouteRule struct { +// // Matches define conditions used for matching the rule against incoming +// // HTTP requests. Each match is independent, i.e. this rule will be matched +// // if **any** one of the matches is satisfied. +// // +// // For example, take the following matches configuration: +// // +// // ``` +// // matches: +// // - path: +// // value: "/foo" +// // headers: +// // - name: "version" +// // value: "v2" +// // - path: +// // value: "/v2/foo" +// // ``` +// // +// // For a request to match against this rule, a request must satisfy +// // EITHER of the two conditions: +// // +// // - path prefixed with `/foo` AND contains the header `version: v2` +// // - path prefix of `/v2/foo` +// // +// // See the documentation for HTTPRouteMatch on how to specify multiple +// // match conditions that should be ANDed together. +// // +// // If no matches are specified, the default is a prefix +// // path match on "/", which has the effect of matching every +// // HTTP request. +// // +// // Proxy or Load Balancer routing configuration generated from HTTPRoutes +// // MUST prioritize matches based on the following criteria, continuing on +// // ties. Across all rules specified on applicable Routes, precedence must be +// // given to the match having: +// // +// // * "Exact" path match. +// // * "Prefix" path match with largest number of characters. +// // * Method match. +// // * Largest number of header matches. +// // * Largest number of query param matches. +// // +// // Note: The precedence of RegularExpression path matches are implementation-specific. +// // +// // If ties still exist across multiple Routes, matching precedence MUST be +// // determined in order of the following criteria, continuing on ties: +// // +// // * The oldest Route based on creation timestamp. +// // * The Route appearing first in alphabetical order by +// // "{namespace}/{name}". +// // +// // If ties still exist within an HTTPRoute, matching precedence MUST be granted +// // to the FIRST matching rule (in list order) with a match meeting the above +// // criteria. +// // +// // When no rules matching a request have been successfully attached to the +// // parent a request is coming from, a HTTP 404 status code MUST be returned. +// // +// // +optional +// // +kubebuilder:validation:MaxItems=8 +// Matches []HTTPRouteMatch `json:"matches,omitempty"` +// // Filters define the filters that are applied to requests that match +// // this rule. +// // +// // The effects of ordering of multiple behaviors are currently unspecified. +// // This can change in the future based on feedback during the alpha stage. +// // +// // Conformance-levels at this level are defined based on the type of filter: +// // +// // - ALL core filters MUST be supported by all implementations. +// // - Implementers are encouraged to support extended filters. +// // - Implementation-specific custom filters have no API guarantees across +// // implementations. +// // +// // Specifying the same filter multiple times is not supported unless explicitly +// // indicated in the filter. +// // +// // All filters are expected to be compatible with each other except for the +// // URLRewrite and RequestRedirect filters, which may not be combined. If an +// // implementation can not support other combinations of filters, they must clearly +// // document that limitation. In cases where incompatible or unsupported +// // filters are specified and cause the `Accepted` condition to be set to status +// // `False`, implementations may use the `IncompatibleFilters` reason to specify +// // this configuration error. +// // +// // Support: Core +// // +// // +optional +// // +kubebuilder:validation:MaxItems=16 +// // +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))" +// // +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1" +// // +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1" +// // +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.filter(f, f.type == 'RequestRedirect').size() <= 1" +// // +kubebuilder:validation:XValidation:message="URLRewrite filter cannot be repeated",rule="self.filter(f, f.type == 'URLRewrite').size() <= 1" +// Filters []gatewayapiv1.HTTPRouteFilter `json:"filters,omitempty"` +// } diff --git a/apis/rollout/v1alpha1/rolloutrun_types.go b/apis/rollout/v1alpha1/rolloutrun_types.go index 444623e..b9cf496 100644 --- a/apis/rollout/v1alpha1/rolloutrun_types.go +++ b/apis/rollout/v1alpha1/rolloutrun_types.go @@ -112,7 +112,7 @@ type RolloutRunCanaryStrategy struct { // PodTemplateMetadataPatch defines a patch for workload podTemplate metadata. // +optional - PodTemplateMetadataPatch *MetadataPatch `json:"podTemplateMetadataPatch,omitempty"` + TemplateMetadataPatch *MetadataPatch `json:"podTemplateMetadataPatch,omitempty"` } type RolloutRunStepTarget struct { diff --git a/apis/rollout/v1alpha1/rolloutstrategy_types.go b/apis/rollout/v1alpha1/rolloutstrategy_types.go index c4c7091..350ed5e 100644 --- a/apis/rollout/v1alpha1/rolloutstrategy_types.go +++ b/apis/rollout/v1alpha1/rolloutstrategy_types.go @@ -128,7 +128,7 @@ type CanaryStrategy struct { // +optional Properties map[string]string `json:"properties,omitempty"` - // PodTemplateMetadataPatch defines a patch for workload podTemplate metadata. + // TemplateMetadataPatch defines a patch for workload template metadata. // +optional - PodTemplateMetadataPatch *MetadataPatch `json:"podTemplateMetadataPatch,omitempty"` + TemplateMetadataPatch *MetadataPatch `json:"templateMetadataPatch,omitempty"` } diff --git a/apis/rollout/v1alpha1/traffic_route_types.go b/apis/rollout/v1alpha1/traffic_route_types.go index b5c516d..419d9f2 100644 --- a/apis/rollout/v1alpha1/traffic_route_types.go +++ b/apis/rollout/v1alpha1/traffic_route_types.go @@ -193,12 +193,18 @@ type CanaryBackendRule struct { } type TrafficStrategy struct { + HTTP *HTTPTrafficStrategy `json:"http,omitempty"` +} + +type HTTPTrafficStrategy struct { + HTTPRouteRule `json:",inline"` // Weight indicate how many percentage of traffic the canary pods should receive // // +kubebuilder:validation:Minimum=0 // +kubebuilder:validation:Maximum=100 - Weight *int32 `json:"weight,omitempty"` - HTTPRule *HTTPRouteRule `json:"http,omitempty"` + Weight *int32 `json:"weight,omitempty"` + // BaseTraffic indicate the base traffic rule + BaseTraffic *HTTPRouteRule `json:"baseTraffic,omitempty"` } type BackendRoutingStatus struct { diff --git a/apis/rollout/v1alpha1/validation/rolloutrun.go b/apis/rollout/v1alpha1/validation/rolloutrun.go index e1d385b..f9df647 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun.go @@ -58,7 +58,7 @@ func ValidateRolloutRunCanaryStrategy(canary *rolloutv1alpha1.RolloutRunCanarySt // validate targets allErrs = append(allErrs, validateRolloutRunStepTargets(canary.Targets, fldPath.Child("targets"))...) // validate pod template metadata path - allErrs = append(allErrs, validatePodTemplatePatch(canary.PodTemplateMetadataPatch, fldPath.Child("podTemplateMetadataPath"))...) + allErrs = append(allErrs, validateTemplateMetadataPatch(canary.TemplateMetadataPatch, fldPath.Child("podTemplateMetadataPath"))...) // validate traffic allErrs = append(allErrs, validateTrafficStrategy(canary.Traffic, fldPath.Child("traffic"))...) @@ -133,7 +133,7 @@ func ValidateRolloutRunUpdate(newObj, oldObj *rolloutv1alpha1.RolloutRun) field. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("canary"), "canary is immutable")) } else if oldObj.Spec.Canary != nil && newObj.Spec.Canary != nil { // pod template metadata patch is immutable - if !apiequality.Semantic.DeepEqual(newObj.Spec.Canary.PodTemplateMetadataPatch, oldObj.Spec.Canary.PodTemplateMetadataPatch) { + if !apiequality.Semantic.DeepEqual(newObj.Spec.Canary.TemplateMetadataPatch, oldObj.Spec.Canary.TemplateMetadataPatch) { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec").Child("canary").Child("podTemplateMetadataPatch"), "podTemplateMetadataPatch is immutable")) } diff --git a/apis/rollout/v1alpha1/validation/rolloutrun_test.go b/apis/rollout/v1alpha1/validation/rolloutrun_test.go index 742dba9..233d144 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun_test.go @@ -75,7 +75,7 @@ func newValidRollotRun() *rolloutv1alpha1.RolloutRun { Replicas: intstr.FromInt(1), }, }, - PodTemplateMetadataPatch: &rolloutv1alpha1.MetadataPatch{ + TemplateMetadataPatch: &rolloutv1alpha1.MetadataPatch{ Labels: map[string]string{ "canary": "true", }, @@ -305,7 +305,9 @@ func TestValidateRolloutRunUpdate(t *testing.T) { obj := validRolloutRun.DeepCopy() obj.Spec.Canary.Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Canary.Traffic = &rolloutv1alpha1.TrafficStrategy{ - Weight: ptr.To[int32](10), + HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ + Weight: ptr.To[int32](10), + }, } return obj }(), @@ -320,7 +322,7 @@ func TestValidateRolloutRunUpdate(t *testing.T) { State: rolloutv1alpha1.RolloutStepRunning, } obj.Spec.Canary.Targets[0].Replicas = intstr.FromInt(2) - obj.Spec.Canary.PodTemplateMetadataPatch.Labels["canary"] = "false" + obj.Spec.Canary.TemplateMetadataPatch.Labels["canary"] = "false" return obj }(), wantErr: true, @@ -350,7 +352,9 @@ func TestValidateRolloutRunUpdate(t *testing.T) { } obj.Spec.Batch.Batches[0].Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Batch.Batches[0].Traffic = &rolloutv1alpha1.TrafficStrategy{ - Weight: ptr.To[int32](10), + HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ + Weight: ptr.To[int32](10), + }, } return obj }(), @@ -387,7 +391,9 @@ func TestValidateRolloutRunUpdate(t *testing.T) { obj.Spec.Batch.Batches[0].Breakpoint = true obj.Spec.Batch.Batches[0].Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Batch.Batches[0].Traffic = &rolloutv1alpha1.TrafficStrategy{ - Weight: ptr.To[int32](10), + HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ + Weight: ptr.To[int32](10), + }, } return obj }(), diff --git a/apis/rollout/v1alpha1/validation/rolloutstrategy.go b/apis/rollout/v1alpha1/validation/rolloutstrategy.go index fa15d04..183fedd 100644 --- a/apis/rollout/v1alpha1/validation/rolloutstrategy.go +++ b/apis/rollout/v1alpha1/validation/rolloutstrategy.go @@ -78,13 +78,13 @@ func ValidateCanaryStrategy(strategy *rolloutv1alpha1.CanaryStrategy, fldPath *f allErrs = append(allErrs, appsvalidation.ValidatePositiveIntOrPercent(strategy.Replicas, fldPath.Child("replicas"))...) allErrs = append(allErrs, ValidateResourceMatch(strategy.Match, fldPath.Child("matchTargets"))...) - allErrs = append(allErrs, validatePodTemplatePatch(strategy.PodTemplateMetadataPatch, fldPath.Child("patch"))...) + allErrs = append(allErrs, validateTemplateMetadataPatch(strategy.TemplateMetadataPatch, fldPath.Child("patch"))...) allErrs = append(allErrs, validateTrafficStrategy(strategy.Traffic, fldPath.Child("traffic"))...) return allErrs } -func validatePodTemplatePatch(patch *rolloutv1alpha1.MetadataPatch, fldPath *field.Path) field.ErrorList { +func validateTemplateMetadataPatch(patch *rolloutv1alpha1.MetadataPatch, fldPath *field.Path) field.ErrorList { if patch == nil { return nil } @@ -140,8 +140,16 @@ func validateTrafficStrategy(traffic *rolloutv1alpha1.TrafficStrategy, fldPath * } allErrs := field.ErrorList{} - if traffic.Weight != nil && (traffic.HTTPRule != nil && len(traffic.HTTPRule.Matches) > 0) { - allErrs = append(allErrs, field.Forbidden(fldPath, "weight and http rule matches cannot be specified together")) + if traffic.HTTP != nil { + if traffic.HTTP.Weight != nil { + if len(traffic.HTTP.Matches) > 0 { + allErrs = append(allErrs, field.Forbidden(fldPath, "weight and http rule matches cannot be specified together")) + } + if traffic.HTTP.BaseTraffic != nil { + allErrs = append(allErrs, field.Forbidden(fldPath, "weight and base traffic cannot be specified together")) + } + } } + return allErrs } diff --git a/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go b/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go index c2f4fbb..e5e4023 100644 --- a/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go @@ -28,14 +28,18 @@ import ( ) var validTraffic = &rolloutv1alpha1.TrafficStrategy{ - Weight: ptr.To[int32](10), - HTTPRule: &rolloutv1alpha1.HTTPRouteRule{ - Filter: rolloutv1alpha1.HTTPRouteFilter{ - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "foo", - Value: "bar", + HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ + Weight: ptr.To[int32](10), + HTTPRouteRule: rolloutv1alpha1.HTTPRouteRule{ + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "foo", + Value: "bar", + }, + }, }, }, }, @@ -44,14 +48,28 @@ var validTraffic = &rolloutv1alpha1.TrafficStrategy{ } var invalidTraffic = &rolloutv1alpha1.TrafficStrategy{ - Weight: ptr.To[int32](10), - HTTPRule: &rolloutv1alpha1.HTTPRouteRule{ - Matches: []rolloutv1alpha1.HTTPRouteMatch{ - { - Headers: []gatewayapiv1.HTTPHeaderMatch{ - { - Name: "foo", - Value: "bar", + HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ + Weight: ptr.To[int32](10), + HTTPRouteRule: rolloutv1alpha1.HTTPRouteRule{ + Matches: []rolloutv1alpha1.HTTPRouteMatch{ + { + Headers: []gatewayapiv1.HTTPHeaderMatch{ + { + Name: "foo", + Value: "bar", + }, + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "foo", + Value: "bar", + }, + }, }, }, }, diff --git a/apis/rollout/v1alpha1/zz_generated.deepcopy.go b/apis/rollout/v1alpha1/zz_generated.deepcopy.go index 3890c5d..14077ff 100644 --- a/apis/rollout/v1alpha1/zz_generated.deepcopy.go +++ b/apis/rollout/v1alpha1/zz_generated.deepcopy.go @@ -357,8 +357,8 @@ func (in *CanaryStrategy) DeepCopyInto(out *CanaryStrategy) { (*out)[key] = val } } - if in.PodTemplateMetadataPatch != nil { - in, out := &in.PodTemplateMetadataPatch, &out.PodTemplateMetadataPatch + if in.TemplateMetadataPatch != nil { + in, out := &in.TemplateMetadataPatch, &out.TemplateMetadataPatch *out = new(MetadataPatch) (*in).DeepCopyInto(*out) } @@ -444,29 +444,13 @@ func (in *CrossClusterObjectReference) DeepCopy() *CrossClusterObjectReference { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRouteFilter) DeepCopyInto(out *HTTPRouteFilter) { +func (in *HTTPRouteMatch) DeepCopyInto(out *HTTPRouteMatch) { *out = *in - if in.RequestHeaderModifier != nil { - in, out := &in.RequestHeaderModifier, &out.RequestHeaderModifier - *out = new(v1.HTTPHeaderFilter) + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(v1.HTTPPathMatch) (*in).DeepCopyInto(*out) } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteFilter. -func (in *HTTPRouteFilter) DeepCopy() *HTTPRouteFilter { - if in == nil { - return nil - } - out := new(HTTPRouteFilter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRouteMatch) DeepCopyInto(out *HTTPRouteMatch) { - *out = *in if in.Headers != nil { in, out := &in.Headers, &out.Headers *out = make([]v1.HTTPHeaderMatch, len(*in)) @@ -504,7 +488,13 @@ func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.Filter.DeepCopyInto(&out.Filter) + if in.Filters != nil { + in, out := &in.Filters, &out.Filters + *out = make([]v1.HTTPRouteFilter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -518,6 +508,33 @@ func (in *HTTPRouteRule) DeepCopy() *HTTPRouteRule { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPTrafficStrategy) DeepCopyInto(out *HTTPTrafficStrategy) { + *out = *in + in.HTTPRouteRule.DeepCopyInto(&out.HTTPRouteRule) + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(int32) + **out = **in + } + if in.BaseTraffic != nil { + in, out := &in.BaseTraffic, &out.BaseTraffic + *out = new(HTTPRouteRule) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPTrafficStrategy. +func (in *HTTPTrafficStrategy) DeepCopy() *HTTPTrafficStrategy { + if in == nil { + return nil + } + out := new(HTTPTrafficStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MetadataPatch) DeepCopyInto(out *MetadataPatch) { *out = *in @@ -811,8 +828,8 @@ func (in *RolloutRunCanaryStrategy) DeepCopyInto(out *RolloutRunCanaryStrategy) (*out)[key] = val } } - if in.PodTemplateMetadataPatch != nil { - in, out := &in.PodTemplateMetadataPatch, &out.PodTemplateMetadataPatch + if in.TemplateMetadataPatch != nil { + in, out := &in.TemplateMetadataPatch, &out.TemplateMetadataPatch *out = new(MetadataPatch) (*in).DeepCopyInto(*out) } @@ -1509,14 +1526,9 @@ func (in *TopologyInfo) DeepCopy() *TopologyInfo { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrafficStrategy) DeepCopyInto(out *TrafficStrategy) { *out = *in - if in.Weight != nil { - in, out := &in.Weight, &out.Weight - *out = new(int32) - **out = **in - } - if in.HTTPRule != nil { - in, out := &in.HTTPRule, &out.HTTPRule - *out = new(HTTPRouteRule) + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(HTTPTrafficStrategy) (*in).DeepCopyInto(*out) } return diff --git a/apis/rollout/well_known_labels.go b/apis/rollout/well_known_labels.go index 3a4b086..30cec91 100644 --- a/apis/rollout/well_known_labels.go +++ b/apis/rollout/well_known_labels.go @@ -26,9 +26,9 @@ const ( // This label will be added to canary workload and pods. LabelCanary = "rollout.kusionstack.io/canary" // This label indicates the revision of pods controlled by workload. - LabelPodRevision = "pod.rollout.kusionstack.io/revision" - LabelValuePodRevisionBase = "base" - LabelValuePodRevisionCanary = "canary" + LabelTrafficRevision = "traffic.rollout.kusionstack.io/revision" + LabelValueTrafficRevisionBase = "base" + LabelValueTrafficRevisionCanary = "canary" ) // rollout class label diff --git a/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml b/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml index 7cfcb2a..960a4f1 100644 --- a/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml +++ b/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -91,158 +90,1894 @@ spec: properties: http: properties: - filter: - description: Filter defines a filter for the canary service. + baseTraffic: + description: BaseTraffic indicate the base traffic rule properties: - requestHeaderModifier: + filters: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - Input: - GET /foo HTTP/1.1 - my-header: foo + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Config: + Support: Core + properties: add: - - name: "my-header" - value: "bar,baz" + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header - name and value as defined by RFC 7230. + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended properties: - name: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 + Support: Core + maxLength: 253 minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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: + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: - name - - value + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Config: - remove: ["my-header1", "my-header3"] + Support: Extended (Exact) - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - set: - - name: "my-header" - value: "bar" + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header - name and value as defined by RFC 7230. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource properties: - name: + group: + default: "" description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 + Support: Implementation-specific (Services with type ExternalName) + maxLength: 63 minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer required: - name - - value type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array matches: - description: Matches define conditions used for matching - the incoming HTTP requests to canary service. + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. items: properties: headers: @@ -298,8 +2033,7 @@ spec: - RegularExpression type: string value: - description: Value is the value of HTTP Header - to be matched. + description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string @@ -312,6 +2046,30 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match @@ -372,8 +2130,7 @@ spec: - RegularExpression type: string value: - description: Value is the value of HTTP query - param to be matched. + description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string @@ -387,33 +2144,30 @@ spec: - name x-kubernetes-list-type: map type: object + maxItems: 8 type: array + weight: + description: Weight indicate how many percentage of traffic the canary pods should receive + format: int32 + maximum: 100 + minimum: 0 + type: integer type: object name: - description: the temporary canary backend service name, generally - it is the {originServiceName}-canary + description: the temporary canary backend service name, generally it is the {originServiceName}-canary type: string - weight: - description: Weight indicate how many percentage of traffic - the canary pods should receive - format: int32 - maximum: 100 - minimum: 0 - type: integer type: object stable: properties: name: - description: the temporary stable backend service name, generally - it is the {originServiceName}-stable + description: the temporary stable backend service name, generally it is the {originServiceName}-stable type: string type: object type: object routes: description: Routes defines the list of routes items: - description: CrossClusterObjectReference is a reference to a kubernetes - object in a different cluster. + description: CrossClusterObjectReference is a reference to a kubernetes object in a different cluster. properties: apiVersion: description: |- @@ -451,8 +2205,7 @@ spec: description: Canary backend status properties: conditions: - description: Conditions represents the current condition of - an backend. + description: Conditions represents the current condition of an backend. properties: ready: description: |- @@ -479,8 +2232,7 @@ spec: description: Origin backend status properties: conditions: - description: Conditions represents the current condition of - an backend. + description: Conditions represents the current condition of an backend. properties: ready: description: |- @@ -507,8 +2259,7 @@ spec: description: Stable backend status properties: conditions: - description: Conditions represents the current condition of - an backend. + description: Conditions represents the current condition of an backend. properties: ready: description: |- @@ -542,8 +2293,7 @@ spec: routeStatuses: description: route statuses items: - description: BackendRouteStatus defines the status of a backend - route. + description: BackendRouteStatus defines the status of a backend route. properties: apiVersion: description: |- diff --git a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml index 0f09e3b..7b7c114 100644 --- a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -66,19 +65,16 @@ spec: description: Batch Strategy properties: batches: - description: Batches define the order of phases to execute release - in batch release + description: Batches define the order of phases to execute release in batch release items: properties: breakpoint: - description: If set to true, the rollout will be paused - before the step starts. + description: If set to true, the rollout will be paused before the step starts. type: boolean properties: additionalProperties: type: string - description: Properties contains additional information - for step + description: Properties contains additional information for step type: object targets: description: desired target replicas @@ -102,9 +98,7 @@ spec: anyOf: - type: integer - type: string - description: Replicas is the replicas of the rollout - task, which represents the number of pods to be - upgraded + description: Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded x-kubernetes-int-or-string: true required: - name @@ -116,560 +110,4045 @@ spec: properties: http: properties: - filter: - description: Filter defines a filter for the canary - service. + baseTraffic: + description: BaseTraffic indicate the base traffic rule properties: - requestHeaderModifier: + filters: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Filters define the filters that are applied to requests that match + this rule. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Input: - GET /foo HTTP/1.1 - my-header: foo + Conformance-levels at this level are defined based on the type of filter: - Config: - add: - - name: "my-header" - value: "bar,baz" + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an - HTTP Header name and value as defined - by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + maxLength: 63 minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string - value: - description: Value is the value of - HTTP Header to be matched. - maxLength: 4096 + name: + description: Name is the name of the referent. + maxLength: 253 minLength: 1 type: string required: + - group + - kind - name - - value type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - remove: ["my-header1", "my-header3"] + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Config: + add: + - name: "my-header" + value: "bar,baz" - Input: - GET /foo HTTP/1.1 - my-header: foo + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Config: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set set: - - name: "my-header" - value: "bar" + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an - HTTP Header name and value as defined - by RFC 7230. + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended properties: - name: + backendRef: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + BackendRef references a resource where mirrored requests are sent. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + Support: Core + maxLength: 253 minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string - value: - description: Value is the value of - HTTP Header to be matched. - maxLength: 4096 - minLength: 1 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https type: string - required: - - name - - value + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: object - matches: - description: Matches define conditions used for - matching the incoming HTTP requests to canary - service. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + weight: + description: Weight indicate how many percentage of traffic the canary pods should receive + format: int32 + maximum: 100 + minimum: 0 + type: integer + type: object + type: object + required: + - targets + type: object + type: array + toleration: + description: Toleration is the toleration policy of the canary strategy + properties: + initialDelaySeconds: + description: Number of seconds after the toleration check has started before the task are initiated. + format: int32 + type: integer + taskFailureThreshold: + anyOf: + - type: integer + - type: string + description: |- + FailureThreshold indicates how many failed pods can be tolerated before marking the rollout task as success + If not set, the default value is 0, which means no failed pods can be tolerated + This is a task level threshold. + x-kubernetes-int-or-string: true + workloadTotalFailureThreshold: + anyOf: + - type: integer + - type: string + description: |- + WorkloadFailureThreshold indicates how many failed pods can be tolerated in all upgraded pods of one workload. + The default value is 0, which means no failed pods can be tolerated. + This is a workload level threshold. + x-kubernetes-int-or-string: true + type: object + type: object + canary: + description: Canary defines the canary strategy + properties: + podTemplateMetadataPatch: + description: PodTemplateMetadataPatch defines a patch for workload podTemplate metadata. + properties: + annotations: + additionalProperties: + type: string + description: Annotations are additional metadata that can be included. + type: object + labels: + additionalProperties: + type: string + description: Labels are additional metadata that can be included. + type: object + type: object + properties: + additionalProperties: + type: string + description: Properties contains additional information for step + type: object + targets: + description: desired target replicas + items: + properties: + cluster: + description: Cluster indicates the name of cluster + type: string + name: + description: Name is the resource name + type: string + replicaSlidingWindow: + anyOf: + - type: integer + - type: string + description: |- + ReplicaSlidingWindow used to control the number of pods that are allowed to be upgraded in + a sliding window for progressive rollout smoothly. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded + x-kubernetes-int-or-string: true + required: + - name + - replicas + type: object + type: array + traffic: + description: traffic strategy + properties: + http: + properties: + baseTraffic: + description: BaseTraffic indicate the base traffic rule + properties: + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Support: Core (Exact) + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Support: Implementation-specific (RegularExpression) + Config: + remove: ["my-header1", "my-header3"] - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + Input: + GET /foo HTTP/1.1 + my-header: foo - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Config: + set: + - name: "my-header" + value: "bar" - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - Support: Extended (Exact) + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Support: Implementation-specific (RegularExpression) + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP - query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: array - type: object - weight: - description: Weight indicate how many percentage of - traffic the canary pods should receive - format: int32 - maximum: 100 - minimum: 0 - type: integer - type: object - required: - - targets - type: object - type: array - toleration: - description: Toleration is the toleration policy of the canary - strategy - properties: - initialDelaySeconds: - description: Number of seconds after the toleration check - has started before the task are initiated. - format: int32 - type: integer - taskFailureThreshold: - anyOf: - - type: integer - - type: string - description: |- - FailureThreshold indicates how many failed pods can be tolerated before marking the rollout task as success - If not set, the default value is 0, which means no failed pods can be tolerated - This is a task level threshold. - x-kubernetes-int-or-string: true - workloadTotalFailureThreshold: - anyOf: - - type: integer - - type: string - description: |- - WorkloadFailureThreshold indicates how many failed pods can be tolerated in all upgraded pods of one workload. - The default value is 0, which means no failed pods can be tolerated. - This is a workload level threshold. - x-kubernetes-int-or-string: true - type: object - type: object - canary: - description: Canary defines the canary strategy - properties: - podTemplateMetadataPatch: - description: PodTemplateMetadataPatch defines a patch for workload - podTemplate metadata. - properties: - annotations: - additionalProperties: - type: string - description: Annotations are additional metadata that can - be included. - type: object - labels: - additionalProperties: - type: string - description: Labels are additional metadata that can be included. - type: object - type: object - properties: - additionalProperties: - type: string - description: Properties contains additional information for step - type: object - targets: - description: desired target replicas - items: - properties: - cluster: - description: Cluster indicates the name of cluster - type: string - name: - description: Name is the resource name - type: string - replicaSlidingWindow: - anyOf: - - type: integer - - type: string - description: |- - ReplicaSlidingWindow used to control the number of pods that are allowed to be upgraded in - a sliding window for progressive rollout smoothly. - x-kubernetes-int-or-string: true - replicas: - anyOf: - - type: integer - - type: string - description: Replicas is the replicas of the rollout task, - which represents the number of pods to be upgraded - x-kubernetes-int-or-string: true - required: - - name - - replicas - type: object - type: array - traffic: - description: traffic strategy - properties: - http: - properties: - filter: - description: Filter defines a filter for the canary service. - properties: - requestHeaderModifier: - description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - Input: - GET /foo HTTP/1.1 - my-header: foo + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Config: - add: - - name: "my-header" - value: "bar,baz" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header - name and value as defined by RFC 7230. + Support: Extended properties: - name: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 - minLength: 1 + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch type: string required: - - name - - value + - type type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + For example, take the following matches configuration: - Config: - remove: ["my-header1", "my-header3"] + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - Input: - GET /foo HTTP/1.1 - my-header: foo + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - Config: - set: - - name: "my-header" - value: "bar" + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header - name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: object - matches: - description: Matches define conditions used for matching - the incoming HTTP requests to canary service. + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. items: properties: headers: @@ -725,8 +4204,7 @@ spec: - RegularExpression type: string value: - description: Value is the value of HTTP Header - to be matched. + description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string @@ -739,6 +4217,30 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match @@ -799,8 +4301,7 @@ spec: - RegularExpression type: string value: - description: Value is the value of HTTP query - param to be matched. + description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string @@ -814,15 +4315,15 @@ spec: - name x-kubernetes-list-type: map type: object + maxItems: 8 type: array + weight: + description: Weight indicate how many percentage of traffic the canary pods should receive + format: int32 + maximum: 100 + minimum: 0 + type: integer type: object - weight: - description: Weight indicate how many percentage of traffic - the canary pods should receive - format: int32 - maximum: 100 - minimum: 0 - type: integer type: object required: - targets @@ -958,8 +4459,7 @@ spec: description: BatchStatus describes the state of the active batch release properties: currentBatchIndex: - description: CurrentBatchIndex defines the current batch index - of batch release progress. + description: CurrentBatchIndex defines the current batch index of batch release progress. format: int32 type: integer currentBatchState: @@ -985,13 +4485,11 @@ spec: description: State is Rollout step state type: string targets: - description: WorkloadDetails contains release details for - each workload + description: WorkloadDetails contains release details for each workload items: properties: cluster: - description: Cluster defines which cluster the workload - is in. + description: Cluster defines which cluster the workload is in. type: string generation: description: Generation is the found in workload metadata. @@ -1001,40 +4499,30 @@ spec: description: Name is the workload name type: string observedGeneration: - description: ObservedGeneration is the most recent - generation observed for this workload. + description: ObservedGeneration is the most recent generation observed for this workload. format: int64 type: integer replicas: - description: Replicas is the desired number of pods - targeted by workload + description: Replicas is the desired number of pods targeted by workload format: int32 type: integer stableRevision: - description: StableRevision is the old stable revision - used to generate pods. + description: StableRevision is the old stable revision used to generate pods. type: string updatedAvailableReplicas: - description: UpdatedAvailableReplicas is the number - of service available pods targeted by workload that - have the updated template spec. + description: UpdatedAvailableReplicas is the number of service available pods targeted by workload that have the updated template spec. format: int32 type: integer updatedReadyReplicas: - description: UpdatedReadyReplicas is the number of - ready pods targeted by workload that have the updated - template spec. + description: UpdatedReadyReplicas is the number of ready pods targeted by workload that have the updated template spec. format: int32 type: integer updatedReplicas: - description: UpdatedReplicas is the number of pods - targeted by workload that have the updated template - spec. + description: UpdatedReplicas is the number of pods targeted by workload that have the updated template spec. format: int32 type: integer updatedRevision: - description: UpdatedRevision is the updated template - revision used to generate pods. + description: UpdatedRevision is the updated template revision used to generate pods. type: string required: - replicas @@ -1058,8 +4546,7 @@ spec: description: Webhook Type type: string message: - description: A human-readable message indicating details - about the transition. + description: A human-readable message indicating details about the transition. type: string name: description: Webhook Name @@ -1078,8 +4565,7 @@ spec: - currentBatchIndex type: object canaryStatus: - description: CanaryStatus describes the state of the active canary - release + description: CanaryStatus describes the state of the active canary release properties: finishTime: description: FinishTime is the time when the stage finished @@ -1097,13 +4583,11 @@ spec: description: State is Rollout step state type: string targets: - description: WorkloadDetails contains release details for each - workload + description: WorkloadDetails contains release details for each workload items: properties: cluster: - description: Cluster defines which cluster the workload - is in. + description: Cluster defines which cluster the workload is in. type: string generation: description: Generation is the found in workload metadata. @@ -1113,39 +4597,30 @@ spec: description: Name is the workload name type: string observedGeneration: - description: ObservedGeneration is the most recent generation - observed for this workload. + description: ObservedGeneration is the most recent generation observed for this workload. format: int64 type: integer replicas: - description: Replicas is the desired number of pods targeted - by workload + description: Replicas is the desired number of pods targeted by workload format: int32 type: integer stableRevision: - description: StableRevision is the old stable revision used - to generate pods. + description: StableRevision is the old stable revision used to generate pods. type: string updatedAvailableReplicas: - description: UpdatedAvailableReplicas is the number of service - available pods targeted by workload that have the updated - template spec. + description: UpdatedAvailableReplicas is the number of service available pods targeted by workload that have the updated template spec. format: int32 type: integer updatedReadyReplicas: - description: UpdatedReadyReplicas is the number of ready - pods targeted by workload that have the updated template - spec. + description: UpdatedReadyReplicas is the number of ready pods targeted by workload that have the updated template spec. format: int32 type: integer updatedReplicas: - description: UpdatedReplicas is the number of pods targeted - by workload that have the updated template spec. + description: UpdatedReplicas is the number of pods targeted by workload that have the updated template spec. format: int32 type: integer updatedRevision: - description: UpdatedRevision is the updated template revision - used to generate pods. + description: UpdatedRevision is the updated template revision used to generate pods. type: string required: - replicas @@ -1169,8 +4644,7 @@ spec: description: Webhook Type type: string message: - description: A human-readable message indicating details - about the transition. + description: A human-readable message indicating details about the transition. type: string name: description: Webhook Name @@ -1192,8 +4666,7 @@ spec: See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties properties: lastTransitionTime: - description: Last time the condition transitioned from one status - to another. + description: Last time the condition transitioned from one status to another. format: date-time type: string lastUpdateTime: @@ -1201,8 +4674,7 @@ spec: format: date-time type: string message: - description: A human-readable message indicating details about - the transition. + description: A human-readable message indicating details about the transition. type: string reason: description: The reason for the condition's last transition. @@ -1225,8 +4697,7 @@ spec: description: Code is a globally unique identifier type: string message: - description: A human-readable message indicating details about - the transition. + description: A human-readable message indicating details about the transition. type: string reason: description: A human-readable short word @@ -1260,38 +4731,30 @@ spec: description: Name is the workload name type: string observedGeneration: - description: ObservedGeneration is the most recent generation - observed for this workload. + description: ObservedGeneration is the most recent generation observed for this workload. format: int64 type: integer replicas: - description: Replicas is the desired number of pods targeted - by workload + description: Replicas is the desired number of pods targeted by workload format: int32 type: integer stableRevision: - description: StableRevision is the old stable revision used - to generate pods. + description: StableRevision is the old stable revision used to generate pods. type: string updatedAvailableReplicas: - description: UpdatedAvailableReplicas is the number of service - available pods targeted by workload that have the updated - template spec. + description: UpdatedAvailableReplicas is the number of service available pods targeted by workload that have the updated template spec. format: int32 type: integer updatedReadyReplicas: - description: UpdatedReadyReplicas is the number of ready pods - targeted by workload that have the updated template spec. + description: UpdatedReadyReplicas is the number of ready pods targeted by workload that have the updated template spec. format: int32 type: integer updatedReplicas: - description: UpdatedReplicas is the number of pods targeted - by workload that have the updated template spec. + description: UpdatedReplicas is the number of pods targeted by workload that have the updated template spec. format: int32 type: integer updatedRevision: - description: UpdatedRevision is the updated template revision - used to generate pods. + description: UpdatedRevision is the updated template revision used to generate pods. type: string required: - replicas diff --git a/config/crd/bases/rollout.kusionstack.io_rollouts.yaml b/config/crd/bases/rollout.kusionstack.io_rollouts.yaml index 4b0fb2f..353ce4f 100644 --- a/config/crd/bases/rollout.kusionstack.io_rollouts.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rollouts.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -96,14 +95,12 @@ spec: description: Kind is the type of resource being referenced type: string match: - description: Match indicates how to match workloads. only one - workload should be matches in one cluster + description: Match indicates how to match workloads. only one workload should be matches in one cluster properties: names: description: Names is a list of workload name items: - description: CrossClusterObjectNameReference contains cluster - and name reference to a k8s object + description: CrossClusterObjectNameReference contains cluster and name reference to a k8s object properties: cluster: description: Cluster indicates the name of cluster @@ -116,20 +113,17 @@ spec: type: object type: array selector: - description: Selector is a label query over a set of resources, - in this case resource + description: Selector is a label query over a set of resources, in this case resource properties: matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that the selector - applies to. + description: key is the label key that the selector applies to. type: string operator: description: |- @@ -177,8 +171,7 @@ spec: See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties properties: lastTransitionTime: - description: Last time the condition transitioned from one status - to another. + description: Last time the condition transitioned from one status to another. format: date-time type: string lastUpdateTime: @@ -186,8 +179,7 @@ spec: format: date-time type: string message: - description: A human-readable message indicating details about - the transition. + description: A human-readable message indicating details about the transition. type: string reason: description: The reason for the condition's last transition. diff --git a/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml b/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml index 45b6238..dfa14c7 100644 --- a/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -32,24 +31,20 @@ spec: description: Batch is the batch strategy for upgrade and operation properties: batches: - description: Batches define the order of phases to execute release - in canary release + description: Batches define the order of phases to execute release in canary release items: description: Custom release step properties: breakpoint: - description: If set to true, the rollout will be paused before - the step starts. + description: If set to true, the rollout will be paused before the step starts. type: boolean matchTargets: - description: Match defines condition used for matching resource - cross clusterset + description: Match defines condition used for matching resource cross clusterset properties: names: description: Names is a list of workload name items: - description: CrossClusterObjectNameReference contains - cluster and name reference to a k8s object + description: CrossClusterObjectNameReference contains cluster and name reference to a k8s object properties: cluster: description: Cluster indicates the name of cluster @@ -62,20 +57,17 @@ spec: type: object type: array selector: - description: Selector is a label query over a set of resources, - in this case resource + description: Selector is a label query over a set of resources, in this case resource properties: matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that the selector - applies to. + description: key is the label key that the selector applies to. type: string operator: description: |- @@ -110,8 +102,7 @@ spec: properties: additionalProperties: type: string - description: Properties contains additional information for - step + description: Properties contains additional information for step type: object replicaSlidingWindow: anyOf: @@ -125,608 +116,4090 @@ spec: anyOf: - type: integer - type: string - description: Replicas is the replicas of the rollout task, which - represents the number of pods to be upgraded + description: Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded x-kubernetes-int-or-string: true traffic: description: traffic strategy properties: http: properties: - filter: - description: Filter defines a filter for the canary - service. + baseTraffic: + description: BaseTraffic indicate the base traffic rule properties: - requestHeaderModifier: + filters: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Filters define the filters that are applied to requests that match + this rule. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Input: - GET /foo HTTP/1.1 - my-header: foo + Conformance-levels at this level are defined based on the type of filter: - Config: - add: - - name: "my-header" - value: "bar,baz" + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP - Header name and value as defined by RFC - 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + maxLength: 63 minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 + name: + description: Name is the name of the referent. + maxLength: 253 minLength: 1 type: string required: + - group + - kind - name - - value type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - remove: ["my-header1", "my-header3"] + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Config: + add: + - name: "my-header" + value: "bar,baz" - Input: - GET /foo HTTP/1.1 - my-header: foo + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Config: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set set: - - name: "my-header" - value: "bar" + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP - Header name and value as defined by RFC - 7230. + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended properties: - name: + backendRef: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + BackendRef references a resource where mirrored requests are sent. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + Support: Core + maxLength: 253 minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 - minLength: 1 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https type: string - required: - - name - - value + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: object - matches: - description: Matches define conditions used for matching - the incoming HTTP requests to canary service. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + Input: + GET /foo HTTP/1.1 + my-header: foo - Support: Core (Exact) + Config: + add: + - name: "my-header" + value: "bar,baz" - Support: Implementation-specific (RegularExpression) + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP - Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + weight: + description: Weight indicate how many percentage of traffic the canary pods should receive + format: int32 + maximum: 100 + minimum: 0 + type: integer + type: object + type: object + required: + - replicas + type: object + type: array + toleration: + description: Toleration is the toleration policy of the canary strategy + properties: + initialDelaySeconds: + description: Number of seconds after the toleration check has started before the task are initiated. + format: int32 + type: integer + taskFailureThreshold: + anyOf: + - type: integer + - type: string + description: |- + FailureThreshold indicates how many failed pods can be tolerated before marking the rollout task as success + If not set, the default value is 0, which means no failed pods can be tolerated + This is a task level threshold. + x-kubernetes-int-or-string: true + workloadTotalFailureThreshold: + anyOf: + - type: integer + - type: string + description: |- + WorkloadFailureThreshold indicates how many failed pods can be tolerated in all upgraded pods of one workload. + The default value is 0, which means no failed pods can be tolerated. + This is a workload level threshold. + x-kubernetes-int-or-string: true + type: object + type: object + canary: + description: Canary defines the canary strategy for upgrade and operation + properties: + matchTargets: + description: Match defines condition used for matching resource cross clusterset + properties: + names: + description: Names is a list of workload name + items: + description: CrossClusterObjectNameReference contains cluster and name reference to a k8s object + properties: + cluster: + description: Cluster indicates the name of cluster + type: string + name: + description: Name is the resource name + type: string + required: + - name + type: object + type: array + selector: + description: Selector is a label query over a set of resources, in this case resource + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + properties: + additionalProperties: + type: string + description: Properties contains additional information for step + type: object + replicas: + anyOf: + - type: integer + - type: string + description: Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded + x-kubernetes-int-or-string: true + templateMetadataPatch: + description: TemplateMetadataPatch defines a patch for workload template metadata. + properties: + annotations: + additionalProperties: + type: string + description: Annotations are additional metadata that can be included. + type: object + labels: + additionalProperties: + type: string + description: Labels are additional metadata that can be included. + type: object + type: object + traffic: + description: traffic strategy + properties: + http: + properties: + baseTraffic: + description: BaseTraffic indicate the base traffic rule + properties: + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: - name - - value + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: - name - x-kubernetes-list-type: map - queryParams: + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + + This filter can be used multiple times within the same rule. + + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - Support: Extended (Exact) + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Support: Implementation-specific (RegularExpression) + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP - query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: array - type: object - weight: - description: Weight indicate how many percentage of traffic - the canary pods should receive - format: int32 - maximum: 100 - minimum: 0 - type: integer - type: object - required: - - replicas - type: object - type: array - toleration: - description: Toleration is the toleration policy of the canary strategy - properties: - initialDelaySeconds: - description: Number of seconds after the toleration check has - started before the task are initiated. - format: int32 - type: integer - taskFailureThreshold: - anyOf: - - type: integer - - type: string - description: |- - FailureThreshold indicates how many failed pods can be tolerated before marking the rollout task as success - If not set, the default value is 0, which means no failed pods can be tolerated - This is a task level threshold. - x-kubernetes-int-or-string: true - workloadTotalFailureThreshold: - anyOf: - - type: integer - - type: string - description: |- - WorkloadFailureThreshold indicates how many failed pods can be tolerated in all upgraded pods of one workload. - The default value is 0, which means no failed pods can be tolerated. - This is a workload level threshold. - x-kubernetes-int-or-string: true - type: object - type: object - canary: - description: Canary defines the canary strategy for upgrade and operation - properties: - matchTargets: - description: Match defines condition used for matching resource cross - clusterset - properties: - names: - description: Names is a list of workload name - items: - description: CrossClusterObjectNameReference contains cluster - and name reference to a k8s object - properties: - cluster: - description: Cluster indicates the name of cluster - type: string - name: - description: Name is the resource name - type: string - required: - - name - type: object - type: array - selector: - description: Selector is a label query over a set of resources, - in this case resource - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef type: string - values: + urlRewrite: description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: object - podTemplateMetadataPatch: - description: PodTemplateMetadataPatch defines a patch for workload - podTemplate metadata. - properties: - annotations: - additionalProperties: - type: string - description: Annotations are additional metadata that can be included. - type: object - labels: - additionalProperties: - type: string - description: Labels are additional metadata that can be included. - type: object - type: object - properties: - additionalProperties: - type: string - description: Properties contains additional information for step - type: object - replicas: - anyOf: - - type: integer - - type: string - description: Replicas is the replicas of the rollout task, which represents - the number of pods to be upgraded - x-kubernetes-int-or-string: true - traffic: - description: traffic strategy - properties: - http: - properties: - filter: - description: Filter defines a filter for the canary service. - properties: - requestHeaderModifier: - description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. - - - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + URLRewrite defines a schema for a filter that modifies a request during forwarding. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. - Config: - add: - - name: "my-header" - value: "bar,baz" + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header - name and value as defined by RFC 7230. + Support: Extended properties: - name: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 type: string - value: - description: Value is the value of HTTP Header - to be matched. - maxLength: 4096 - minLength: 1 + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch type: string required: - - name - - value + - type type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + For example, take the following matches configuration: - Config: - remove: ["my-header1", "my-header3"] + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - Input: - GET /foo HTTP/1.1 - my-header: foo + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - Config: - set: - - name: "my-header" - value: "bar" + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header - name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header - to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: object - matches: - description: Matches define conditions used for matching the - incoming HTTP requests to canary service. + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. items: properties: headers: @@ -782,8 +4255,7 @@ spec: - RegularExpression type: string value: - description: Value is the value of HTTP Header - to be matched. + description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string @@ -796,6 +4268,30 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match @@ -856,8 +4352,7 @@ spec: - RegularExpression type: string value: - description: Value is the value of HTTP query - param to be matched. + description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string @@ -871,15 +4366,15 @@ spec: - name x-kubernetes-list-type: map type: object + maxItems: 8 type: array + weight: + description: Weight indicate how many percentage of traffic the canary pods should receive + format: int32 + maximum: 100 + minimum: 0 + type: integer type: object - weight: - description: Weight indicate how many percentage of traffic the - canary pods should receive - format: int32 - maximum: 100 - minimum: 0 - type: integer type: object required: - replicas diff --git a/config/crd/bases/rollout.kusionstack.io_traffictopologies.yaml b/config/crd/bases/rollout.kusionstack.io_traffictopologies.yaml index 4d90954..e37396f 100644 --- a/config/crd/bases/rollout.kusionstack.io_traffictopologies.yaml +++ b/config/crd/bases/rollout.kusionstack.io_traffictopologies.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -141,14 +140,12 @@ spec: description: Kind is the type of resource being referenced type: string match: - description: Match indicates how to match workloads. only one - workload should be matches in one cluster + description: Match indicates how to match workloads. only one workload should be matches in one cluster properties: names: description: Names is a list of workload name items: - description: CrossClusterObjectNameReference contains cluster - and name reference to a k8s object + description: CrossClusterObjectNameReference contains cluster and name reference to a k8s object properties: cluster: description: Cluster indicates the name of cluster @@ -161,20 +158,17 @@ spec: type: object type: array selector: - description: Selector is a label query over a set of resources, - in this case resource + description: Selector is a label query over a set of resources, in this case resource properties: matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: - description: key is the label key that the selector - applies to. + description: key is the label key that the selector applies to. type: string operator: description: |- @@ -225,8 +219,7 @@ spec: See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties properties: lastTransitionTime: - description: Last time the condition transitioned from one status - to another. + description: Last time the condition transitioned from one status to another. format: date-time type: string lastUpdateTime: @@ -234,8 +227,7 @@ spec: format: date-time type: string message: - description: A human-readable message indicating details about - the transition. + description: A human-readable message indicating details about the transition. type: string reason: description: The reason for the condition's last transition. diff --git a/config/kind/workload/bases/rollout.yaml b/config/kind/workload/bases/rollout.yaml index 9d5829d..f16c268 100644 --- a/config/kind/workload/bases/rollout.yaml +++ b/config/kind/workload/bases/rollout.yaml @@ -25,16 +25,17 @@ metadata: name: rollout-demo canary: replicas: 2 - podTemplateMetadataPatch: + templateMetadataPatch: labels: service.tag: "canary" traffic: http: - filter: - requestHeaderModifier: + filters: + - requestHeaderModifier: set: - name: x-mse-tag value: canary + type: RequestHeaderModifier matches: - headers: - type: Exact diff --git a/config/kind/workload/bases/traffic.yaml b/config/kind/workload/bases/traffic.yaml index 4f5e79b..69c0f0e 100644 --- a/config/kind/workload/bases/traffic.yaml +++ b/config/kind/workload/bases/traffic.yaml @@ -48,6 +48,7 @@ kind: Ingress metadata: name: rollout-demo1 spec: + ingressClassName: mse rules: - host: rollout-demo.example.com http: @@ -66,6 +67,7 @@ kind: Ingress metadata: name: rollout-demo2 spec: + ingressClassName: mse rules: - host: rollout-demo.example.com http: diff --git a/hack/make-rules/update-manifests.sh b/hack/make-rules/update-manifests.sh index 6d27c7c..f18d367 100644 --- a/hack/make-rules/update-manifests.sh +++ b/hack/make-rules/update-manifests.sh @@ -28,3 +28,10 @@ bash "${ROOT_DIR}/hack/make-rules/install-go-tools.sh" controller-gen webhook \ paths="./..." \ output:crd:artifacts:config=config/crd/bases + +for file in config/crd/bases/*.yaml; do + # Traversal delete x-kubernetes-validations fields in crd yaml + yq -i eval 'del(.. | ."x-kubernetes-validations"?)' "$file" + # delete array indent + yamlfmt -formatter indentless_arrays=true "$file" +done diff --git a/pkg/backend/service/backend.go b/pkg/backend/service/backend.go index af4aa2f..2749a20 100644 --- a/pkg/backend/service/backend.go +++ b/pkg/backend/service/backend.go @@ -44,7 +44,7 @@ func (s *serviceBackend) ForkCanary(canaryName string) client.Object { if canaryBackend.Spec.Selector == nil { canaryBackend.Spec.Selector = make(map[string]string) } - canaryBackend.Spec.Selector[rollout.LabelPodRevision] = rollout.LabelValuePodRevisionCanary + canaryBackend.Spec.Selector[rollout.LabelTrafficRevision] = rollout.LabelValueTrafficRevisionCanary return canaryBackend } @@ -57,6 +57,6 @@ func (s *serviceBackend) ForkStable(stableName string) client.Object { if stableBackend.Spec.Selector == nil { stableBackend.Spec.Selector = make(map[string]string) } - stableBackend.Spec.Selector[rollout.LabelPodRevision] = rollout.LabelValuePodRevisionBase + stableBackend.Spec.Selector[rollout.LabelTrafficRevision] = rollout.LabelValueTrafficRevisionBase return stableBackend } diff --git a/pkg/controllers/backendrouting/backendrouting_controller_test.go b/pkg/controllers/backendrouting/backendrouting_controller_test.go index ed8f19b..ff836a4 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller_test.go +++ b/pkg/controllers/backendrouting/backendrouting_controller_test.go @@ -243,7 +243,7 @@ var _ = Describe("backend-routing-controller", func() { }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) }) - It("Canary ready", func() { + It("Canary By Weight", func() { // add canary to backendrouting brTmp := &v1alpha1.BackendRouting{} err := fedClient.Get(ctx, types.NamespacedName{ @@ -255,24 +255,19 @@ var _ = Describe("backend-routing-controller", func() { brTmp.Spec.Forwarding.Canary = v1alpha1.CanaryBackendRule{ Name: "br-controller-ut-svc1-canary", TrafficStrategy: v1alpha1.TrafficStrategy{ - Weight: &canaryWeight, - HTTPRule: &v1alpha1.HTTPRouteRule{ - Matches: []v1alpha1.HTTPRouteMatch{ - { - Headers: []gatewayapiv1.HTTPHeaderMatch{ - { - Name: "env", - Value: "canary", - }, - }, - }, - }, - Filter: v1alpha1.HTTPRouteFilter{ - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "x-mse-tag", - Value: "canary", + HTTP: &v1alpha1.HTTPTrafficStrategy{ + Weight: &canaryWeight, + HTTPRouteRule: v1alpha1.HTTPRouteRule{ + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "x-mse-tag", + Value: "canary", + }, + }, }, }, }, @@ -294,7 +289,6 @@ var _ = Describe("backend-routing-controller", func() { } return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary"] == "true" && igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-weight"] == "50" && - igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] == "canary" && igsTmp.Annotations["mse.ingress.kubernetes.io/request-header-control-update"] == "" && igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-canary" }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) @@ -322,24 +316,88 @@ var _ = Describe("backend-routing-controller", func() { brTmp.Spec.Forwarding.Canary = v1alpha1.CanaryBackendRule{ Name: "br-controller-ut-svc1-canary", TrafficStrategy: v1alpha1.TrafficStrategy{ - Weight: &canaryWeight, - HTTPRule: &v1alpha1.HTTPRouteRule{ - Matches: []v1alpha1.HTTPRouteMatch{ - { - Headers: []gatewayapiv1.HTTPHeaderMatch{ - { - Name: "env", - Value: "canary", + HTTP: &v1alpha1.HTTPTrafficStrategy{ + Weight: &canaryWeight, + HTTPRouteRule: v1alpha1.HTTPRouteRule{ + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "x-mse-tag", + Value: "canary", + }, + }, }, }, }, }, - Filter: v1alpha1.HTTPRouteFilter{ - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "x-mse-tag", - Value: "canary", + }, + }, + } + err = fedClient.Update(ctx, brTmp) + Expect(err).ShouldNot(HaveOccurred()) + + Eventually(func() bool { + igsTmp := &networkingv1.Ingress{} + err = clusterClient1.Get(ctx, types.NamespacedName{ + Name: "br-controller-ut-igs1-canary", + Namespace: "default", + }, igsTmp) + if err != nil { + return false + } + return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-weight"] == "20" && + igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-canary" + }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) + + Eventually(func() bool { + brTmp = &v1alpha1.BackendRouting{} + err = fedClient.Get(ctx, types.NamespacedName{ + Name: br0.Name, + Namespace: br0.Namespace, + }, brTmp) + if err != nil { + return false + } + return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration + }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) + }) + + It("Canary By Header", func() { + // add canary to backendrouting + brTmp := &v1alpha1.BackendRouting{} + err := fedClient.Get(ctx, types.NamespacedName{ + Name: br0.Name, + Namespace: br0.Namespace, + }, brTmp) + Expect(err).ShouldNot(HaveOccurred()) + brTmp.Spec.Forwarding.Canary = v1alpha1.CanaryBackendRule{ + Name: "br-controller-ut-svc1-canary", + TrafficStrategy: v1alpha1.TrafficStrategy{ + HTTP: &v1alpha1.HTTPTrafficStrategy{ + HTTPRouteRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + { + Headers: []gatewayapiv1.HTTPHeaderMatch{ + { + Name: "env", + Value: "canary", + }, + }, + }, + }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "x-mse-tag", + Value: "canary", + }, + }, }, }, }, @@ -359,7 +417,9 @@ var _ = Describe("backend-routing-controller", func() { if err != nil { return false } - return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-weight"] == "20" && + return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary"] == "true" && + igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] == "canary" && + igsTmp.Annotations["mse.ingress.kubernetes.io/request-header-control-update"] == "" && igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-canary" }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) diff --git a/pkg/controllers/podcanarylabel/podcanarylabel.go b/pkg/controllers/podcanarylabel/podcanarylabel.go index 54fc504..d93747f 100644 --- a/pkg/controllers/podcanarylabel/podcanarylabel.go +++ b/pkg/controllers/podcanarylabel/podcanarylabel.go @@ -104,7 +104,7 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque // this workload is not controlled by rollout, we need to make sure pod revision label is not added updated, err := utils.UpdateOnConflict(ctx, r.Client, r.Client, pod, func() error { utils.MutateLabels(pod, func(labels map[string]string) { - delete(labels, rolloutapi.LabelPodRevision) + delete(labels, rolloutapi.LabelTrafficRevision) }) return nil }) @@ -125,7 +125,7 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque // patch pod label updated, err := utils.UpdateOnConflict(ctx, r.Client, r.Client, pod, func() error { utils.MutateLabels(pod, func(labels map[string]string) { - labels[rolloutapi.LabelPodRevision] = podRevision + labels[rolloutapi.LabelTrafficRevision] = podRevision }) return nil }) @@ -140,17 +140,17 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque func recognizePodRevision(pc workload.PodControl, reader client.Reader, workloadObj client.Object, pod *corev1.Pod) string { if workload.IsCanary(workloadObj) { // canary workload, always set pod revision to canary - return rolloutapi.LabelValuePodRevisionCanary + return rolloutapi.LabelValueTrafficRevisionCanary } if !workload.IsProgressing(workloadObj) { // workload is not progressing, set pod revision to base - return rolloutapi.LabelValuePodRevisionBase + return rolloutapi.LabelValueTrafficRevisionBase } // workload is progressing, set updated pod revision to canary if updated, _ := pc.IsUpdatedPod(reader, workloadObj, pod); updated { - return rolloutapi.LabelValuePodRevisionCanary + return rolloutapi.LabelValueTrafficRevisionCanary } - return rolloutapi.LabelValuePodRevisionBase + return rolloutapi.LabelValueTrafficRevisionBase } diff --git a/pkg/controllers/rollout/utils.go b/pkg/controllers/rollout/utils.go index 19aae2a..5f8a5b5 100644 --- a/pkg/controllers/rollout/utils.go +++ b/pkg/controllers/rollout/utils.go @@ -126,10 +126,10 @@ func constructRolloutRunCanary(strategy *rolloutv1alpha1.CanaryStrategy, workloa } step := &rolloutv1alpha1.RolloutRunCanaryStrategy{ - Targets: targets, - Traffic: strategy.Traffic, - Properties: strategy.Properties, - PodTemplateMetadataPatch: strategy.PodTemplateMetadataPatch, + Targets: targets, + Traffic: strategy.Traffic, + Properties: strategy.Properties, + TemplateMetadataPatch: strategy.TemplateMetadataPatch, } return step } diff --git a/pkg/controllers/rolloutrun/executor/canary.go b/pkg/controllers/rolloutrun/executor/canary.go index 77da8e2..1e4ce09 100644 --- a/pkg/controllers/rolloutrun/executor/canary.go +++ b/pkg/controllers/rolloutrun/executor/canary.go @@ -181,7 +181,7 @@ func (e *canaryExecutor) doCanary(ctx *ExecutorContext) (bool, time.Duration, er logger.Info("about to create canary resources and check") canaryWorkloads := make([]*workload.Info, 0) - patch := appendBuiltinPodTemplateMetadataPatch(rolloutRun.Spec.Canary.PodTemplateMetadataPatch) + patch := appendBuiltinPodTemplateMetadataPatch(rolloutRun.Spec.Canary.TemplateMetadataPatch) changed := false releaseControl := control.NewCanaryReleaseControl(ctx.Accessor, ctx.Client) @@ -241,7 +241,7 @@ func appendBuiltinPodTemplateMetadataPatch(patch *rolloutv1alpha1.MetadataPatch) } patch.Labels[rolloutapi.LabelCanary] = "true" - patch.Labels[rolloutapi.LabelPodRevision] = "canary" + patch.Labels[rolloutapi.LabelTrafficRevision] = "canary" return patch } diff --git a/pkg/route/ingress/route.go b/pkg/route/ingress/route.go index f27bda8..32cd3f1 100644 --- a/pkg/route/ingress/route.go +++ b/pkg/route/ingress/route.go @@ -19,6 +19,7 @@ import ( "strconv" "strings" + "github.com/samber/lo" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -59,36 +60,41 @@ func (i *ingressRoute) AddCanaryRoute(ctx context.Context, forwarding *v1alpha1. AnnoMseReqHeaderCtrlAdd: "", AnnoMseReqHeaderCtrlRemove: "", } - if strategy.Weight != nil { - annosCanaryNeedCheck[AnnoCanaryWeight] = strconv.Itoa(int(*strategy.Weight)) - } isMseIngress := igs.Spec.IngressClassName != nil && *igs.Spec.IngressClassName == MseIngressClass - if strategy.HTTPRule != nil { - if len(strategy.HTTPRule.Matches) > 0 { - if len(strategy.HTTPRule.Matches[0].Headers) > 0 { - annosCanaryNeedCheck[AnnoCanaryHeader] = string(strategy.HTTPRule.Matches[0].Headers[0].Name) - annosCanaryNeedCheck[AnnoCanaryHeaderValue] = strategy.HTTPRule.Matches[0].Headers[0].Value + if strategy.HTTP != nil { + if len(strategy.HTTP.Matches) > 0 { + if len(strategy.HTTP.Matches[0].Headers) > 0 { + annosCanaryNeedCheck[AnnoCanaryHeader] = string(strategy.HTTP.Matches[0].Headers[0].Name) + annosCanaryNeedCheck[AnnoCanaryHeaderValue] = strategy.HTTP.Matches[0].Headers[0].Value } - if isMseIngress && len(strategy.HTTPRule.Matches[0].QueryParams) > 0 { - annosCanaryNeedCheck[AnnoMseCanaryQuery] = string(strategy.HTTPRule.Matches[0].QueryParams[0].Name) - annosCanaryNeedCheck[AnnoMseCanaryQueryValue] = strategy.HTTPRule.Matches[0].QueryParams[0].Value + if isMseIngress && len(strategy.HTTP.Matches[0].QueryParams) > 0 { + annosCanaryNeedCheck[AnnoMseCanaryQuery] = string(strategy.HTTP.Matches[0].QueryParams[0].Name) + annosCanaryNeedCheck[AnnoMseCanaryQueryValue] = strategy.HTTP.Matches[0].QueryParams[0].Value } + } else if strategy.HTTP.Weight != nil { + annosCanaryNeedCheck[AnnoCanaryWeight] = strconv.Itoa(int(*strategy.HTTP.Weight)) } - if isMseIngress && strategy.HTTPRule.Filter.RequestHeaderModifier != nil { - annoSet := generateMultiHeadersAnno(strategy.HTTPRule.Filter.RequestHeaderModifier.Set) - if annoSet != "" { - annosCanaryNeedCheck[AnnoMseReqHeaderCtrlUpdate] = annoSet - } - annoAdd := generateMultiHeadersAnno(strategy.HTTPRule.Filter.RequestHeaderModifier.Add) - if annoAdd != "" { - annosCanaryNeedCheck[AnnoMseReqHeaderCtrlAdd] = annoAdd - } + if isMseIngress && len(strategy.HTTP.Filters) > 0 { + filter, ok := lo.Find(strategy.HTTP.Filters, func(item v1.HTTPRouteFilter) bool { + return item.RequestHeaderModifier != nil + }) + if ok { + annoSet := generateMultiHeadersAnno(filter.RequestHeaderModifier.Set) + if annoSet != "" { + annosCanaryNeedCheck[AnnoMseReqHeaderCtrlUpdate] = annoSet + } - if len(strategy.HTTPRule.Filter.RequestHeaderModifier.Remove) > 0 { - annosCanaryNeedCheck[AnnoMseReqHeaderCtrlRemove] = strings.Join(strategy.HTTPRule.Filter.RequestHeaderModifier.Remove, ",") + annoAdd := generateMultiHeadersAnno(filter.RequestHeaderModifier.Add) + if annoAdd != "" { + annosCanaryNeedCheck[AnnoMseReqHeaderCtrlAdd] = annoAdd + } + + if len(filter.RequestHeaderModifier.Remove) > 0 { + annosCanaryNeedCheck[AnnoMseReqHeaderCtrlRemove] = strings.Join(filter.RequestHeaderModifier.Remove, ",") + } } } } From ddb606f26b9e2ec09744b58b0bc6a4860c992788 Mon Sep 17 00:00:00 2001 From: zoumo Date: Tue, 15 Jul 2025 16:26:51 +0800 Subject: [PATCH 02/10] refactor: move rollout api to kusionstack/kube-api --- apis/rollout/v1alpha1/condition.go | 42 - apis/rollout/v1alpha1/condition/condition.go | 103 - apis/rollout/v1alpha1/doc.go | 23 - apis/rollout/v1alpha1/rollout_gateway_api.go | 243 --- apis/rollout/v1alpha1/rollout_types.go | 238 --- .../rollout/v1alpha1/rollout_webhook_types.go | 186 -- apis/rollout/v1alpha1/rolloutrun_types.go | 230 --- .../rollout/v1alpha1/rolloutstrategy_types.go | 134 -- apis/rollout/v1alpha1/shared_types.go | 118 -- apis/rollout/v1alpha1/traffic_route_types.go | 268 --- apis/rollout/v1alpha1/validation/rollout.go | 2 +- .../v1alpha1/validation/rollout_test.go | 2 +- .../rollout/v1alpha1/validation/rolloutrun.go | 2 +- .../v1alpha1/validation/rolloutrun_test.go | 2 +- .../v1alpha1/validation/rolloutstrategy.go | 5 +- .../validation/rolloutstrategy_test.go | 2 +- .../v1alpha1/validation/traffic_topology.go | 2 +- .../rollout/v1alpha1/validation/validation.go | 33 + .../rollout/v1alpha1/zz_generated.deepcopy.go | 1697 ----------------- .../rollout/v1alpha1/zz_generated.register.go | 74 - apis/rollout/well_known_annotations.go | 43 - apis/rollout/well_known_finalizers.go | 21 - apis/rollout/well_known_labels.go | 37 - cmd/rollout/app/options/controller.go | 2 +- cmd/rollout/import_known_versions.go | 2 +- go.mod | 88 +- go.sum | 137 +- pkg/backend/service/backend.go | 2 +- .../backendrouting_controller.go | 2 +- .../backendrouting_controller_suite_test.go | 2 +- .../backendrouting_controller_test.go | 2 +- .../podcanarylabel/podcanarylabel.go | 2 +- pkg/controllers/rollout/event_handler.go | 2 +- pkg/controllers/rollout/rollout_controller.go | 6 +- pkg/controllers/rollout/utils.go | 4 +- pkg/controllers/rollout/utils_test.go | 2 +- pkg/controllers/rolloutrun/control/control.go | 4 +- pkg/controllers/rolloutrun/executor/alias.go | 2 +- pkg/controllers/rolloutrun/executor/batch.go | 2 +- .../rolloutrun/executor/batch_test.go | 2 +- pkg/controllers/rolloutrun/executor/canary.go | 4 +- .../rolloutrun/executor/context.go | 2 +- .../rolloutrun/executor/context_test.go | 2 +- .../rolloutrun/executor/default.go | 4 +- .../rolloutrun/executor/default_test.go | 4 +- .../rolloutrun/executor/do_command.go | 4 +- .../rolloutrun/executor/do_hook.go | 2 +- .../rolloutrun/executor/do_hook_test.go | 2 +- .../rolloutrun/executor/step_lifecycle.go | 2 +- .../rolloutrun/rolloutrun_controller.go | 6 +- .../rolloutrun/traffic/traffic_manager.go | 2 +- pkg/controllers/rolloutrun/webhook/manager.go | 2 +- .../rolloutrun/webhook/probe/http/http.go | 2 +- .../webhook/probe/http/http_test.go | 2 +- .../rolloutrun/webhook/probe/probe.go | 2 +- pkg/controllers/rolloutrun/webhook/worker.go | 2 +- .../rolloutrun/webhook/worker_test.go | 2 +- pkg/controllers/traffictopology/adapter.go | 2 +- .../traffictopology_controller_suite_test.go | 2 +- .../traffictopology_controller_test.go | 2 +- pkg/controllers/traffictopology/types.go | 2 +- pkg/features/ontimestrategy/ontimestrategy.go | 2 +- pkg/route/ingress/route.go | 2 +- pkg/route/interface.go | 2 +- .../progressinginfos/progressing_info.go | 10 +- .../progressinginfos/progressing_info_test.go | 2 +- pkg/utils/slice.go | 2 +- pkg/utils/slice_test.go | 2 +- pkg/webhook/mutating/pod/pod_mutating.go | 2 +- .../validating/rollout/rollout_validating.go | 24 +- pkg/workload/collaset/release.go | 2 +- pkg/workload/info.go | 4 +- pkg/workload/interface.go | 2 +- pkg/workload/matcher.go | 2 +- pkg/workload/statefulset/release.go | 2 +- pkg/workload/util.go | 4 +- test/e2e/builder/rollout_builder.go | 2 +- test/e2e/builder/rolloutstrategy_builder.go | 2 +- test/e2e/collaset_test.go | 4 +- test/e2e/statefulset_test.go | 4 +- test/e2e/suite_test.go | 2 +- 81 files changed, 215 insertions(+), 3687 deletions(-) delete mode 100644 apis/rollout/v1alpha1/condition.go delete mode 100644 apis/rollout/v1alpha1/condition/condition.go delete mode 100644 apis/rollout/v1alpha1/doc.go delete mode 100644 apis/rollout/v1alpha1/rollout_gateway_api.go delete mode 100644 apis/rollout/v1alpha1/rollout_types.go delete mode 100644 apis/rollout/v1alpha1/rollout_webhook_types.go delete mode 100644 apis/rollout/v1alpha1/rolloutrun_types.go delete mode 100644 apis/rollout/v1alpha1/rolloutstrategy_types.go delete mode 100644 apis/rollout/v1alpha1/shared_types.go delete mode 100644 apis/rollout/v1alpha1/traffic_route_types.go create mode 100644 apis/rollout/v1alpha1/validation/validation.go delete mode 100644 apis/rollout/v1alpha1/zz_generated.deepcopy.go delete mode 100644 apis/rollout/v1alpha1/zz_generated.register.go delete mode 100644 apis/rollout/well_known_annotations.go delete mode 100644 apis/rollout/well_known_finalizers.go delete mode 100644 apis/rollout/well_known_labels.go diff --git a/apis/rollout/v1alpha1/condition.go b/apis/rollout/v1alpha1/condition.go deleted file mode 100644 index be987bd..0000000 --- a/apis/rollout/v1alpha1/condition.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2023 The KusionStack 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" -) - -// Condition defines the condition of a resource -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties -type Condition struct { - // Type of the condition. - Type ConditionType `json:"type"` - // Status of the condition, one of True, False, Unknown. - Status metav1.ConditionStatus `json:"status"` - // Last time the condition transitioned from one status to another. - // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` - // The last time this condition was updated. - // +optional - LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` - // The reason for the condition's last transition. - // +optional - Reason string `json:"reason,omitempty"` - // A human-readable message indicating details about the transition. - // +optional - Message string `json:"message,omitempty"` -} - -type ConditionType string diff --git a/apis/rollout/v1alpha1/condition/condition.go b/apis/rollout/v1alpha1/condition/condition.go deleted file mode 100644 index 99fb6cb..0000000 --- a/apis/rollout/v1alpha1/condition/condition.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2023 The KusionStack 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 condition - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" -) - -func GetCondition(conditions []rolloutv1alpha1.Condition, ctype rolloutv1alpha1.ConditionType) *rolloutv1alpha1.Condition { - for i := range conditions { - c := conditions[i] - if c.Type == ctype { - return &c - } - } - return nil -} - -func NewCondition(ctype rolloutv1alpha1.ConditionType, status metav1.ConditionStatus, reason, message string) *rolloutv1alpha1.Condition { - return &rolloutv1alpha1.Condition{ - Type: ctype, - Status: status, - LastTransitionTime: metav1.Now(), - LastUpdateTime: metav1.Now(), - Reason: reason, - Message: message, - } -} - -func SetCondition(conditions []rolloutv1alpha1.Condition, condition rolloutv1alpha1.Condition) []rolloutv1alpha1.Condition { - if len(condition.Type) == 0 { - // invalid input condition - return conditions - } - currentCondition := GetCondition(conditions, condition.Type) - if currentCondition != nil { - if conditionEquals(*currentCondition, condition) { - return conditions - } - if currentCondition.Status == condition.Status { - // inherite LastTransitionTime from current condition - condition.LastTransitionTime = currentCondition.LastTransitionTime - } - } - result := FilterOutConditions(conditions, condition.Type) - result = append(result, condition) - return result -} - -func conditionEquals(a, b rolloutv1alpha1.Condition) bool { - if a.Type == b.Type && - a.Status == b.Status && - a.Reason == b.Reason && - a.Message == b.Message { - return true - } - return false -} - -func FilterOutConditions(conditions []rolloutv1alpha1.Condition, ctype rolloutv1alpha1.ConditionType) []rolloutv1alpha1.Condition { - result := []rolloutv1alpha1.Condition{} - for i := range conditions { - c := conditions[i] - if c.Type == ctype { - continue - } - result = append(result, c) - } - return result -} - -func IsTerminationCompleted(conditions []rolloutv1alpha1.Condition) bool { - cond := GetCondition(conditions, rolloutv1alpha1.RolloutConditionTerminating) - if cond != nil && - cond.Status == metav1.ConditionTrue && - cond.Reason == rolloutv1alpha1.RolloutReasonTerminatingCompleted { - // finalize completed, remove finalizer - return true - } - return false -} - -func IsAvailable(conditions []rolloutv1alpha1.Condition) bool { - cond := GetCondition(conditions, rolloutv1alpha1.RolloutConditionAvailable) - if cond != nil && cond.Status == metav1.ConditionTrue { - return true - } - return false -} diff --git a/apis/rollout/v1alpha1/doc.go b/apis/rollout/v1alpha1/doc.go deleted file mode 100644 index cfe252e..0000000 --- a/apis/rollout/v1alpha1/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2023 The KusionStack 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 contains API Schema definitions for the rollout v1alpha1 API group -// -// +kubebuilder:object:generate=true -// +groupName=rollout.kusionstack.io -// +k8s:deepcopy-gen=package -// +k8s:defaulter-gen=TypeMeta -package v1alpha1 diff --git a/apis/rollout/v1alpha1/rollout_gateway_api.go b/apis/rollout/v1alpha1/rollout_gateway_api.go deleted file mode 100644 index 171ba8d..0000000 --- a/apis/rollout/v1alpha1/rollout_gateway_api.go +++ /dev/null @@ -1,243 +0,0 @@ -/** - * Copyright 2023 The KusionStack 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 ( - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" -) - -type HTTPRouteMatch struct { - // Path specifies a HTTP request path matcher. - // - // +optional - Path *gatewayapiv1.HTTPPathMatch `json:"path,omitempty"` - - // Headers specifies HTTP request header matchers. Multiple match values are - // ANDed together, meaning, a request must match all the specified headers - // to select the route. - // - // +listType=map - // +listMapKey=name - // +optional - // +kubebuilder:validation:MaxItems=16 - Headers []gatewayapiv1.HTTPHeaderMatch `json:"headers,omitempty"` - // QueryParams specifies HTTP query parameter matchers. Multiple match - // values are ANDed together, meaning, a request must match all the - // specified query parameters to select the route. - // - // Support: Extended - // - // +listType=map - // +listMapKey=name - // +optional - // +kubebuilder:validation:MaxItems=16 - QueryParams []gatewayapiv1.HTTPQueryParamMatch `json:"queryParams,omitempty"` -} - -type HTTPRouteRule struct { - // Matches define conditions used for matching the rule against incoming - // HTTP requests. Each match is independent, i.e. this rule will be matched - // if **any** one of the matches is satisfied. - // - // For example, take the following matches configuration: - // - // ``` - // matches: - // - path: - // value: "/foo" - // headers: - // - name: "version" - // value: "v2" - // - path: - // value: "/v2/foo" - // ``` - // - // For a request to match against this rule, a request must satisfy - // EITHER of the two conditions: - // - // - path prefixed with `/foo` AND contains the header `version: v2` - // - path prefix of `/v2/foo` - // - // See the documentation for HTTPRouteMatch on how to specify multiple - // match conditions that should be ANDed together. - // - // If no matches are specified, the default is a prefix - // path match on "/", which has the effect of matching every - // HTTP request. - // - // Proxy or Load Balancer routing configuration generated from HTTPRoutes - // MUST prioritize matches based on the following criteria, continuing on - // ties. Across all rules specified on applicable Routes, precedence must be - // given to the match having: - // - // * "Exact" path match. - // * "Prefix" path match with largest number of characters. - // * Method match. - // * Largest number of header matches. - // * Largest number of query param matches. - // - // Note: The precedence of RegularExpression path matches are implementation-specific. - // - // If ties still exist across multiple Routes, matching precedence MUST be - // determined in order of the following criteria, continuing on ties: - // - // * The oldest Route based on creation timestamp. - // * The Route appearing first in alphabetical order by - // "{namespace}/{name}". - // - // If ties still exist within an HTTPRoute, matching precedence MUST be granted - // to the FIRST matching rule (in list order) with a match meeting the above - // criteria. - // - // When no rules matching a request have been successfully attached to the - // parent a request is coming from, a HTTP 404 status code MUST be returned. - // - // +optional - // +kubebuilder:validation:MaxItems=8 - Matches []HTTPRouteMatch `json:"matches,omitempty"` - // Filters define the filters that are applied to requests that match - // this rule. - // - // The effects of ordering of multiple behaviors are currently unspecified. - // This can change in the future based on feedback during the alpha stage. - // - // Conformance-levels at this level are defined based on the type of filter: - // - // - ALL core filters MUST be supported by all implementations. - // - Implementers are encouraged to support extended filters. - // - Implementation-specific custom filters have no API guarantees across - // implementations. - // - // Specifying the same filter multiple times is not supported unless explicitly - // indicated in the filter. - // - // All filters are expected to be compatible with each other except for the - // URLRewrite and RequestRedirect filters, which may not be combined. If an - // implementation can not support other combinations of filters, they must clearly - // document that limitation. In cases where incompatible or unsupported - // filters are specified and cause the `Accepted` condition to be set to status - // `False`, implementations may use the `IncompatibleFilters` reason to specify - // this configuration error. - // - // Support: Core - // - // +optional - // +kubebuilder:validation:MaxItems=16 - // +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))" - // +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1" - // +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1" - // +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.filter(f, f.type == 'RequestRedirect').size() <= 1" - // +kubebuilder:validation:XValidation:message="URLRewrite filter cannot be repeated",rule="self.filter(f, f.type == 'URLRewrite').size() <= 1" - Filters []gatewayapiv1.HTTPRouteFilter `json:"filters,omitempty"` -} - -// type BaseHTTPRouteRule struct { -// // Matches define conditions used for matching the rule against incoming -// // HTTP requests. Each match is independent, i.e. this rule will be matched -// // if **any** one of the matches is satisfied. -// // -// // For example, take the following matches configuration: -// // -// // ``` -// // matches: -// // - path: -// // value: "/foo" -// // headers: -// // - name: "version" -// // value: "v2" -// // - path: -// // value: "/v2/foo" -// // ``` -// // -// // For a request to match against this rule, a request must satisfy -// // EITHER of the two conditions: -// // -// // - path prefixed with `/foo` AND contains the header `version: v2` -// // - path prefix of `/v2/foo` -// // -// // See the documentation for HTTPRouteMatch on how to specify multiple -// // match conditions that should be ANDed together. -// // -// // If no matches are specified, the default is a prefix -// // path match on "/", which has the effect of matching every -// // HTTP request. -// // -// // Proxy or Load Balancer routing configuration generated from HTTPRoutes -// // MUST prioritize matches based on the following criteria, continuing on -// // ties. Across all rules specified on applicable Routes, precedence must be -// // given to the match having: -// // -// // * "Exact" path match. -// // * "Prefix" path match with largest number of characters. -// // * Method match. -// // * Largest number of header matches. -// // * Largest number of query param matches. -// // -// // Note: The precedence of RegularExpression path matches are implementation-specific. -// // -// // If ties still exist across multiple Routes, matching precedence MUST be -// // determined in order of the following criteria, continuing on ties: -// // -// // * The oldest Route based on creation timestamp. -// // * The Route appearing first in alphabetical order by -// // "{namespace}/{name}". -// // -// // If ties still exist within an HTTPRoute, matching precedence MUST be granted -// // to the FIRST matching rule (in list order) with a match meeting the above -// // criteria. -// // -// // When no rules matching a request have been successfully attached to the -// // parent a request is coming from, a HTTP 404 status code MUST be returned. -// // -// // +optional -// // +kubebuilder:validation:MaxItems=8 -// Matches []HTTPRouteMatch `json:"matches,omitempty"` -// // Filters define the filters that are applied to requests that match -// // this rule. -// // -// // The effects of ordering of multiple behaviors are currently unspecified. -// // This can change in the future based on feedback during the alpha stage. -// // -// // Conformance-levels at this level are defined based on the type of filter: -// // -// // - ALL core filters MUST be supported by all implementations. -// // - Implementers are encouraged to support extended filters. -// // - Implementation-specific custom filters have no API guarantees across -// // implementations. -// // -// // Specifying the same filter multiple times is not supported unless explicitly -// // indicated in the filter. -// // -// // All filters are expected to be compatible with each other except for the -// // URLRewrite and RequestRedirect filters, which may not be combined. If an -// // implementation can not support other combinations of filters, they must clearly -// // document that limitation. In cases where incompatible or unsupported -// // filters are specified and cause the `Accepted` condition to be set to status -// // `False`, implementations may use the `IncompatibleFilters` reason to specify -// // this configuration error. -// // -// // Support: Core -// // -// // +optional -// // +kubebuilder:validation:MaxItems=16 -// // +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))" -// // +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1" -// // +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1" -// // +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.filter(f, f.type == 'RequestRedirect').size() <= 1" -// // +kubebuilder:validation:XValidation:message="URLRewrite filter cannot be repeated",rule="self.filter(f, f.type == 'URLRewrite').size() <= 1" -// Filters []gatewayapiv1.HTTPRouteFilter `json:"filters,omitempty"` -// } diff --git a/apis/rollout/v1alpha1/rollout_types.go b/apis/rollout/v1alpha1/rollout_types.go deleted file mode 100644 index 0763b7b..0000000 --- a/apis/rollout/v1alpha1/rollout_types.go +++ /dev/null @@ -1,238 +0,0 @@ -/** - * Copyright 2023 The KusionStack 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 -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:subresource:status -// +kubebuilder:resource:shortName=ro -// +kubebuilder:printcolumn:name="Available",type="string",JSONPath=".status.conditions[?(@.type=='Available')].status" -// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" -// +kubebuilder:printcolumn:name="ID",type="string",JSONPath=".status.rolloutID" -// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",format="date-time" - -// Rollout is the Schema for the rollouts API -type Rollout struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RolloutSpec `json:"spec,omitempty"` - Status RolloutStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true - -// RolloutList contains a list of Rollout -type RolloutList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Rollout `json:"items"` -} - -// RolloutSpec defines the desired state of Rollout -type RolloutSpec struct { - // Disabled means that rollout will not response for new event. - // Default value is false. - Disabled bool `json:"disabled,omitempty"` - - // HistoryLimit defines the maximum number of completed rolloutRun - // history records to keep. - // The HistoryLimit can start from 0 (no retained RolloutRun history). - // When not set or set to math.MaxInt32, the Rollout will keep all RolloutRun history records. - // - // +kubebuilder:default=10 - HistoryLimit *int32 `json:"historyLimit,omitempty"` - - // TriggerPolicy defines when rollout will be triggered - // - // +kubebuilder:default=Auto - TriggerPolicy RolloutTriggerPolicy `json:"triggerPolicy,omitempty"` - - // StrategyRef is the reference to the rollout strategy - // - // +kubebuilder:validation:Required - StrategyRef string `json:"strategyRef,omitempty"` - - // WorkloadRef is a reference to a kind of workloads - WorkloadRef WorkloadRef `json:"workloadRef,omitempty"` - - // TrafficTopologyRefs defines the networking traffic relationships between - // workloads, backend services, and routes. - TrafficTopologyRefs []string `json:"trafficTopologyRefs,omitempty"` -} - -type RolloutTriggerPolicy string - -const ( - // AutoTriggerPolicy specifies the rollout progress will be triggered when all related - // workloads are waiting for rolling update, it is the default policy. - AutoTriggerPolicy RolloutTriggerPolicy = "Auto" - - // ManualTriggerPolicy specifies the rollout will only triggered by manually. - ManualTriggerPolicy RolloutTriggerPolicy = "Manual" -) - -// WorkloadRef is a reference to a workload -type WorkloadRef struct { - // APIVersion is the group/version for the resource being referenced. - // If APIVersion is not specified, the specified Kind must be in the core API group. - // For any other third-party types, APIVersion is required. - // +optional - APIVersion string `json:"apiVersion"` - // Kind is the type of resource being referenced - // - // +kubebuilder:validation:Required - Kind string `json:"kind"` - // Match indicates how to match workloads. only one workload should be matches in one cluster - Match ResourceMatch `json:"match"` -} - -// RolloutStatus defines the observed state of Rollout -type RolloutStatus struct { - // ObservedGeneration is the most recent generation observed for this Rollout. It corresponds to the - // Rollout's generation, which is updated on mutation by the API Server. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // Conditions is the list of conditions - Conditions []Condition `json:"conditions,omitempty"` - // Phase indicates the current phase of rollout - Phase RolloutPhase `json:"phase,omitempty"` - // The last time this status was updated. - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` - // RolloutID is reference to rolloutRun name. - RolloutID string `json:"rolloutID,omitempty"` -} - -// RolloutPhase indicates the current rollout phase -type RolloutPhase string - -const ( - // RolloutPhaseInitialized indicates the rollout is ready and waiting for next trigger - RolloutPhaseInitialized RolloutPhase = "Initialized" - // RolloutPhaseTerminating indicates the rollout is disabled - RolloutPhaseDisabled RolloutPhase = "Disabled" - // RolloutPhaseProgressing indicates the rollout is progressing - RolloutPhaseProgressing RolloutPhase = "Progressing" - // RolloutPhaseTerminating indicates the rollout is terminating - RolloutPhaseTerminating RolloutPhase = "Terminating" -) - -const ( - // rollout condition types - - // Available means all the dependents of this Rollout are available. - RolloutConditionAvailable ConditionType = "Available" - // RolloutConditionProgressing means the rollout is progressing - RolloutConditionProgressing ConditionType = "Progressing" - // RolloutConditionCompleted means the rollout is Terminating - RolloutConditionTerminating ConditionType = "Terminating" - // RolloutConditionTrigger means the rollout is triggered. - RolloutConditionTrigger ConditionType = "Trigger" - - // rollout condition reasons - - // RolloutReasonTerminatingCompleted means the termination of rollout is Completed. - RolloutReasonTerminatingCompleted = "Completed" - // RolloutReasonProgressingRunning means the rollout is not triggered. - RolloutReasonProgressingUnTriggered = "UnTriggered" - // RolloutReasonProgressingRunning means the rollout is running. - RolloutReasonProgressingRunning = "Running" - // RolloutReasonProgressingCompleted means the rollout is completed. - RolloutReasonProgressingCompleted = "Completed" - // RolloutReasonProgressingCanceled means the rollout is completed. - RolloutReasonProgressingCanceled = "Canceled" - // RolloutReasonProgressingError means the rollout is completed. - RolloutReasonProgressingError = "Error" -) - -// RolloutBatchStatus defines the status of batch release. -type RolloutBatchStatus struct { - // CurrentBatchIndex defines the current batch index of batch release progress. - CurrentBatchIndex int32 `json:"currentBatchIndex"` - // CurrentBatchState indicates the current batch state. - CurrentBatchState RolloutStepState `json:"currentBatchState,omitempty"` -} - -type RolloutReplicasSummary struct { - // Replicas is the desired number of pods targeted by workload - Replicas int32 `json:"replicas"` - // UpdatedReplicas is the number of pods targeted by workload that have the updated template spec. - UpdatedReplicas int32 `json:"updatedReplicas"` - // UpdatedReadyReplicas is the number of ready pods targeted by workload that have the updated template spec. - UpdatedReadyReplicas int32 `json:"updatedReadyReplicas"` - // UpdatedAvailableReplicas is the number of service available pods targeted by workload that have the updated template spec. - UpdatedAvailableReplicas int32 `json:"updatedAvailableReplicas"` -} - -type RolloutWorkloadStatus struct { - // summary of replicas - RolloutReplicasSummary `json:",inline,omitempty"` - - // Name is the workload name - Name string `json:"name,omitempty"` - // Cluster defines which cluster the workload is in. - Cluster string `json:"cluster,omitempty"` - // Generation is the found in workload metadata. - Generation int64 `json:"generation,omitempty"` - // ObservedGeneration is the most recent generation observed for this workload. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // StableRevision is the old stable revision used to generate pods. - StableRevision string `json:"stableRevision,omitempty"` - // UpdatedRevision is the updated template revision used to generate pods. - UpdatedRevision string `json:"updatedRevision,omitempty"` -} - -type RolloutStepState string - -const ( - // RolloutStepNone indicates that the step is not started. - RolloutStepNone RolloutStepState = "" - - // RolloutStepPending indicates that the step is pending. - RolloutStepPending RolloutStepState = "Pending" - - // RolloutStepPreCanaryStepHook indicates that the step is in the pre-canary hook. - RolloutStepPreCanaryStepHook RolloutStepState = RolloutStepState(PreCanaryStepHook) - - // RolloutStepPreBatchStepHook indicates that the step is in the pre-batch hook. - RolloutStepPreBatchStepHook RolloutStepState = RolloutStepState(PreBatchStepHook) - - // RolloutStepRunning indicates that the step is running. - RolloutStepRunning RolloutStepState = "Running" - - // RolloutStepPostCanaryStepHook indicates that the step is in the post-canary hook. - RolloutStepPostCanaryStepHook RolloutStepState = RolloutStepState(PostCanaryStepHook) - - // RolloutStepPostBatchStepHook indicates that the step is in the post-batch hook. - RolloutStepPostBatchStepHook RolloutStepState = RolloutStepState(PostBatchStepHook) - - // RolloutStepSucceeded indicates that the step is completed. - RolloutStepSucceeded RolloutStepState = "Succeeded" - - // RolloutStepResourceRecycling indicates that the step is recycling resources. - // In Canary strategy, it occurs after the user confirms (Paused). - // In Batch strategy, it occurs before the PreBatchStepHook. - RolloutStepResourceRecycling RolloutStepState = "ResourceRecycling" -) diff --git a/apis/rollout/v1alpha1/rollout_webhook_types.go b/apis/rollout/v1alpha1/rollout_webhook_types.go deleted file mode 100644 index 04c3fc2..0000000 --- a/apis/rollout/v1alpha1/rollout_webhook_types.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2023 The KusionStack 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" -) - -type RolloutWebhook struct { - // Name is the identity of webhook - Name string `json:"name,omitempty"` - // HookTypes defines when to communicate with the hook, specifies the types of events - // that trigger the webhook. - // Required - HookTypes []HookType `json:"hookTypes,omitempty"` - // ClientConfig defines how to communicate with the hook. - // Required - ClientConfig WebhookClientConfig `json:"clientConfig,omitempty"` - // Minimum consecutive failures for the probe to be considered failed after having succeeded. - // Defaults to 3. Minimum value is 1. - // - // +optional - // +kubebuilder:default=3 - // +kubebuilder:validation:Minimum=1 - FailureThreshold int32 `json:"failureThreshold,omitempty" protobuf:"varint,6,opt,name=failureThreshold"` - // FailurePolicy defines how unrecognized errors from the admission endpoint are handled - - // allowed values are Ignore or Fail. Defaults to Ignore. - // +optional - FailurePolicy FailurePolicyType `json:"failurePolicy,omitempty"` - // Properties provide additional data for webhook. - // +optional - Properties map[string]string `json:"properties,omitempty"` - // By default, rollout communicates with the webhook through the structure RolloutWebhookReview. - // If provider is set, then the protocol of the interaction will be determined by the provider - // +optional - Provider *string `json:"provider,omitempty"` -} - -// FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled. -type FailurePolicyType string - -const ( - // Ignore means that an error calling the webhook is ignored. - Ignore FailurePolicyType = "Ignore" - // Fail means that an error calling the webhook causes the admission to fail. - Fail FailurePolicyType = "Fail" -) - -// WebhookClientConfig contains the information to make a TLS -// connection with the webhook -type WebhookClientConfig struct { - // `url` gives the location of the webhook, in standard URL form - // (`scheme://host:port/path`). Exactly one of `url` or `service` - // must be specified. - // - // The `host` should not refer to a service running in the cluster; use - // the `service` field instead. The host might be resolved via external - // DNS in some apiservers (e.g., `kube-apiserver` cannot resolve - // in-cluster DNS as that would be a layering violation). `host` may - // also be an IP address. - // - // Please note that using `localhost` or `127.0.0.1` as a `host` is - // risky unless you take great care to run this webhook on all hosts - // which run an apiserver which might need to make calls to this - // webhook. Such installs are likely to be non-portable, i.e., not easy - // to turn up in a new cluster. - // - // The scheme must be "https"; the URL must begin with "https://". - // - // A path is optional, and if present may be any string permissible in - // a URL. You may use the path to pass an arbitrary string to the - // webhook, for example, a cluster identifier. - // - // Attempting to use a user or basic auth e.g. "user:password@" is not - // allowed. Fragments ("#...") and query parameters ("?...") are not - // allowed, either. - URL string `json:"url,omitempty" protobuf:"bytes,3,opt,name=url"` - - // `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. - // If unspecified, system trust roots' CA on the node. - // +optional - CABundle []byte `json:"caBundle,omitempty" protobuf:"bytes,2,opt,name=caBundle"` - - // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, - // the webhook call will be ignored or the API call will fail based on the - // failure policy. - // - // +optional - // +kubebuilder:default=10 - TimeoutSeconds int32 `json:"timeoutSeconds,omitempty"` - - // How often (in seconds) to perform the probe. - // Default to 10 seconds. Minimum value is 1. - // - // +optional - // +kubebuilder:default=10 - // +kubebuilder:validation:Minimum=1 - PeriodSeconds int32 `json:"periodSeconds,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:skipversion - -type RolloutWebhookReview struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RolloutWebhookReviewSpec `json:"spec,omitempty"` - Status RolloutWebhookReviewStatus `json:"status,omitempty"` -} - -type RolloutWebhookReviewSpec struct { - // Kind - Kind string `json:"kind,omitempty"` - - // Rollout Name - RolloutName string `json:"rolloutName,omitempty"` - - // Rollout ID - RolloutID string `json:"rolloutID,omitempty"` - - // HookType specifies the type of webhook - HookType HookType `json:"hookType,omitempty"` - - // TargetType defines the type of the target object - TargetType ObjectTypeRef `json:"targetType,omitempty"` - - // Properties stores custom parameters from the webhook to be passed to the server side - Properties map[string]string `json:"properties,omitempty"` - - // Canary defines the canary step webhook review spec - // +optional - Canary *RolloutWebhookReviewCanary `json:"canary,omitempty"` - - // Batch defines the batch step webhook review spec - // +optional - Batch *RolloutWebhookReviewBatch `json:"batch,omitempty"` -} - -type RolloutWebhookReviewCanary struct { - // Targets contains the list of rollout run step targets - Targets []RolloutRunStepTarget `json:"targets,omitempty"` - // Properties stores custom parameters from the webhook to be passed to the server side - Properties map[string]string `json:"properties,omitempty"` -} - -type RolloutWebhookReviewBatch struct { - // BatchIndex is the index of the executing batch - BatchIndex int32 `json:"batchIndex,omitempty"` - // Targets contains the list of rollout run step targets - Targets []RolloutRunStepTarget `json:"targets,omitempty"` - // Properties stores custom parameters from the webhook to be passed to the server side - Properties map[string]string `json:"properties,omitempty"` -} - -// Webhook type -type HookType string - -const ( - PreCanaryStepHook HookType = "PreCanaryStepHook" - PostCanaryStepHook HookType = "PostCanaryStepHook" - PreBatchStepHook HookType = "PreBatchStepHook" - PostBatchStepHook HookType = "PostBatchStepHook" -) - -type RolloutWebhookReviewStatus struct { - CodeReasonMessage `json:",inline"` -} - -const ( - WebhookReviewCodeOK string = "OK" - WebhookReviewCodeError string = "Error" - WebhookReviewCodeProcessing string = "Processing" -) diff --git a/apis/rollout/v1alpha1/rolloutrun_types.go b/apis/rollout/v1alpha1/rolloutrun_types.go deleted file mode 100644 index b9cf496..0000000 --- a/apis/rollout/v1alpha1/rolloutrun_types.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2023 The KusionStack 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" - "k8s.io/apimachinery/pkg/util/intstr" -) - -// +genclient -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:subresource:status -// +kubebuilder:resource:shortName=ror -// +kubebuilder:printcolumn:name="OWNER",type="string",JSONPath=".metadata.ownerReferences[0].name" -// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase" -// +kubebuilder:printcolumn:name="Canary State",type="string",JSONPath=".status.canaryStatus.state" -// +kubebuilder:printcolumn:name="Batch Index",type="string",JSONPath=".status.batchStatus.currentBatchIndex" -// +kubebuilder:printcolumn:name="Batch State",type="string",JSONPath=".status.batchStatus.currentBatchState" -// +kubebuilder:printcolumn:name="Error",type="string",JSONPath=".status.error.code" -// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",format="date-time" - -type RolloutRun struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec RolloutRunSpec `json:"spec,omitempty"` - Status RolloutRunStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true - -// RolloutList contains a list of Rollout -type RolloutRunList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []RolloutRun `json:"items"` -} - -type RolloutRunSpec struct { - // TargetType defines the GroupVersionKind of target resource - TargetType ObjectTypeRef `json:"targetType,omitempty"` - - // Webhooks defines rollout webhook configuration - Webhooks []RolloutWebhook `json:"webhooks,omitempty"` - - // TrafficTopologyRefs defines the networking traffic relationships between - // workloads, backend services, and routes. - TrafficTopologyRefs []string `json:"trafficTopologyRefs,omitempty"` - - // Canary defines the canary strategy - // +optional - Canary *RolloutRunCanaryStrategy `json:"canary,omitempty"` - - // Batch Strategy - // +optional - Batch *RolloutRunBatchStrategy `json:"batch,omitempty"` -} - -type RolloutRunBatchStrategy struct { - // Batches define the order of phases to execute release in batch release - Batches []RolloutRunStep `json:"batches,omitempty"` - - // Toleration is the toleration policy of the canary strategy - // +optional - Toleration *TolerationStrategy `json:"toleration,omitempty"` -} - -type RolloutRunStep struct { - // desired target replicas - Targets []RolloutRunStepTarget `json:"targets"` - - // traffic strategy - // +optional - Traffic *TrafficStrategy `json:"traffic,omitempty"` - - // If set to true, the rollout will be paused before the step starts. - // +optional - Breakpoint bool `json:"breakpoint,omitempty"` - - // Properties contains additional information for step - // +optional - Properties map[string]string `json:"properties,omitempty"` -} - -type RolloutRunCanaryStrategy struct { - // desired target replicas - Targets []RolloutRunStepTarget `json:"targets"` - - // traffic strategy - // +optional - Traffic *TrafficStrategy `json:"traffic,omitempty"` - - // Properties contains additional information for step - // +optional - Properties map[string]string `json:"properties,omitempty"` - - // PodTemplateMetadataPatch defines a patch for workload podTemplate metadata. - // +optional - TemplateMetadataPatch *MetadataPatch `json:"podTemplateMetadataPatch,omitempty"` -} - -type RolloutRunStepTarget struct { - CrossClusterObjectNameReference `json:",inline"` - - // Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded - Replicas intstr.IntOrString `json:"replicas"` - - // ReplicaSlidingWindow used to control the number of pods that are allowed to be upgraded in - // a sliding window for progressive rollout smoothly. - // +optional - ReplicaSlidingWindow *intstr.IntOrString `json:"replicaSlidingWindow,omitempty"` -} - -type RolloutRunStatus struct { - // ObservedGeneration is the most recent generation observed for this Rollout. It corresponds to the - // Rollout's generation, which is updated on mutation by the API Server. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // Conditions is the list of conditions - Conditions []Condition `json:"conditions,omitempty"` - // Phase indecates the current phase of rollout - Phase RolloutRunPhase `json:"phase,omitempty"` - // The last time this status was updated. - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` - // Error indicates the error info of progressing - Error *CodeReasonMessage `json:"error,omitempty"` - // CanaryStatus describes the state of the active canary release - // +optional - CanaryStatus *RolloutRunStepStatus `json:"canaryStatus,omitempty"` - // BatchStatus describes the state of the active batch release - // +optional - BatchStatus *RolloutRunBatchStatus `json:"batchStatus,omitempty"` - // TargetStatuses describes the referenced workloads status - // +optional - TargetStatuses []RolloutWorkloadStatus `json:"targetStatuses,omitempty"` -} - -type RolloutRunBatchStatus struct { - // RolloutBatchStatus contains status of current batch - RolloutBatchStatus `json:",inline"` - // Records contains all batches status details. - Records []RolloutRunStepStatus `json:"records,omitempty"` -} - -type RolloutRunPhase string - -const ( - // RolloutRunPhaseInitial defines the initial phase of rolloutRun - RolloutRunPhaseInitial RolloutRunPhase = "Initial" - // RolloutRunPhasePreRollout defines the phase of rolloutRun before rollout - RolloutRunPhasePreRollout RolloutRunPhase = "PreRollout" - // RolloutRunPhasePausing defines the phase of rolloutRun pausing - RolloutRunPhasePausing RolloutRunPhase = "Pausing" - // RolloutRunPhasePaused defines the phase of rolloutRun paused - RolloutRunPhasePaused RolloutRunPhase = "Paused" - // RolloutRunPhaseProgressing defines the phase of rolloutRun progressing - RolloutRunPhaseProgressing RolloutRunPhase = "Progressing" - // RolloutRunPhasePostRollout defines the phase of rollout after progressing - RolloutRunPhasePostRollout RolloutRunPhase = "PostRollout" - // RolloutRunPhaseCanceling defines the phase of rolloutRun canceling - RolloutRunPhaseCanceling RolloutRunPhase = "Canceling" - // RolloutRunPhaseCanceled defines the phase of rolloutRun canceled - RolloutRunPhaseCanceled RolloutRunPhase = "Canceled" - // RolloutRunPhaseFailed defines the phase of rolloutRun succeeded - RolloutRunPhaseSucceeded RolloutRunPhase = "Succeeded" -) - -type RolloutRunStepStatus struct { - // Index is the id of the batch - Index *int32 `json:"index,omitempty"` - // State is Rollout step state - State RolloutStepState `json:"state,omitempty"` - // StartTime is the time when the stage started - // +optional - StartTime *metav1.Time `json:"startTime,omitempty"` - // FinishTime is the time when the stage finished - // +optional - FinishTime *metav1.Time `json:"finishTime,omitempty"` - // WorkloadDetails contains release details for each workload - // +optional - Targets []RolloutWorkloadStatus `json:"targets,omitempty"` - // Webhooks contains webhook status - // +optional - Webhooks []RolloutWebhookStatus `json:"webhooks,omitempty"` -} - -type RolloutWebhookStatus struct { - // Current webhook worker state - State RolloutWebhookState `json:"state,omitempty"` - // Webhook Type - HookType HookType `json:"hookType,omitempty"` - // Webhook Name - Name string `json:"name,omitempty"` - // Webhook result - CodeReasonMessage `json:",inline"` - // Failure count - FailureCount int32 `json:"failureCount,omitempty"` -} - -// RolloutWebhookState indicates current state of webhook webhook. -type RolloutWebhookState string - -const ( - WebhookRunning RolloutWebhookState = "Running" - WebhookOnHold RolloutWebhookState = "OnHold" - WebhookCompleted RolloutWebhookState = "Completed" -) - -func (r *RolloutRun) IsCompleted() bool { - if r == nil { - return false - } - return r.Status.Phase == RolloutRunPhaseSucceeded || r.Status.Phase == RolloutRunPhaseCanceled -} diff --git a/apis/rollout/v1alpha1/rolloutstrategy_types.go b/apis/rollout/v1alpha1/rolloutstrategy_types.go deleted file mode 100644 index 350ed5e..0000000 --- a/apis/rollout/v1alpha1/rolloutstrategy_types.go +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright 2023 The KusionStack 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" - "k8s.io/apimachinery/pkg/util/intstr" -) - -// +genclient -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:resource:shortName=ros - -// RolloutStrategy is the Schema for the rolloutstrategies API -type RolloutStrategy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Canary defines the canary strategy for upgrade and operation - // +optional - Canary *CanaryStrategy `json:"canary,omitempty"` - - // Batch is the batch strategy for upgrade and operation - // +optional - Batch *BatchStrategy `json:"batch,omitempty"` - - // Webhooks defines - // +optional - Webhooks []RolloutWebhook `json:"webhooks,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true - -// RolloutStrategyList contains a list of RolloutStrategy -type RolloutStrategyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []RolloutStrategy `json:"items"` -} - -// BatchStrategy defines the batch strategy -type BatchStrategy struct { - // Batches define the order of phases to execute release in canary release - Batches []RolloutStep `json:"batches,omitempty"` - - // Toleration is the toleration policy of the canary strategy - // +optional - Toleration *TolerationStrategy `json:"toleration,omitempty"` -} - -// TolerationStrategy defines the toleration strategy -type TolerationStrategy struct { - // WorkloadFailureThreshold indicates how many failed pods can be tolerated in all upgraded pods of one workload. - // The default value is 0, which means no failed pods can be tolerated. - // This is a workload level threshold. - // +optional - WorkloadFailureThreshold *intstr.IntOrString `json:"workloadTotalFailureThreshold,omitempty"` - - // FailureThreshold indicates how many failed pods can be tolerated before marking the rollout task as success - // If not set, the default value is 0, which means no failed pods can be tolerated - // This is a task level threshold. - // +optional - TaskFailureThreshold *intstr.IntOrString `json:"taskFailureThreshold,omitempty"` - - // Number of seconds after the toleration check has started before the task are initiated. - InitialDelaySeconds int32 `json:"initialDelaySeconds,omitempty"` -} - -// Custom release step -type RolloutStep struct { - // Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded - Replicas intstr.IntOrString `json:"replicas"` - - // ReplicaSlidingWindow used to control the number of pods that are allowed to be upgraded in - // a sliding window for progressive rollout smoothly. - // +optional - ReplicaSlidingWindow *intstr.IntOrString `json:"replicaSlidingWindow,omitempty"` - - // traffic strategy - // +optional - Traffic *TrafficStrategy `json:"traffic,omitempty"` - - // Match defines condition used for matching resource cross clusterset - // +optional - Match *ResourceMatch `json:"matchTargets,omitempty"` - - // If set to true, the rollout will be paused before the step starts. - // +optional - Breakpoint bool `json:"breakpoint,omitempty"` - - // Properties contains additional information for step - // +optional - Properties map[string]string `json:"properties,omitempty"` -} - -type CanaryStrategy struct { - // Replicas is the replicas of the rollout task, which represents the number of pods to be upgraded - Replicas intstr.IntOrString `json:"replicas"` - - // traffic strategy - // +optional - Traffic *TrafficStrategy `json:"traffic,omitempty"` - - // Match defines condition used for matching resource cross clusterset - // +optional - Match *ResourceMatch `json:"matchTargets,omitempty"` - - // Properties contains additional information for step - // +optional - Properties map[string]string `json:"properties,omitempty"` - - // TemplateMetadataPatch defines a patch for workload template metadata. - // +optional - TemplateMetadataPatch *MetadataPatch `json:"templateMetadataPatch,omitempty"` -} diff --git a/apis/rollout/v1alpha1/shared_types.go b/apis/rollout/v1alpha1/shared_types.go deleted file mode 100644 index dacc991..0000000 --- a/apis/rollout/v1alpha1/shared_types.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2023 The KusionStack 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 ( - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type ResourceMatch struct { - // Selector is a label query over a set of resources, in this case resource - Selector *metav1.LabelSelector `json:"selector,omitempty"` - // Names is a list of workload name - Names []CrossClusterObjectNameReference `json:"names,omitempty"` -} - -// CrossClusterObjectReference is a reference to a kubernetes object in a different cluster. -type CrossClusterObjectReference struct { - ObjectTypeRef `json:",inline"` - CrossClusterObjectNameReference `json:",inline"` -} - -type ObjectTypeRef struct { - // APIVersion is the group/version for the resource being referenced. - // If APIVersion is not specified, the specified Kind must be in the core API group. - // For any other third-party types, APIVersion is required. - // +optional - APIVersion string `json:"apiVersion,omitempty"` - // Kind is the type of resource being referenced - Kind string `json:"kind"` -} - -const ( - MatchAllCluster = "" -) - -// CrossClusterObjectNameReference contains cluster and name reference to a k8s object -type CrossClusterObjectNameReference struct { - // Cluster indicates the name of cluster - Cluster string `json:"cluster,omitempty"` - // Name is the resource name - Name string `json:"name"` -} - -func (r CrossClusterObjectNameReference) Matches(cluster, name string) bool { - if r.Name != name { - // object name is not matched - return false - } - if r.Cluster == MatchAllCluster || cluster == MatchAllCluster { - // match all clusters - return true - } - return r.Cluster == cluster -} - -func (r CrossClusterObjectNameReference) String() string { - if len(r.Cluster) == 0 { - return fmt.Sprintf("name=%s", r.Name) - } - - return fmt.Sprintf("cluster=%s,name=%s", r.Cluster, r.Name) -} - -type CodeReasonMessage struct { - // Code is a globally unique identifier - Code string `json:"code,omitempty"` - // A human-readable short word - // +optional - Reason string `json:"reason,omitempty"` - // A human-readable message indicating details about the transition. - // +optional - Message string `json:"message,omitempty"` -} - -// Error implements error. -func (c *CodeReasonMessage) Error() string { - return fmt.Sprintf("err: code=%q, reason=%q, message=%q", c.Code, c.Reason, c.Message) -} - -// MetadataPatch is a patch for metadata -type MetadataPatch struct { - // Annotations are additional metadata that can be included. - // +optional - Annotations map[string]string `json:"annotations,omitempty"` - - // Labels are additional metadata that can be included. - // +optional - Labels map[string]string `json:"labels,omitempty"` -} - -// ProgressingInfo is the rollout progressing info -type ProgressingInfo struct { - Kind string `json:"kind,omitempty"` - RolloutName string `json:"rollout,omitempty"` - RolloutID string `json:"rolloutID,omitempty"` - Canary *CanaryProgressingInfo `json:"canary,omitempty"` - Batch *BatchProgressingInfo `json:"batch,omitempty"` -} - -type CanaryProgressingInfo struct{} - -type BatchProgressingInfo struct { - CurrentBatchIndex int32 `json:"currentBatchIndex"` -} diff --git a/apis/rollout/v1alpha1/traffic_route_types.go b/apis/rollout/v1alpha1/traffic_route_types.go deleted file mode 100644 index 419d9f2..0000000 --- a/apis/rollout/v1alpha1/traffic_route_types.go +++ /dev/null @@ -1,268 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:subresource:status -// +kubebuilder:resource:shortName=ttopo -// +kubebuilder:printcolumn:name="TYPE",type="string",JSONPath=".spec.trafficType" -// +kubebuilder:printcolumn:name="SERVICE",type="string",JSONPath=".spec.backend.name" -// +kubebuilder:printcolumn:name="Routes",type="string",JSONPath=".spec.routes[*].name" -// +kubebuilder:printcolumn:name="BACKEND_ROUTINGS",type="string",JSONPath=".status.topologies[*].backendRoutingName" -// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",format="date-time" - -// TrafficTopologies defines the networking traffic relationships between -// workloads, backend services, and routes. -type TrafficTopology struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec TrafficTopologySpec `json:"spec,omitempty"` - Status TrafficTopologyStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true - -// TrafficTopologyList is a list of TrafficTopology resources. -type TrafficTopologyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []TrafficTopology `json:"items"` -} - -// TrafficTopologySpec is the spec for a TrafficTopology resource. -type TrafficTopologySpec struct { - // WorkloadRef is the reference to a kind of workloads - WorkloadRef WorkloadRef `json:"workloadRef"` - - // TrafficType defines the type of traffic - TrafficType TrafficType `json:"trafficType"` - - // Backend defines the reference to a kind of backend - Backend BackendRef `json:"backend"` - - // Routes defines the list of routes - Routes []RouteRef `json:"routes,omitempty"` -} - -type TrafficType string - -const ( - MultiClusterTrafficType TrafficType = "MultiCluster" - InClusterTrafficType TrafficType = "InCluster" - - TrafficTopologyConditionReady ConditionType = "Ready" -) - -type BackendRef struct { - // Group is the group of the referent. For example, "gateway.networking.k8s.io". - // When unspecified or empty string, core API group is inferred. - // - // +optional - // +kubebuilder:default="v1" - APIVersion *string `json:"apiVersion,omitempty"` - - // Kind is the Kubernetes resource kind of the referent. For example - // "Service". - // - // Defaults to "Service" when not specified. - // - // ExternalName services can refer to CNAME DNS records that may live - // outside of the cluster and as such are difficult to reason about in - // terms of conformance. They also may not be safe to forward to (see - // CVE-2021-25740 for more information). Implementations SHOULD NOT - // support ExternalName Services. - // - // Support: Core (Services with a type other than ExternalName) - // - // Support: Implementation-specific (Services with type ExternalName) - // - // +optional - // +kubebuilder:default=Service - Kind *string `json:"kind,omitempty"` - - // Name is the name of the referent. - Name string `json:"name"` -} - -type RouteRef struct { - // APIVersion is the group/version of the referent. For example, "gateway.networking.k8s.io/v1". - // - // Defaults to "gateway.networking.k8s.io/v1" when not specified. - // - // +optional - // +kubebuilder:default="gateway.networking.k8s.io/v1" - APIVersion *string `json:"apiVersion,omitempty"` - // Kind is the Kubernetes resource kind of the referent. For example - // "HTTPRoute". - // - // Defaults to "HTTPRoute" when not specified. - // - // +optional - // +kubebuilder:default=HTTPRoute - Kind *string `json:"kind,omitempty"` - // Name is the name of the custom route. - Name string `json:"name"` -} - -type TrafficTopologyStatus struct { - // ObservedGeneration is the most recent generation observed. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // Conditions is the list of conditions - Conditions []Condition `json:"conditions,omitempty"` - // Topologies information aggregated by workload - Topologies []TopologyInfo `json:"topologies,omitempty"` -} - -type TopologyInfo struct { - // workload reference name and cluster - WorkloadRef CrossClusterObjectNameReference `json:"workloadRef,omitempty"` - // backend routing reference - // The name of the backendRouting referent - BackendRoutingName string `json:"backendRoutingName,omitempty"` -} - -// +genclient -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:subresource:status -// +kubebuilder:resource:shortName=bkr -// +kubebuilder:printcolumn:name="TYPE",type="string",JSONPath=".spec.trafficType" -// +kubebuilder:printcolumn:name="BACKEND",type="string",JSONPath=".spec.backend.name" -// +kubebuilder:printcolumn:name="ROUTES",type="string",JSONPath=".spec.routes[*].name" -// +kubebuilder:printcolumn:name="STABLE",type="string",JSONPath=".status.backends.stable.name" -// +kubebuilder:printcolumn:name="CANARY",type="string",JSONPath=".status.backends.canary.name" -// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",format="date-time" - -// BackendRouting defines defines the association between frontend routes and -// backend service, and it allows the user to define forwarding rules for canary scenario. -type BackendRouting struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec BackendRoutingSpec `json:"spec,omitempty"` - Status BackendRoutingStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:object:root=true - -// BackendRoutingList is a list of BackendRouting resources. -type BackendRoutingList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []BackendRouting `json:"items"` -} - -type BackendRoutingSpec struct { - // TrafficType defines the type of traffic - TrafficType TrafficType `json:"trafficType"` - // Backend defines the reference to a kind of backend - Backend CrossClusterObjectReference `json:"backend"` - // Routes defines the list of routes - Routes []CrossClusterObjectReference `json:"routes,omitempty"` - // Forwarding defines the forwarding rules for canary scenario - Forwarding *BackendForwarding `json:"forwarding,omitempty"` -} - -type BackendForwarding struct { - Stable StableBackendRule `json:"stable,omitempty"` - Canary CanaryBackendRule `json:"canary,omitempty"` -} - -type StableBackendRule struct { - // the temporary stable backend service name, generally it is the {originServiceName}-stable - Name string `json:"name,omitempty"` -} - -type CanaryBackendRule struct { - // the temporary canary backend service name, generally it is the {originServiceName}-canary - Name string `json:"name,omitempty"` - TrafficStrategy `json:",inline"` -} - -type TrafficStrategy struct { - HTTP *HTTPTrafficStrategy `json:"http,omitempty"` -} - -type HTTPTrafficStrategy struct { - HTTPRouteRule `json:",inline"` - // Weight indicate how many percentage of traffic the canary pods should receive - // - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:Maximum=100 - Weight *int32 `json:"weight,omitempty"` - // BaseTraffic indicate the base traffic rule - BaseTraffic *HTTPRouteRule `json:"baseTraffic,omitempty"` -} - -type BackendRoutingStatus struct { - // ObservedGeneration is the most recent generation observed. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // Phase indicates the current phase of this object. - Phase BackendRoutingPhase `json:"phase,omitempty"` - // current backends routing - Backends BackendStatuses `json:"backends,omitempty"` - // route statuses - RouteStatuses []BackendRouteStatus `json:"routeStatuses,omitempty"` -} - -type BackendStatuses struct { - // Origin backend status - Origin BackendStatus `json:"origin,omitempty"` - // Stable backend status - Stable BackendStatus `json:"stable,omitempty"` - // Canary backend status - Canary BackendStatus `json:"canary,omitempty"` -} - -type BackendStatus struct { - // Name is the name of the referent. - Name string `json:"name"` - // Conditions represents the current condition of an backend. - Conditions BackendConditions `json:"conditions,omitempty"` -} - -// Backendonditions represents the current condition of an backend. -type BackendConditions struct { - // ready indicates that this endpoint is prepared to receive traffic, - // according to whatever system is managing the endpoint. A nil value - // indicates an unknown state. In most cases consumers should interpret this - // unknown state as ready. For compatibility reasons, ready should never be - // "true" for terminating endpoints. - // +optional - Ready *bool `json:"ready,omitempty" protobuf:"bytes,1,name=ready"` - - // terminating indicates that this endpoint is terminating. A nil value - // indicates an unknown state. Consumers should interpret this unknown state - // to mean that the endpoint is not terminating. - // +optional - Terminating *bool `json:"terminating,omitempty" protobuf:"bytes,3,name=terminating"` -} - -type BackendRoutingPhase string - -const ( - BackendUpgrading BackendRoutingPhase = "BackendUpgrading" - RouteUpgrading BackendRoutingPhase = "RouteSyncing" - Ready BackendRoutingPhase = "Ready" -) - -// BackendRouteStatus defines the status of a backend route. -type BackendRouteStatus struct { - // CrossClusterObjectReference defines the reference to a kind of route resource. - CrossClusterObjectReference `json:",inline"` - // Synced indicates whether the backend route is synced. - Synced bool `json:"synced,omitempty"` -} diff --git a/apis/rollout/v1alpha1/validation/rollout.go b/apis/rollout/v1alpha1/validation/rollout.go index 1d3f2db..608fb7b 100644 --- a/apis/rollout/v1alpha1/validation/rollout.go +++ b/apis/rollout/v1alpha1/validation/rollout.go @@ -22,7 +22,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) type SupportedGVKFunc func(gvk schema.GroupVersionKind) bool diff --git a/apis/rollout/v1alpha1/validation/rollout_test.go b/apis/rollout/v1alpha1/validation/rollout_test.go index 0f7b59d..d148020 100644 --- a/apis/rollout/v1alpha1/validation/rollout_test.go +++ b/apis/rollout/v1alpha1/validation/rollout_test.go @@ -22,7 +22,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func supportAllGVK(gvk schema.GroupVersionKind) bool { diff --git a/apis/rollout/v1alpha1/validation/rolloutrun.go b/apis/rollout/v1alpha1/validation/rolloutrun.go index f9df647..79efbcc 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func ValidateRolloutRun(obj *rolloutv1alpha1.RolloutRun) field.ErrorList { diff --git a/apis/rollout/v1alpha1/validation/rolloutrun_test.go b/apis/rollout/v1alpha1/validation/rolloutrun_test.go index 233d144..3da32ae 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun_test.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func newValidRollotRun() *rolloutv1alpha1.RolloutRun { diff --git a/apis/rollout/v1alpha1/validation/rolloutstrategy.go b/apis/rollout/v1alpha1/validation/rolloutstrategy.go index 183fedd..d0f4258 100644 --- a/apis/rollout/v1alpha1/validation/rolloutstrategy.go +++ b/apis/rollout/v1alpha1/validation/rolloutstrategy.go @@ -21,10 +21,9 @@ import ( metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" - webhookutil "k8s.io/apiserver/pkg/util/webhook" appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func ValidateRolloutStrategy(obj *rolloutv1alpha1.RolloutStrategy) field.ErrorList { @@ -129,7 +128,7 @@ func ValidateRolloutWebhook(webhook *rolloutv1alpha1.RolloutWebhook, fldPath *fi allErrs = append(allErrs, field.Required(fldPath.Child("hookTypes"), "must specify at least one hook type")) } - allErrs = append(allErrs, webhookutil.ValidateWebhookURL(fldPath.Child("url"), webhook.ClientConfig.URL, false)...) + allErrs = append(allErrs, ValidateWebhookURL(fldPath.Child("url"), webhook.ClientConfig.URL, false)...) return allErrs } diff --git a/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go b/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go index e5e4023..f7d75d8 100644 --- a/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go @@ -24,7 +24,7 @@ import ( "k8s.io/utils/ptr" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) var validTraffic = &rolloutv1alpha1.TrafficStrategy{ diff --git a/apis/rollout/v1alpha1/validation/traffic_topology.go b/apis/rollout/v1alpha1/validation/traffic_topology.go index 386fb0c..0a37902 100644 --- a/apis/rollout/v1alpha1/validation/traffic_topology.go +++ b/apis/rollout/v1alpha1/validation/traffic_topology.go @@ -20,7 +20,7 @@ import ( apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/util/validation/field" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func ValidateTrafficTopology(obj *rolloutv1alpha1.TrafficTopology) field.ErrorList { diff --git a/apis/rollout/v1alpha1/validation/validation.go b/apis/rollout/v1alpha1/validation/validation.go new file mode 100644 index 0000000..f649133 --- /dev/null +++ b/apis/rollout/v1alpha1/validation/validation.go @@ -0,0 +1,33 @@ +package validation + +import ( + "net/url" + + "k8s.io/apimachinery/pkg/util/validation/field" +) + +// ValidateWebhookURL validates webhook's URL. +func ValidateWebhookURL(fldPath *field.Path, URL string, forceHttps bool) field.ErrorList { + var allErrors field.ErrorList + const form = "; desired format: https://host[/path]" + if u, err := url.Parse(URL); err != nil { + allErrors = append(allErrors, field.Required(fldPath, "url must be a valid URL: "+err.Error()+form)) + } else { + if forceHttps && u.Scheme != "https" { + allErrors = append(allErrors, field.Invalid(fldPath, u.Scheme, "'https' is the only allowed URL scheme"+form)) + } + if len(u.Host) == 0 { + allErrors = append(allErrors, field.Invalid(fldPath, u.Host, "host must be provided"+form)) + } + if u.User != nil { + allErrors = append(allErrors, field.Invalid(fldPath, u.User.String(), "user information is not permitted in the URL")) + } + if len(u.Fragment) != 0 { + allErrors = append(allErrors, field.Invalid(fldPath, u.Fragment, "fragments are not permitted in the URL")) + } + if len(u.RawQuery) != 0 { + allErrors = append(allErrors, field.Invalid(fldPath, u.RawQuery, "query parameters are not permitted in the URL")) + } + } + return allErrors +} diff --git a/apis/rollout/v1alpha1/zz_generated.deepcopy.go b/apis/rollout/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 14077ff..0000000 --- a/apis/rollout/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,1697 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// Copyright 2024 The KusionStack 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 deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - intstr "k8s.io/apimachinery/pkg/util/intstr" - v1 "sigs.k8s.io/gateway-api/apis/v1" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendConditions) DeepCopyInto(out *BackendConditions) { - *out = *in - if in.Ready != nil { - in, out := &in.Ready, &out.Ready - *out = new(bool) - **out = **in - } - if in.Terminating != nil { - in, out := &in.Terminating, &out.Terminating - *out = new(bool) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendConditions. -func (in *BackendConditions) DeepCopy() *BackendConditions { - if in == nil { - return nil - } - out := new(BackendConditions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendForwarding) DeepCopyInto(out *BackendForwarding) { - *out = *in - out.Stable = in.Stable - in.Canary.DeepCopyInto(&out.Canary) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendForwarding. -func (in *BackendForwarding) DeepCopy() *BackendForwarding { - if in == nil { - return nil - } - out := new(BackendForwarding) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendRef) DeepCopyInto(out *BackendRef) { - *out = *in - if in.APIVersion != nil { - in, out := &in.APIVersion, &out.APIVersion - *out = new(string) - **out = **in - } - if in.Kind != nil { - in, out := &in.Kind, &out.Kind - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRef. -func (in *BackendRef) DeepCopy() *BackendRef { - if in == nil { - return nil - } - out := new(BackendRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendRouteStatus) DeepCopyInto(out *BackendRouteStatus) { - *out = *in - out.CrossClusterObjectReference = in.CrossClusterObjectReference - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRouteStatus. -func (in *BackendRouteStatus) DeepCopy() *BackendRouteStatus { - if in == nil { - return nil - } - out := new(BackendRouteStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendRouting) DeepCopyInto(out *BackendRouting) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRouting. -func (in *BackendRouting) DeepCopy() *BackendRouting { - if in == nil { - return nil - } - out := new(BackendRouting) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackendRouting) 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 *BackendRoutingList) DeepCopyInto(out *BackendRoutingList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]BackendRouting, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRoutingList. -func (in *BackendRoutingList) DeepCopy() *BackendRoutingList { - if in == nil { - return nil - } - out := new(BackendRoutingList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackendRoutingList) 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 *BackendRoutingSpec) DeepCopyInto(out *BackendRoutingSpec) { - *out = *in - out.Backend = in.Backend - if in.Routes != nil { - in, out := &in.Routes, &out.Routes - *out = make([]CrossClusterObjectReference, len(*in)) - copy(*out, *in) - } - if in.Forwarding != nil { - in, out := &in.Forwarding, &out.Forwarding - *out = new(BackendForwarding) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRoutingSpec. -func (in *BackendRoutingSpec) DeepCopy() *BackendRoutingSpec { - if in == nil { - return nil - } - out := new(BackendRoutingSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendRoutingStatus) DeepCopyInto(out *BackendRoutingStatus) { - *out = *in - in.Backends.DeepCopyInto(&out.Backends) - if in.RouteStatuses != nil { - in, out := &in.RouteStatuses, &out.RouteStatuses - *out = make([]BackendRouteStatus, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRoutingStatus. -func (in *BackendRoutingStatus) DeepCopy() *BackendRoutingStatus { - if in == nil { - return nil - } - out := new(BackendRoutingStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendStatus) DeepCopyInto(out *BackendStatus) { - *out = *in - in.Conditions.DeepCopyInto(&out.Conditions) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendStatus. -func (in *BackendStatus) DeepCopy() *BackendStatus { - if in == nil { - return nil - } - out := new(BackendStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackendStatuses) DeepCopyInto(out *BackendStatuses) { - *out = *in - in.Origin.DeepCopyInto(&out.Origin) - in.Stable.DeepCopyInto(&out.Stable) - in.Canary.DeepCopyInto(&out.Canary) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendStatuses. -func (in *BackendStatuses) DeepCopy() *BackendStatuses { - if in == nil { - return nil - } - out := new(BackendStatuses) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BatchProgressingInfo) DeepCopyInto(out *BatchProgressingInfo) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BatchProgressingInfo. -func (in *BatchProgressingInfo) DeepCopy() *BatchProgressingInfo { - if in == nil { - return nil - } - out := new(BatchProgressingInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BatchStrategy) DeepCopyInto(out *BatchStrategy) { - *out = *in - if in.Batches != nil { - in, out := &in.Batches, &out.Batches - *out = make([]RolloutStep, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Toleration != nil { - in, out := &in.Toleration, &out.Toleration - *out = new(TolerationStrategy) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BatchStrategy. -func (in *BatchStrategy) DeepCopy() *BatchStrategy { - if in == nil { - return nil - } - out := new(BatchStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CanaryBackendRule) DeepCopyInto(out *CanaryBackendRule) { - *out = *in - in.TrafficStrategy.DeepCopyInto(&out.TrafficStrategy) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanaryBackendRule. -func (in *CanaryBackendRule) DeepCopy() *CanaryBackendRule { - if in == nil { - return nil - } - out := new(CanaryBackendRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CanaryProgressingInfo) DeepCopyInto(out *CanaryProgressingInfo) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanaryProgressingInfo. -func (in *CanaryProgressingInfo) DeepCopy() *CanaryProgressingInfo { - if in == nil { - return nil - } - out := new(CanaryProgressingInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CanaryStrategy) DeepCopyInto(out *CanaryStrategy) { - *out = *in - out.Replicas = in.Replicas - if in.Traffic != nil { - in, out := &in.Traffic, &out.Traffic - *out = new(TrafficStrategy) - (*in).DeepCopyInto(*out) - } - if in.Match != nil { - in, out := &in.Match, &out.Match - *out = new(ResourceMatch) - (*in).DeepCopyInto(*out) - } - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.TemplateMetadataPatch != nil { - in, out := &in.TemplateMetadataPatch, &out.TemplateMetadataPatch - *out = new(MetadataPatch) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanaryStrategy. -func (in *CanaryStrategy) DeepCopy() *CanaryStrategy { - if in == nil { - return nil - } - out := new(CanaryStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CodeReasonMessage) DeepCopyInto(out *CodeReasonMessage) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CodeReasonMessage. -func (in *CodeReasonMessage) DeepCopy() *CodeReasonMessage { - if in == nil { - return nil - } - out := new(CodeReasonMessage) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Condition) DeepCopyInto(out *Condition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. -func (in *Condition) DeepCopy() *Condition { - if in == nil { - return nil - } - out := new(Condition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CrossClusterObjectNameReference) DeepCopyInto(out *CrossClusterObjectNameReference) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossClusterObjectNameReference. -func (in *CrossClusterObjectNameReference) DeepCopy() *CrossClusterObjectNameReference { - if in == nil { - return nil - } - out := new(CrossClusterObjectNameReference) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CrossClusterObjectReference) DeepCopyInto(out *CrossClusterObjectReference) { - *out = *in - out.ObjectTypeRef = in.ObjectTypeRef - out.CrossClusterObjectNameReference = in.CrossClusterObjectNameReference - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossClusterObjectReference. -func (in *CrossClusterObjectReference) DeepCopy() *CrossClusterObjectReference { - if in == nil { - return nil - } - out := new(CrossClusterObjectReference) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRouteMatch) DeepCopyInto(out *HTTPRouteMatch) { - *out = *in - if in.Path != nil { - in, out := &in.Path, &out.Path - *out = new(v1.HTTPPathMatch) - (*in).DeepCopyInto(*out) - } - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = make([]v1.HTTPHeaderMatch, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.QueryParams != nil { - in, out := &in.QueryParams, &out.QueryParams - *out = make([]v1.HTTPQueryParamMatch, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteMatch. -func (in *HTTPRouteMatch) DeepCopy() *HTTPRouteMatch { - if in == nil { - return nil - } - out := new(HTTPRouteMatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) { - *out = *in - if in.Matches != nil { - in, out := &in.Matches, &out.Matches - *out = make([]HTTPRouteMatch, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Filters != nil { - in, out := &in.Filters, &out.Filters - *out = make([]v1.HTTPRouteFilter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteRule. -func (in *HTTPRouteRule) DeepCopy() *HTTPRouteRule { - if in == nil { - return nil - } - out := new(HTTPRouteRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPTrafficStrategy) DeepCopyInto(out *HTTPTrafficStrategy) { - *out = *in - in.HTTPRouteRule.DeepCopyInto(&out.HTTPRouteRule) - if in.Weight != nil { - in, out := &in.Weight, &out.Weight - *out = new(int32) - **out = **in - } - if in.BaseTraffic != nil { - in, out := &in.BaseTraffic, &out.BaseTraffic - *out = new(HTTPRouteRule) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPTrafficStrategy. -func (in *HTTPTrafficStrategy) DeepCopy() *HTTPTrafficStrategy { - if in == nil { - return nil - } - out := new(HTTPTrafficStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetadataPatch) DeepCopyInto(out *MetadataPatch) { - *out = *in - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataPatch. -func (in *MetadataPatch) DeepCopy() *MetadataPatch { - if in == nil { - return nil - } - out := new(MetadataPatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ObjectTypeRef) DeepCopyInto(out *ObjectTypeRef) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectTypeRef. -func (in *ObjectTypeRef) DeepCopy() *ObjectTypeRef { - if in == nil { - return nil - } - out := new(ObjectTypeRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProgressingInfo) DeepCopyInto(out *ProgressingInfo) { - *out = *in - if in.Canary != nil { - in, out := &in.Canary, &out.Canary - *out = new(CanaryProgressingInfo) - **out = **in - } - if in.Batch != nil { - in, out := &in.Batch, &out.Batch - *out = new(BatchProgressingInfo) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProgressingInfo. -func (in *ProgressingInfo) DeepCopy() *ProgressingInfo { - if in == nil { - return nil - } - out := new(ProgressingInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResourceMatch) DeepCopyInto(out *ResourceMatch) { - *out = *in - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = new(metav1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.Names != nil { - in, out := &in.Names, &out.Names - *out = make([]CrossClusterObjectNameReference, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceMatch. -func (in *ResourceMatch) DeepCopy() *ResourceMatch { - if in == nil { - return nil - } - out := new(ResourceMatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Rollout) DeepCopyInto(out *Rollout) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rollout. -func (in *Rollout) DeepCopy() *Rollout { - if in == nil { - return nil - } - out := new(Rollout) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Rollout) 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 *RolloutBatchStatus) DeepCopyInto(out *RolloutBatchStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutBatchStatus. -func (in *RolloutBatchStatus) DeepCopy() *RolloutBatchStatus { - if in == nil { - return nil - } - out := new(RolloutBatchStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutList) DeepCopyInto(out *RolloutList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Rollout, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutList. -func (in *RolloutList) DeepCopy() *RolloutList { - if in == nil { - return nil - } - out := new(RolloutList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RolloutList) 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 *RolloutReplicasSummary) DeepCopyInto(out *RolloutReplicasSummary) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutReplicasSummary. -func (in *RolloutReplicasSummary) DeepCopy() *RolloutReplicasSummary { - if in == nil { - return nil - } - out := new(RolloutReplicasSummary) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRun) DeepCopyInto(out *RolloutRun) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRun. -func (in *RolloutRun) DeepCopy() *RolloutRun { - if in == nil { - return nil - } - out := new(RolloutRun) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RolloutRun) 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 *RolloutRunBatchStatus) DeepCopyInto(out *RolloutRunBatchStatus) { - *out = *in - out.RolloutBatchStatus = in.RolloutBatchStatus - if in.Records != nil { - in, out := &in.Records, &out.Records - *out = make([]RolloutRunStepStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunBatchStatus. -func (in *RolloutRunBatchStatus) DeepCopy() *RolloutRunBatchStatus { - if in == nil { - return nil - } - out := new(RolloutRunBatchStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunBatchStrategy) DeepCopyInto(out *RolloutRunBatchStrategy) { - *out = *in - if in.Batches != nil { - in, out := &in.Batches, &out.Batches - *out = make([]RolloutRunStep, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Toleration != nil { - in, out := &in.Toleration, &out.Toleration - *out = new(TolerationStrategy) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunBatchStrategy. -func (in *RolloutRunBatchStrategy) DeepCopy() *RolloutRunBatchStrategy { - if in == nil { - return nil - } - out := new(RolloutRunBatchStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunCanaryStrategy) DeepCopyInto(out *RolloutRunCanaryStrategy) { - *out = *in - if in.Targets != nil { - in, out := &in.Targets, &out.Targets - *out = make([]RolloutRunStepTarget, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Traffic != nil { - in, out := &in.Traffic, &out.Traffic - *out = new(TrafficStrategy) - (*in).DeepCopyInto(*out) - } - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.TemplateMetadataPatch != nil { - in, out := &in.TemplateMetadataPatch, &out.TemplateMetadataPatch - *out = new(MetadataPatch) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunCanaryStrategy. -func (in *RolloutRunCanaryStrategy) DeepCopy() *RolloutRunCanaryStrategy { - if in == nil { - return nil - } - out := new(RolloutRunCanaryStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunList) DeepCopyInto(out *RolloutRunList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]RolloutRun, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunList. -func (in *RolloutRunList) DeepCopy() *RolloutRunList { - if in == nil { - return nil - } - out := new(RolloutRunList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RolloutRunList) 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 *RolloutRunSpec) DeepCopyInto(out *RolloutRunSpec) { - *out = *in - out.TargetType = in.TargetType - if in.Webhooks != nil { - in, out := &in.Webhooks, &out.Webhooks - *out = make([]RolloutWebhook, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.TrafficTopologyRefs != nil { - in, out := &in.TrafficTopologyRefs, &out.TrafficTopologyRefs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Canary != nil { - in, out := &in.Canary, &out.Canary - *out = new(RolloutRunCanaryStrategy) - (*in).DeepCopyInto(*out) - } - if in.Batch != nil { - in, out := &in.Batch, &out.Batch - *out = new(RolloutRunBatchStrategy) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunSpec. -func (in *RolloutRunSpec) DeepCopy() *RolloutRunSpec { - if in == nil { - return nil - } - out := new(RolloutRunSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunStatus) DeepCopyInto(out *RolloutRunStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() - } - if in.Error != nil { - in, out := &in.Error, &out.Error - *out = new(CodeReasonMessage) - **out = **in - } - if in.CanaryStatus != nil { - in, out := &in.CanaryStatus, &out.CanaryStatus - *out = new(RolloutRunStepStatus) - (*in).DeepCopyInto(*out) - } - if in.BatchStatus != nil { - in, out := &in.BatchStatus, &out.BatchStatus - *out = new(RolloutRunBatchStatus) - (*in).DeepCopyInto(*out) - } - if in.TargetStatuses != nil { - in, out := &in.TargetStatuses, &out.TargetStatuses - *out = make([]RolloutWorkloadStatus, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunStatus. -func (in *RolloutRunStatus) DeepCopy() *RolloutRunStatus { - if in == nil { - return nil - } - out := new(RolloutRunStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunStep) DeepCopyInto(out *RolloutRunStep) { - *out = *in - if in.Targets != nil { - in, out := &in.Targets, &out.Targets - *out = make([]RolloutRunStepTarget, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Traffic != nil { - in, out := &in.Traffic, &out.Traffic - *out = new(TrafficStrategy) - (*in).DeepCopyInto(*out) - } - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunStep. -func (in *RolloutRunStep) DeepCopy() *RolloutRunStep { - if in == nil { - return nil - } - out := new(RolloutRunStep) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunStepStatus) DeepCopyInto(out *RolloutRunStepStatus) { - *out = *in - if in.Index != nil { - in, out := &in.Index, &out.Index - *out = new(int32) - **out = **in - } - if in.StartTime != nil { - in, out := &in.StartTime, &out.StartTime - *out = (*in).DeepCopy() - } - if in.FinishTime != nil { - in, out := &in.FinishTime, &out.FinishTime - *out = (*in).DeepCopy() - } - if in.Targets != nil { - in, out := &in.Targets, &out.Targets - *out = make([]RolloutWorkloadStatus, len(*in)) - copy(*out, *in) - } - if in.Webhooks != nil { - in, out := &in.Webhooks, &out.Webhooks - *out = make([]RolloutWebhookStatus, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunStepStatus. -func (in *RolloutRunStepStatus) DeepCopy() *RolloutRunStepStatus { - if in == nil { - return nil - } - out := new(RolloutRunStepStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutRunStepTarget) DeepCopyInto(out *RolloutRunStepTarget) { - *out = *in - out.CrossClusterObjectNameReference = in.CrossClusterObjectNameReference - out.Replicas = in.Replicas - if in.ReplicaSlidingWindow != nil { - in, out := &in.ReplicaSlidingWindow, &out.ReplicaSlidingWindow - *out = new(intstr.IntOrString) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutRunStepTarget. -func (in *RolloutRunStepTarget) DeepCopy() *RolloutRunStepTarget { - if in == nil { - return nil - } - out := new(RolloutRunStepTarget) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { - *out = *in - if in.HistoryLimit != nil { - in, out := &in.HistoryLimit, &out.HistoryLimit - *out = new(int32) - **out = **in - } - in.WorkloadRef.DeepCopyInto(&out.WorkloadRef) - if in.TrafficTopologyRefs != nil { - in, out := &in.TrafficTopologyRefs, &out.TrafficTopologyRefs - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutSpec. -func (in *RolloutSpec) DeepCopy() *RolloutSpec { - if in == nil { - return nil - } - out := new(RolloutSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutStatus) DeepCopyInto(out *RolloutStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStatus. -func (in *RolloutStatus) DeepCopy() *RolloutStatus { - if in == nil { - return nil - } - out := new(RolloutStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutStep) DeepCopyInto(out *RolloutStep) { - *out = *in - out.Replicas = in.Replicas - if in.ReplicaSlidingWindow != nil { - in, out := &in.ReplicaSlidingWindow, &out.ReplicaSlidingWindow - *out = new(intstr.IntOrString) - **out = **in - } - if in.Traffic != nil { - in, out := &in.Traffic, &out.Traffic - *out = new(TrafficStrategy) - (*in).DeepCopyInto(*out) - } - if in.Match != nil { - in, out := &in.Match, &out.Match - *out = new(ResourceMatch) - (*in).DeepCopyInto(*out) - } - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStep. -func (in *RolloutStep) DeepCopy() *RolloutStep { - if in == nil { - return nil - } - out := new(RolloutStep) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutStrategy) DeepCopyInto(out *RolloutStrategy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.Canary != nil { - in, out := &in.Canary, &out.Canary - *out = new(CanaryStrategy) - (*in).DeepCopyInto(*out) - } - if in.Batch != nil { - in, out := &in.Batch, &out.Batch - *out = new(BatchStrategy) - (*in).DeepCopyInto(*out) - } - if in.Webhooks != nil { - in, out := &in.Webhooks, &out.Webhooks - *out = make([]RolloutWebhook, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStrategy. -func (in *RolloutStrategy) DeepCopy() *RolloutStrategy { - if in == nil { - return nil - } - out := new(RolloutStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RolloutStrategy) 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 *RolloutStrategyList) DeepCopyInto(out *RolloutStrategyList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]RolloutStrategy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStrategyList. -func (in *RolloutStrategyList) DeepCopy() *RolloutStrategyList { - if in == nil { - return nil - } - out := new(RolloutStrategyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RolloutStrategyList) 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 *RolloutWebhook) DeepCopyInto(out *RolloutWebhook) { - *out = *in - if in.HookTypes != nil { - in, out := &in.HookTypes, &out.HookTypes - *out = make([]HookType, len(*in)) - copy(*out, *in) - } - in.ClientConfig.DeepCopyInto(&out.ClientConfig) - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Provider != nil { - in, out := &in.Provider, &out.Provider - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhook. -func (in *RolloutWebhook) DeepCopy() *RolloutWebhook { - if in == nil { - return nil - } - out := new(RolloutWebhook) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutWebhookReview) DeepCopyInto(out *RolloutWebhookReview) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhookReview. -func (in *RolloutWebhookReview) DeepCopy() *RolloutWebhookReview { - if in == nil { - return nil - } - out := new(RolloutWebhookReview) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RolloutWebhookReview) 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 *RolloutWebhookReviewBatch) DeepCopyInto(out *RolloutWebhookReviewBatch) { - *out = *in - if in.Targets != nil { - in, out := &in.Targets, &out.Targets - *out = make([]RolloutRunStepTarget, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhookReviewBatch. -func (in *RolloutWebhookReviewBatch) DeepCopy() *RolloutWebhookReviewBatch { - if in == nil { - return nil - } - out := new(RolloutWebhookReviewBatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutWebhookReviewCanary) DeepCopyInto(out *RolloutWebhookReviewCanary) { - *out = *in - if in.Targets != nil { - in, out := &in.Targets, &out.Targets - *out = make([]RolloutRunStepTarget, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhookReviewCanary. -func (in *RolloutWebhookReviewCanary) DeepCopy() *RolloutWebhookReviewCanary { - if in == nil { - return nil - } - out := new(RolloutWebhookReviewCanary) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutWebhookReviewSpec) DeepCopyInto(out *RolloutWebhookReviewSpec) { - *out = *in - out.TargetType = in.TargetType - if in.Properties != nil { - in, out := &in.Properties, &out.Properties - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Canary != nil { - in, out := &in.Canary, &out.Canary - *out = new(RolloutWebhookReviewCanary) - (*in).DeepCopyInto(*out) - } - if in.Batch != nil { - in, out := &in.Batch, &out.Batch - *out = new(RolloutWebhookReviewBatch) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhookReviewSpec. -func (in *RolloutWebhookReviewSpec) DeepCopy() *RolloutWebhookReviewSpec { - if in == nil { - return nil - } - out := new(RolloutWebhookReviewSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutWebhookReviewStatus) DeepCopyInto(out *RolloutWebhookReviewStatus) { - *out = *in - out.CodeReasonMessage = in.CodeReasonMessage - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhookReviewStatus. -func (in *RolloutWebhookReviewStatus) DeepCopy() *RolloutWebhookReviewStatus { - if in == nil { - return nil - } - out := new(RolloutWebhookReviewStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutWebhookStatus) DeepCopyInto(out *RolloutWebhookStatus) { - *out = *in - out.CodeReasonMessage = in.CodeReasonMessage - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWebhookStatus. -func (in *RolloutWebhookStatus) DeepCopy() *RolloutWebhookStatus { - if in == nil { - return nil - } - out := new(RolloutWebhookStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RolloutWorkloadStatus) DeepCopyInto(out *RolloutWorkloadStatus) { - *out = *in - out.RolloutReplicasSummary = in.RolloutReplicasSummary - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutWorkloadStatus. -func (in *RolloutWorkloadStatus) DeepCopy() *RolloutWorkloadStatus { - if in == nil { - return nil - } - out := new(RolloutWorkloadStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteRef) DeepCopyInto(out *RouteRef) { - *out = *in - if in.APIVersion != nil { - in, out := &in.APIVersion, &out.APIVersion - *out = new(string) - **out = **in - } - if in.Kind != nil { - in, out := &in.Kind, &out.Kind - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteRef. -func (in *RouteRef) DeepCopy() *RouteRef { - if in == nil { - return nil - } - out := new(RouteRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StableBackendRule) DeepCopyInto(out *StableBackendRule) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StableBackendRule. -func (in *StableBackendRule) DeepCopy() *StableBackendRule { - if in == nil { - return nil - } - out := new(StableBackendRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TolerationStrategy) DeepCopyInto(out *TolerationStrategy) { - *out = *in - if in.WorkloadFailureThreshold != nil { - in, out := &in.WorkloadFailureThreshold, &out.WorkloadFailureThreshold - *out = new(intstr.IntOrString) - **out = **in - } - if in.TaskFailureThreshold != nil { - in, out := &in.TaskFailureThreshold, &out.TaskFailureThreshold - *out = new(intstr.IntOrString) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TolerationStrategy. -func (in *TolerationStrategy) DeepCopy() *TolerationStrategy { - if in == nil { - return nil - } - out := new(TolerationStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TopologyInfo) DeepCopyInto(out *TopologyInfo) { - *out = *in - out.WorkloadRef = in.WorkloadRef - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyInfo. -func (in *TopologyInfo) DeepCopy() *TopologyInfo { - if in == nil { - return nil - } - out := new(TopologyInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficStrategy) DeepCopyInto(out *TrafficStrategy) { - *out = *in - if in.HTTP != nil { - in, out := &in.HTTP, &out.HTTP - *out = new(HTTPTrafficStrategy) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficStrategy. -func (in *TrafficStrategy) DeepCopy() *TrafficStrategy { - if in == nil { - return nil - } - out := new(TrafficStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficTopology) DeepCopyInto(out *TrafficTopology) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficTopology. -func (in *TrafficTopology) DeepCopy() *TrafficTopology { - if in == nil { - return nil - } - out := new(TrafficTopology) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *TrafficTopology) 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 *TrafficTopologyList) DeepCopyInto(out *TrafficTopologyList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]TrafficTopology, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficTopologyList. -func (in *TrafficTopologyList) DeepCopy() *TrafficTopologyList { - if in == nil { - return nil - } - out := new(TrafficTopologyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *TrafficTopologyList) 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 *TrafficTopologySpec) DeepCopyInto(out *TrafficTopologySpec) { - *out = *in - in.WorkloadRef.DeepCopyInto(&out.WorkloadRef) - in.Backend.DeepCopyInto(&out.Backend) - if in.Routes != nil { - in, out := &in.Routes, &out.Routes - *out = make([]RouteRef, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficTopologySpec. -func (in *TrafficTopologySpec) DeepCopy() *TrafficTopologySpec { - if in == nil { - return nil - } - out := new(TrafficTopologySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficTopologyStatus) DeepCopyInto(out *TrafficTopologyStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Topologies != nil { - in, out := &in.Topologies, &out.Topologies - *out = make([]TopologyInfo, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficTopologyStatus. -func (in *TrafficTopologyStatus) DeepCopy() *TrafficTopologyStatus { - if in == nil { - return nil - } - out := new(TrafficTopologyStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WebhookClientConfig) DeepCopyInto(out *WebhookClientConfig) { - *out = *in - if in.CABundle != nil { - in, out := &in.CABundle, &out.CABundle - *out = make([]byte, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WebhookClientConfig. -func (in *WebhookClientConfig) DeepCopy() *WebhookClientConfig { - if in == nil { - return nil - } - out := new(WebhookClientConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkloadRef) DeepCopyInto(out *WorkloadRef) { - *out = *in - in.Match.DeepCopyInto(&out.Match) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadRef. -func (in *WorkloadRef) DeepCopy() *WorkloadRef { - if in == nil { - return nil - } - out := new(WorkloadRef) - in.DeepCopyInto(out) - return out -} diff --git a/apis/rollout/v1alpha1/zz_generated.register.go b/apis/rollout/v1alpha1/zz_generated.register.go deleted file mode 100644 index 1c3c2c0..0000000 --- a/apis/rollout/v1alpha1/zz_generated.register.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2024 The KusionStack 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 register-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// GroupName specifies the group name used to register the objects. -const GroupName = "rollout.kusionstack.io" - -// GroupVersion specifies the group and the version used to register the objects. -var GroupVersion = v1.GroupVersion{Group: GroupName, Version: "v1alpha1"} - -// SchemeGroupVersion is group version used to register these objects -// Deprecated: use GroupVersion instead. -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. - SchemeBuilder runtime.SchemeBuilder - localSchemeBuilder = &SchemeBuilder - // Depreciated: use Install instead - AddToScheme = localSchemeBuilder.AddToScheme - Install = localSchemeBuilder.AddToScheme -) - -func init() { - // We only register manually written functions here. The registration of the - // generated functions takes place in the generated files. The separation - // makes the code compile even when the generated files are missing. - localSchemeBuilder.Register(addKnownTypes) -} - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &BackendRouting{}, - &BackendRoutingList{}, - &Rollout{}, - &RolloutList{}, - &RolloutRun{}, - &RolloutRunList{}, - &RolloutStrategy{}, - &RolloutStrategyList{}, - &RolloutWebhookReview{}, - &TrafficTopology{}, - &TrafficTopologyList{}, - ) - // AddToGroupVersion allows the serialization of client types like ListOptions. - v1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/apis/rollout/well_known_annotations.go b/apis/rollout/well_known_annotations.go deleted file mode 100644 index 951863f..0000000 --- a/apis/rollout/well_known_annotations.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2023 The KusionStack 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 rollout - -const ( - // LabelRolloutManualCommand is set in Rollout for users to manipulate rolloutRun - AnnoManualCommandKey = "rollout.kusionstack.io/manual-command" - // Deprecated: use continue - AnnoManualCommandResume = "resume" - AnnoManualCommandContinue = "continue" - AnnoManualCommandRetry = "retry" - AnnoManualCommandSkip = "skip" - AnnoManualCommandPause = "pause" - AnnoManualCommandCancel = "cancel" - AnnoManualCommandForceSkipCurrentBatch = "force-skip-current-batch" - - AnnoRolloutTrigger = "rollout.kusionstack.io/trigger" - - // AnnoRolloutProgressingInfo contains the current progressing info on workload. - // The value is a json string of ProgressingInfo. - AnnoRolloutProgressingInfo = "rollout.kusionstack.io/progressing-info" - - // AnnoRolloutProgressingInfos contains a slice of progressing info on resource. - AnnoRolloutProgressingInfos = "rollout.kusionstack.io/progressing-infos" - - // AnnoPodRolloutProgressingInfos contains a slice of progressing infos on a pod. - AnnoPodRolloutProgressingInfos = "rollout.kusionstack.io/pod-progressing-infos" - - // AnnoRolloutName is the name of the rollout object. - AnnoRolloutName = "rollout.kusionstack.io/name" -) diff --git a/apis/rollout/well_known_finalizers.go b/apis/rollout/well_known_finalizers.go deleted file mode 100644 index 990d36c..0000000 --- a/apis/rollout/well_known_finalizers.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 The KusionStack 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 rollout - -const ( - FinalizerRolloutProtection = "finalizer.rollout.kusionstack.io/protection" - - FinalizerCanaryResourceProtection = "finalizer.rollout.kusionstack.io/canary-release" -) diff --git a/apis/rollout/well_known_labels.go b/apis/rollout/well_known_labels.go deleted file mode 100644 index 30cec91..0000000 --- a/apis/rollout/well_known_labels.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023 The KusionStack 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 rollout - -const ( - // This label is added to objects to reference their controller resource. - LabelControlledBy = "rollout.kusionstack.io/controlled-by" - // This label is added to workload object to identify the workload type. - LabelWorkload = "rollout.kusionstack.io/workload" -) - -// canary labels -const ( - // This label will be added to canary workload and pods. - LabelCanary = "rollout.kusionstack.io/canary" - // This label indicates the revision of pods controlled by workload. - LabelTrafficRevision = "traffic.rollout.kusionstack.io/revision" - LabelValueTrafficRevisionBase = "base" - LabelValueTrafficRevisionCanary = "canary" -) - -// rollout class label -const ( - LabelRolloutClass = "rollout.kusionstack.io/rollout-class" -) diff --git a/cmd/rollout/app/options/controller.go b/cmd/rollout/app/options/controller.go index f410f7a..15c34d1 100644 --- a/cmd/rollout/app/options/controller.go +++ b/cmd/rollout/app/options/controller.go @@ -22,7 +22,7 @@ import ( "github.com/spf13/pflag" corev1 "k8s.io/api/core/v1" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) var GroupKindConcurrency = map[string]int{ diff --git a/cmd/rollout/import_known_versions.go b/cmd/rollout/import_known_versions.go index 1d77ad2..0bc79b4 100644 --- a/cmd/rollout/import_known_versions.go +++ b/cmd/rollout/import_known_versions.go @@ -19,7 +19,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func init() { diff --git a/go.mod b/go.mod index c97d41c..52fd8f3 100644 --- a/go.mod +++ b/go.mod @@ -1,77 +1,62 @@ module kusionstack.io/rollout -go 1.23 +go 1.24.0 + +toolchain go1.24.2 require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc - github.com/go-logr/logr v1.4.1 - github.com/google/uuid v1.4.0 + github.com/go-logr/logr v1.4.2 + github.com/google/uuid v1.6.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.30.0 - github.com/spf13/cobra v1.8.0 - github.com/spf13/pflag v1.0.5 + github.com/spf13/cobra v1.9.1 + github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 - k8s.io/api v0.28.4 - k8s.io/apiextensions-apiserver v0.28.3 - k8s.io/apimachinery v0.28.4 + k8s.io/api v0.33.2 + k8s.io/apiextensions-apiserver v0.32.3 + k8s.io/apimachinery v0.33.2 k8s.io/apiserver v0.29.3 - k8s.io/client-go v0.28.4 - k8s.io/code-generator v0.28.3 + k8s.io/client-go v0.32.3 + k8s.io/code-generator v0.32.3 k8s.io/component-base v0.28.4 - k8s.io/klog/v2 v2.100.1 + k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v1.22.2 - k8s.io/utils v0.0.0-20240102154912-e7106e64919e - kusionstack.io/kube-api v0.5.1-0.20240809093445-d0eef055208b + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 + kusionstack.io/kube-api v0.6.7-0.20250715075952-2aa7e2e576af kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 kusionstack.io/resourceconsist v0.0.2 - sigs.k8s.io/controller-runtime v0.17.3 + sigs.k8s.io/controller-runtime v0.20.4 ) require ( - github.com/blang/semver v3.5.1+incompatible // indirect github.com/cyphar/filepath-securejoin v0.2.2 // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/opencontainers/runc v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/zoumo/golib v0.2.0 // indirect - go.opentelemetry.io/contrib v0.20.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect - go.opentelemetry.io/otel v0.20.0 // indirect - go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect - go.opentelemetry.io/otel/metric v0.20.0 // indirect - go.opentelemetry.io/otel/sdk v0.20.0 // indirect - go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect - go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect - go.opentelemetry.io/otel/trace v0.20.0 // indirect - go.opentelemetry.io/proto/otlp v0.7.0 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/grpc v1.59.0 // indirect + golang.org/x/sync v0.13.0 // indirect gotest.tools/v3 v3.4.0 // indirect k8s.io/component-helpers v0.22.2 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 // indirect ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/zapr v1.2.4 // indirect - github.com/go-openapi/jsonpointer v0.20.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.6.0 + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-cmp v0.7.0 github.com/google/gofuzz v1.2.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect github.com/imdario/mergo v0.3.16 // indirect @@ -91,26 +76,25 @@ require ( github.com/samber/lo v1.47.0 github.com/spf13/afero v1.11.0 // indirect go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.26.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.30.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - sigs.k8s.io/gateway-api v1.0.0 - sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + sigs.k8s.io/gateway-api v1.3.0 + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 8d722c6..e14b56b 100644 --- a/go.sum +++ b/go.sum @@ -65,7 +65,6 @@ github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9 github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.38.49/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -75,7 +74,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -83,8 +81,8 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -129,7 +127,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -170,7 +168,6 @@ github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -197,18 +194,16 @@ github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -245,8 +240,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -262,8 +257,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -274,8 +269,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= @@ -293,7 +288,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -490,8 +484,8 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -521,13 +515,14 @@ github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNo github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -535,8 +530,6 @@ github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwb github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -545,9 +538,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -585,35 +575,24 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 h1:Q3C9yzW6I9jqEc8sawxzxZmY48fs9u220KXq6d5s3XU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -621,8 +600,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -658,8 +637,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -701,15 +680,15 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -723,8 +702,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -787,8 +766,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -797,8 +776,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -807,21 +786,20 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -866,8 +844,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -898,8 +876,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -925,12 +901,6 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -946,8 +916,6 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -960,8 +928,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1050,8 +1018,8 @@ k8s.io/sample-apiserver v0.22.2/go.mod h1:h+/DIV5EmuNq4vfPr5TSXy9mIBVXXlPAKQMPbj k8s.io/system-validators v1.5.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -kusionstack.io/kube-api v0.5.1-0.20240809093445-d0eef055208b h1:Tgpx59rWqs4ij6c4XaySlMRbqVAPR9FWYWAT7FHkztI= -kusionstack.io/kube-api v0.5.1-0.20240809093445-d0eef055208b/go.mod h1:J0+EHiroG/88X904Y9TV9iMRcoEuD5tXMTLMBDSwM+Y= +kusionstack.io/kube-api v0.6.7-0.20250715075952-2aa7e2e576af h1:i8Qxd6NIH7o6cLgt8usKnoFNOKrqgxSbPK2zfVoah1A= +kusionstack.io/kube-api v0.6.7-0.20250715075952-2aa7e2e576af/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 h1:HYE6Wa8EzSlA6UmaTLtNKUgkB2mmasp6Ul69d3/SpK0= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6/go.mod h1:5Uy3GCJ1JEGqZw/Sp/uVnHBJN1t9wjY6USPSZ9s4idk= kusionstack.io/resourceconsist v0.0.2 h1:gf+c/LOMsiKoVR+GLzOomw8qcUbZbPckQLczZllNdVM= @@ -1063,20 +1031,21 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.10.3 h1:s5Ttmw/B4AuIbwrXD3sfBkXwnPMMWrqpVj4WRt1dano= sigs.k8s.io/controller-runtime v0.10.3/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= -sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= -sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= +sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= +sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs= sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016 h1:kXv6kKdoEtedwuqMmkqhbkgvYKeycVbC8+iPCP9j5kQ= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/pkg/backend/service/backend.go b/pkg/backend/service/backend.go index 2749a20..e08f4d4 100644 --- a/pkg/backend/service/backend.go +++ b/pkg/backend/service/backend.go @@ -18,7 +18,7 @@ import ( corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/apis/rollout" + "kusionstack.io/kube-api/rollout" "kusionstack.io/rollout/pkg/backend" ) diff --git a/pkg/controllers/backendrouting/backendrouting_controller.go b/pkg/controllers/backendrouting/backendrouting_controller.go index e9013e6..94087d8 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller.go +++ b/pkg/controllers/backendrouting/backendrouting_controller.go @@ -32,7 +32,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/backend" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/route" diff --git a/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go b/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go index adcf6b2..5ad1d60 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go +++ b/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go @@ -37,7 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" ) diff --git a/pkg/controllers/backendrouting/backendrouting_controller_test.go b/pkg/controllers/backendrouting/backendrouting_controller_test.go index ff836a4..56c2329 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller_test.go +++ b/pkg/controllers/backendrouting/backendrouting_controller_test.go @@ -31,7 +31,7 @@ import ( "kusionstack.io/kube-utils/multicluster/clusterinfo" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) var _ = Describe("backend-routing-controller", func() { diff --git a/pkg/controllers/podcanarylabel/podcanarylabel.go b/pkg/controllers/podcanarylabel/podcanarylabel.go index d93747f..c081a46 100644 --- a/pkg/controllers/podcanarylabel/podcanarylabel.go +++ b/pkg/controllers/podcanarylabel/podcanarylabel.go @@ -29,7 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - rolloutapi "kusionstack.io/rollout/apis/rollout" + rolloutapi "kusionstack.io/kube-api/rollout" "kusionstack.io/rollout/pkg/controllers/registry" rolloutcontroller "kusionstack.io/rollout/pkg/controllers/rollout" "kusionstack.io/rollout/pkg/utils" diff --git a/pkg/controllers/rollout/event_handler.go b/pkg/controllers/rollout/event_handler.go index 86d6b6d..f315e29 100644 --- a/pkg/controllers/rollout/event_handler.go +++ b/pkg/controllers/rollout/event_handler.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rollout/rollout_controller.go b/pkg/controllers/rollout/rollout_controller.go index 2d01e85..c0e2e26 100644 --- a/pkg/controllers/rollout/rollout_controller.go +++ b/pkg/controllers/rollout/rollout_controller.go @@ -44,9 +44,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" - "kusionstack.io/rollout/apis/rollout/v1alpha1/condition" + "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1/condition" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/pkg/features/ontimestrategy" diff --git a/pkg/controllers/rollout/utils.go b/pkg/controllers/rollout/utils.go index 5f8a5b5..abaab28 100644 --- a/pkg/controllers/rollout/utils.go +++ b/pkg/controllers/rollout/utils.go @@ -29,8 +29,8 @@ import ( "kusionstack.io/kube-utils/multicluster" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/pkg/features/ontimestrategy" diff --git a/pkg/controllers/rollout/utils_test.go b/pkg/controllers/rollout/utils_test.go index 0884fd4..a51f1c3 100644 --- a/pkg/controllers/rollout/utils_test.go +++ b/pkg/controllers/rollout/utils_test.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/control/control.go b/pkg/controllers/rolloutrun/control/control.go index d643b9e..a54ce1a 100644 --- a/pkg/controllers/rolloutrun/control/control.go +++ b/pkg/controllers/rolloutrun/control/control.go @@ -33,8 +33,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/alias.go b/pkg/controllers/rolloutrun/executor/alias.go index 1c902a2..0a017de 100644 --- a/pkg/controllers/rolloutrun/executor/alias.go +++ b/pkg/controllers/rolloutrun/executor/alias.go @@ -16,7 +16,7 @@ package executor -import rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" +import rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" const ( StepNone = rolloutv1alpha1.RolloutStepNone diff --git a/pkg/controllers/rolloutrun/executor/batch.go b/pkg/controllers/rolloutrun/executor/batch.go index a42807f..4b9849b 100644 --- a/pkg/controllers/rolloutrun/executor/batch.go +++ b/pkg/controllers/rolloutrun/executor/batch.go @@ -24,7 +24,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/control" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/batch_test.go b/pkg/controllers/rolloutrun/executor/batch_test.go index 1844ba2..d3eedab 100644 --- a/pkg/controllers/rolloutrun/executor/batch_test.go +++ b/pkg/controllers/rolloutrun/executor/batch_test.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/canary.go b/pkg/controllers/rolloutrun/executor/canary.go index 1e4ce09..9eed2de 100644 --- a/pkg/controllers/rolloutrun/executor/canary.go +++ b/pkg/controllers/rolloutrun/executor/canary.go @@ -23,8 +23,8 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/control" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/context.go b/pkg/controllers/rolloutrun/executor/context.go index bc825dd..a40a9cc 100644 --- a/pkg/controllers/rolloutrun/executor/context.go +++ b/pkg/controllers/rolloutrun/executor/context.go @@ -27,7 +27,7 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/traffic" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/context_test.go b/pkg/controllers/rolloutrun/executor/context_test.go index 30dac24..a3c7b99 100644 --- a/pkg/controllers/rolloutrun/executor/context_test.go +++ b/pkg/controllers/rolloutrun/executor/context_test.go @@ -19,7 +19,7 @@ package executor import ( "github.com/stretchr/testify/suite" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) type executorContextTestSuite struct { diff --git a/pkg/controllers/rolloutrun/executor/default.go b/pkg/controllers/rolloutrun/executor/default.go index 928457d..b7693f4 100644 --- a/pkg/controllers/rolloutrun/executor/default.go +++ b/pkg/controllers/rolloutrun/executor/default.go @@ -6,8 +6,8 @@ import ( "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" - rolloutapis "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapis "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/controllers/rolloutrun/executor/default_test.go b/pkg/controllers/rolloutrun/executor/default_test.go index c512c44..9a5a9f9 100644 --- a/pkg/controllers/rolloutrun/executor/default_test.go +++ b/pkg/controllers/rolloutrun/executor/default_test.go @@ -17,8 +17,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/log/zap" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" "kusionstack.io/rollout/pkg/workload/statefulset" ) diff --git a/pkg/controllers/rolloutrun/executor/do_command.go b/pkg/controllers/rolloutrun/executor/do_command.go index b459ad9..6417103 100644 --- a/pkg/controllers/rolloutrun/executor/do_command.go +++ b/pkg/controllers/rolloutrun/executor/do_command.go @@ -3,8 +3,8 @@ package executor import ( ctrl "sigs.k8s.io/controller-runtime" - rolloutapis "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapis "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) // doCommand diff --git a/pkg/controllers/rolloutrun/executor/do_hook.go b/pkg/controllers/rolloutrun/executor/do_hook.go index 6c96548..96b5d1f 100644 --- a/pkg/controllers/rolloutrun/executor/do_hook.go +++ b/pkg/controllers/rolloutrun/executor/do_hook.go @@ -6,7 +6,7 @@ import ( "github.com/samber/lo" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/controllers/rolloutrun/executor/do_hook_test.go b/pkg/controllers/rolloutrun/executor/do_hook_test.go index 438f234..20b0e1f 100644 --- a/pkg/controllers/rolloutrun/executor/do_hook_test.go +++ b/pkg/controllers/rolloutrun/executor/do_hook_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/suite" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) type webhookExecutorTestSuite struct { diff --git a/pkg/controllers/rolloutrun/executor/step_lifecycle.go b/pkg/controllers/rolloutrun/executor/step_lifecycle.go index 7ad9c65..193d4cc 100644 --- a/pkg/controllers/rolloutrun/executor/step_lifecycle.go +++ b/pkg/controllers/rolloutrun/executor/step_lifecycle.go @@ -25,7 +25,7 @@ import ( corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/control" ) diff --git a/pkg/controllers/rolloutrun/rolloutrun_controller.go b/pkg/controllers/rolloutrun/rolloutrun_controller.go index c7e9ff9..2d858aa 100644 --- a/pkg/controllers/rolloutrun/rolloutrun_controller.go +++ b/pkg/controllers/rolloutrun/rolloutrun_controller.go @@ -35,9 +35,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" - "kusionstack.io/rollout/apis/rollout/v1alpha1/condition" + "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1/condition" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/controllers/rolloutrun/executor" "kusionstack.io/rollout/pkg/controllers/rolloutrun/traffic" diff --git a/pkg/controllers/rolloutrun/traffic/traffic_manager.go b/pkg/controllers/rolloutrun/traffic/traffic_manager.go index d14eb33..7c0caab 100644 --- a/pkg/controllers/rolloutrun/traffic/traffic_manager.go +++ b/pkg/controllers/rolloutrun/traffic/traffic_manager.go @@ -24,7 +24,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/controllers/rolloutrun/webhook/manager.go b/pkg/controllers/rolloutrun/webhook/manager.go index ef6d796..249a6d5 100644 --- a/pkg/controllers/rolloutrun/webhook/manager.go +++ b/pkg/controllers/rolloutrun/webhook/manager.go @@ -22,7 +22,7 @@ import ( "k8s.io/apimachinery/pkg/types" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) type Manager interface { diff --git a/pkg/controllers/rolloutrun/webhook/probe/http/http.go b/pkg/controllers/rolloutrun/webhook/probe/http/http.go index de20264..9408f88 100644 --- a/pkg/controllers/rolloutrun/webhook/probe/http/http.go +++ b/pkg/controllers/rolloutrun/webhook/probe/http/http.go @@ -27,7 +27,7 @@ import ( "k8s.io/client-go/transport" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" ) diff --git a/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go b/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go index 60af5f1..0dd8ae5 100644 --- a/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go +++ b/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/assert" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" ) diff --git a/pkg/controllers/rolloutrun/webhook/probe/probe.go b/pkg/controllers/rolloutrun/webhook/probe/probe.go index aab35f4..98a0671 100644 --- a/pkg/controllers/rolloutrun/webhook/probe/probe.go +++ b/pkg/controllers/rolloutrun/webhook/probe/probe.go @@ -17,7 +17,7 @@ package probe import ( - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) type Result = rolloutv1alpha1.CodeReasonMessage diff --git a/pkg/controllers/rolloutrun/webhook/worker.go b/pkg/controllers/rolloutrun/webhook/worker.go index ece5790..9d7a8d5 100644 --- a/pkg/controllers/rolloutrun/webhook/worker.go +++ b/pkg/controllers/rolloutrun/webhook/worker.go @@ -24,7 +24,7 @@ import ( "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe/http" ) diff --git a/pkg/controllers/rolloutrun/webhook/worker_test.go b/pkg/controllers/rolloutrun/webhook/worker_test.go index f9f6fb5..10f16b8 100644 --- a/pkg/controllers/rolloutrun/webhook/worker_test.go +++ b/pkg/controllers/rolloutrun/webhook/worker_test.go @@ -23,7 +23,7 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" ) diff --git a/pkg/controllers/traffictopology/adapter.go b/pkg/controllers/traffictopology/adapter.go index 8297ff9..13f7da3 100644 --- a/pkg/controllers/traffictopology/adapter.go +++ b/pkg/controllers/traffictopology/adapter.go @@ -33,7 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/ratelimiter" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload" diff --git a/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go b/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go index 4e0de23..5a27acf 100644 --- a/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go +++ b/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go @@ -42,7 +42,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" ) diff --git a/pkg/controllers/traffictopology/traffictopology_controller_test.go b/pkg/controllers/traffictopology/traffictopology_controller_test.go index bd7165a..9504a8a 100644 --- a/pkg/controllers/traffictopology/traffictopology_controller_test.go +++ b/pkg/controllers/traffictopology/traffictopology_controller_test.go @@ -30,7 +30,7 @@ import ( "k8s.io/apimachinery/pkg/types" "kusionstack.io/kube-utils/multicluster/clusterinfo" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) var _ = Describe("traffic-topology-controller", func() { diff --git a/pkg/controllers/traffictopology/types.go b/pkg/controllers/traffictopology/types.go index cf715c8..e9f2f46 100644 --- a/pkg/controllers/traffictopology/types.go +++ b/pkg/controllers/traffictopology/types.go @@ -17,7 +17,7 @@ package traffictopology import ( rsFrameController "kusionstack.io/resourceconsist/pkg/frame/controller" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) var _ rsFrameController.IEmployer = TPEmployer{} diff --git a/pkg/features/ontimestrategy/ontimestrategy.go b/pkg/features/ontimestrategy/ontimestrategy.go index 7de6d82..7babe3a 100644 --- a/pkg/features/ontimestrategy/ontimestrategy.go +++ b/pkg/features/ontimestrategy/ontimestrategy.go @@ -17,7 +17,7 @@ package ontimestrategy import ( "encoding/json" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) const ( diff --git a/pkg/route/ingress/route.go b/pkg/route/ingress/route.go index 32cd3f1..1ad0bde 100644 --- a/pkg/route/ingress/route.go +++ b/pkg/route/ingress/route.go @@ -28,7 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" v1 "sigs.k8s.io/gateway-api/apis/v1" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/route" ) diff --git a/pkg/route/interface.go b/pkg/route/interface.go index 1e0af91..05ca1c0 100644 --- a/pkg/route/interface.go +++ b/pkg/route/interface.go @@ -21,7 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) type BackendChangeDetail struct { diff --git a/pkg/utils/progressinginfos/progressing_info.go b/pkg/utils/progressinginfos/progressing_info.go index bf3230f..458cb26 100644 --- a/pkg/utils/progressinginfos/progressing_info.go +++ b/pkg/utils/progressinginfos/progressing_info.go @@ -9,8 +9,8 @@ import ( "github.com/samber/lo" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/utils" ) @@ -112,7 +112,7 @@ func (m *ProgressingInfoMutator) SetProgressingInfo(obj runtimeclient.Object, in // get info from obj annotation var existing *rolloutv1alpha1.ProgressingInfo - objInfo := utils.GetMapValueByDefault(obj.GetAnnotations(), rollout.AnnoRolloutProgressingInfo, "") + objInfo := utils.GetMapValueByDefault(obj.GetAnnotations(), rolloutapi.AnnoRolloutProgressingInfo, "") if len(objInfo) > 0 { temp := rolloutv1alpha1.ProgressingInfo{} err := json.Unmarshal([]byte(objInfo), &temp) @@ -128,7 +128,7 @@ func (m *ProgressingInfoMutator) SetProgressingInfo(obj runtimeclient.Object, in changed = true // set progressingInfo if no progressingInfo utils.MutateAnnotations(obj, func(annotations map[string]string) { - annotations[rollout.AnnoRolloutProgressingInfo] = string(expected) + annotations[rolloutapi.AnnoRolloutProgressingInfo] = string(expected) }) } return changed @@ -156,7 +156,7 @@ func generateProgressingInfos(owners []*registry.WorkloadAccessor) (*rolloutv1al result := ProgressingInfos{} for _, owner := range owners { - ownerInfo := utils.GetMapValueByDefault(owner.Object.GetAnnotations(), rollout.AnnoRolloutProgressingInfo, "") + ownerInfo := utils.GetMapValueByDefault(owner.Object.GetAnnotations(), rolloutapi.AnnoRolloutProgressingInfo, "") if len(ownerInfo) == 0 { continue } diff --git a/pkg/utils/progressinginfos/progressing_info_test.go b/pkg/utils/progressinginfos/progressing_info_test.go index 5c2ae78..4f864c2 100644 --- a/pkg/utils/progressinginfos/progressing_info_test.go +++ b/pkg/utils/progressinginfos/progressing_info_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/assert" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) func newTestProgressingInfo(kind, name, id string) rolloutv1alpha1.ProgressingInfo { diff --git a/pkg/utils/slice.go b/pkg/utils/slice.go index 505e7ba..a4b78e1 100644 --- a/pkg/utils/slice.go +++ b/pkg/utils/slice.go @@ -18,7 +18,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) func SliceTopologyInfoEqual(a, b []v1alpha1.TopologyInfo) bool { diff --git a/pkg/utils/slice_test.go b/pkg/utils/slice_test.go index cd41329..f1dab99 100644 --- a/pkg/utils/slice_test.go +++ b/pkg/utils/slice_test.go @@ -21,7 +21,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) func Test_SliceTopologyInfoEqual(t *testing.T) { diff --git a/pkg/webhook/mutating/pod/pod_mutating.go b/pkg/webhook/mutating/pod/pod_mutating.go index 4cb1e14..f7567ca 100644 --- a/pkg/webhook/mutating/pod/pod_mutating.go +++ b/pkg/webhook/mutating/pod/pod_mutating.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "kusionstack.io/rollout/apis/rollout" + "kusionstack.io/kube-api/rollout" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/utils/progressinginfos" "kusionstack.io/rollout/pkg/webhook/generic" diff --git a/pkg/webhook/validating/rollout/rollout_validating.go b/pkg/webhook/validating/rollout/rollout_validating.go index e49b7b5..581587b 100644 --- a/pkg/webhook/validating/rollout/rollout_validating.go +++ b/pkg/webhook/validating/rollout/rollout_validating.go @@ -27,8 +27,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" - "kusionstack.io/rollout/apis/rollout/v1alpha1/validation" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + rolloutvalidation "kusionstack.io/rollout/apis/rollout/v1alpha1/validation" "kusionstack.io/rollout/pkg/controllers/registry" ) @@ -74,13 +74,13 @@ func (v *Validator) ValidateCreate(ctx context.Context, obj runtime.Object) erro var errs field.ErrorList switch t := obj.(type) { case *rolloutv1alpha1.Rollout: - errs = validation.ValidateRollout(t, registry.IsSupportedWorkload) + errs = rolloutvalidation.ValidateRollout(t, registry.IsSupportedWorkload) case *rolloutv1alpha1.RolloutStrategy: - errs = validation.ValidateRolloutStrategy(t) + errs = rolloutvalidation.ValidateRolloutStrategy(t) case *rolloutv1alpha1.RolloutRun: - errs = validation.ValidateRolloutRun(t) + errs = rolloutvalidation.ValidateRolloutRun(t) case *rolloutv1alpha1.TrafficTopology: - errs = validation.ValidateTrafficTopology(t) + errs = rolloutvalidation.ValidateTrafficTopology(t) default: return fmt.Errorf("unexpected object type %T", obj) } @@ -97,19 +97,19 @@ func (v *Validator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.O var errs field.ErrorList switch newV := newObj.(type) { case *rolloutv1alpha1.Rollout: - errs = validation.ValidateRollout(newV, registry.IsSupportedWorkload) + errs = rolloutvalidation.ValidateRollout(newV, registry.IsSupportedWorkload) if len(errs) == 0 { - errs = validation.ValidateRolloutUpdate(newV, oldObj.(*rolloutv1alpha1.Rollout)) + errs = rolloutvalidation.ValidateRolloutUpdate(newV, oldObj.(*rolloutv1alpha1.Rollout)) } case *rolloutv1alpha1.RolloutStrategy: - errs = validation.ValidateRolloutStrategy(newV) + errs = rolloutvalidation.ValidateRolloutStrategy(newV) case *rolloutv1alpha1.RolloutRun: - errs = validation.ValidateRolloutRun(newV) + errs = rolloutvalidation.ValidateRolloutRun(newV) if len(errs) == 0 { - errs = validation.ValidateRolloutRunUpdate(newV, oldObj.(*rolloutv1alpha1.RolloutRun)) + errs = rolloutvalidation.ValidateRolloutRunUpdate(newV, oldObj.(*rolloutv1alpha1.RolloutRun)) } case *rolloutv1alpha1.TrafficTopology: - errs = validation.ValidateTrafficTopology(newV) + errs = rolloutvalidation.ValidateTrafficTopology(newV) default: return fmt.Errorf("unexpected object type %T", newObj) } diff --git a/pkg/workload/collaset/release.go b/pkg/workload/collaset/release.go index 39f01e9..cd55c75 100644 --- a/pkg/workload/collaset/release.go +++ b/pkg/workload/collaset/release.go @@ -24,7 +24,7 @@ import ( operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/workload/info.go b/pkg/workload/info.go index 0f28457..ba777f2 100644 --- a/pkg/workload/info.go +++ b/pkg/workload/info.go @@ -29,8 +29,8 @@ import ( "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/workload/interface.go b/pkg/workload/interface.go index 3630912..dc0f1bd 100644 --- a/pkg/workload/interface.go +++ b/pkg/workload/interface.go @@ -20,7 +20,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/apis/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1" ) // Accessor defines the functions to access the workload. diff --git a/pkg/workload/matcher.go b/pkg/workload/matcher.go index 44a6371..a532995 100644 --- a/pkg/workload/matcher.go +++ b/pkg/workload/matcher.go @@ -20,7 +20,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) type Matcher interface { diff --git a/pkg/workload/statefulset/release.go b/pkg/workload/statefulset/release.go index 869060c..d7f5411 100644 --- a/pkg/workload/statefulset/release.go +++ b/pkg/workload/statefulset/release.go @@ -24,7 +24,7 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/workload/util.go b/pkg/workload/util.go index c81c769..058ec8e 100644 --- a/pkg/workload/util.go +++ b/pkg/workload/util.go @@ -24,8 +24,8 @@ import ( "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" ) diff --git a/test/e2e/builder/rollout_builder.go b/test/e2e/builder/rollout_builder.go index 3b889b2..81959a9 100644 --- a/test/e2e/builder/rollout_builder.go +++ b/test/e2e/builder/rollout_builder.go @@ -18,7 +18,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) // RolloutBuilder is a builder for Rollout diff --git a/test/e2e/builder/rolloutstrategy_builder.go b/test/e2e/builder/rolloutstrategy_builder.go index 524362e..8e5b494 100644 --- a/test/e2e/builder/rolloutstrategy_builder.go +++ b/test/e2e/builder/rolloutstrategy_builder.go @@ -21,7 +21,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) const defaultStrategyName = DefaultName diff --git a/test/e2e/collaset_test.go b/test/e2e/collaset_test.go index dc1a1b2..d064edf 100644 --- a/test/e2e/collaset_test.go +++ b/test/e2e/collaset_test.go @@ -30,8 +30,8 @@ import ( operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe/http" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload/collaset" diff --git a/test/e2e/statefulset_test.go b/test/e2e/statefulset_test.go index e7949d0..604be4c 100644 --- a/test/e2e/statefulset_test.go +++ b/test/e2e/statefulset_test.go @@ -29,8 +29,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - rolloutapi "kusionstack.io/rollout/apis/rollout" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe/http" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload/statefulset" diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index 8961365..acee29a 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -37,7 +37,7 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - rolloutv1alpha1 "kusionstack.io/rollout/apis/rollout/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/initializers" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/test/e2e/controller" From 1cc21f6d8cbf0da7cf461df7ae23f0b4592735cf Mon Sep 17 00:00:00 2001 From: zoumo Date: Sat, 19 Jul 2025 14:23:28 +0800 Subject: [PATCH 03/10] refactor: backend routing --- Makefile | 6 +- apis/rollout/v1alpha1/validation/rollout.go | 1 - .../v1alpha1/validation/rollout_test.go | 1 - .../rollout/v1alpha1/validation/rolloutrun.go | 1 - .../v1alpha1/validation/rolloutrun_test.go | 13 +- .../v1alpha1/validation/rolloutstrategy.go | 3 +- .../validation/rolloutstrategy_test.go | 57 +- .../v1alpha1/validation/traffic_topology.go | 1 - .../rollout/v1alpha1/validation/validation.go | 4 +- cmd/rollout/app/options/controller.go | 1 - cmd/rollout/import_known_versions.go | 1 - ...ollout.kusionstack.io_backendroutings.yaml | 2173 +++--- .../rollout.kusionstack.io_rolloutruns.yaml | 6648 +++++++++-------- ...lout.kusionstack.io_rolloutstrategies.yaml | 6648 +++++++++-------- go.mod | 5 +- go.sum | 8 +- pkg/backend/interface.go | 24 +- pkg/backend/service/accessor.go | 61 + pkg/backend/service/backend.go | 62 - pkg/backend/service/store.go | 76 - .../backendrouting_controller.go | 727 +- .../backendrouting_controller_suite_test.go | 181 - .../backendrouting_controller_test.go | 1133 +-- .../backendrouting/event_handler.go | 75 - pkg/controllers/backendrouting/suit_test.go | 19 + .../backendrouting/sync_context.go | 213 + .../podcanarylabel/podcanarylabel.go | 20 +- pkg/controllers/registry/backend.go | 6 +- pkg/controllers/registry/route.go | 6 +- pkg/controllers/rollout/event_handler.go | 2 +- pkg/controllers/rollout/rollout_controller.go | 6 +- pkg/controllers/rollout/utils.go | 4 +- pkg/controllers/rollout/utils_test.go | 2 +- pkg/controllers/rolloutrun/control/control.go | 4 +- pkg/controllers/rolloutrun/executor/batch.go | 2 +- .../rolloutrun/executor/batch_test.go | 2 +- pkg/controllers/rolloutrun/executor/canary.go | 49 +- .../rolloutrun/executor/context.go | 3 +- .../rolloutrun/executor/context_test.go | 1 - .../rolloutrun/executor/default.go | 4 +- .../rolloutrun/executor/default_test.go | 4 +- .../rolloutrun/executor/do_command.go | 3 +- .../rolloutrun/executor/do_hook.go | 2 +- .../rolloutrun/executor/do_hook_test.go | 1 - .../rolloutrun/executor/step_lifecycle.go | 2 +- .../rolloutrun/rolloutrun_controller.go | 6 +- .../rolloutrun/traffic/traffic_manager.go | 72 +- pkg/controllers/rolloutrun/webhook/manager.go | 1 - .../rolloutrun/webhook/probe/http/http.go | 2 +- .../webhook/probe/http/http_test.go | 2 +- pkg/controllers/rolloutrun/webhook/worker.go | 2 +- .../rolloutrun/webhook/worker_test.go | 2 +- pkg/controllers/traffictopology/adapter.go | 2 +- .../traffictopology_controller_suite_test.go | 2 +- .../traffictopology_controller_test.go | 3 +- pkg/controllers/traffictopology/types.go | 3 +- pkg/route/ingress/route.go | 163 +- pkg/route/ingress/store.go | 44 +- pkg/route/interface.go | 26 +- pkg/utils/accessor/accessor.go | 41 + .../progressinginfos/progressing_info.go | 4 +- .../progressinginfos/progressing_info_test.go | 1 - pkg/utils/slice.go | 1 - pkg/utils/slice_test.go | 1 - pkg/webhook/mutating/pod/pod_mutating.go | 2 +- .../validating/rollout/rollout_validating.go | 2 +- pkg/workload/collaset/accessor.go | 25 +- pkg/workload/collaset/release.go | 2 +- pkg/workload/info.go | 4 +- pkg/workload/interface.go | 10 +- pkg/workload/matcher.go | 1 - pkg/workload/poddecoration/accessor.go | 25 +- pkg/workload/statefulset/accessor.go | 25 +- pkg/workload/statefulset/release.go | 2 +- pkg/workload/util.go | 4 +- test/e2e/builder/rollout_builder.go | 1 - test/e2e/builder/rolloutstrategy_builder.go | 1 - test/e2e/collaset_test.go | 4 +- test/e2e/statefulset_test.go | 4 +- test/e2e/suite_test.go | 2 +- 80 files changed, 9703 insertions(+), 9054 deletions(-) create mode 100644 pkg/backend/service/accessor.go delete mode 100644 pkg/backend/service/backend.go delete mode 100644 pkg/backend/service/store.go delete mode 100644 pkg/controllers/backendrouting/backendrouting_controller_suite_test.go delete mode 100644 pkg/controllers/backendrouting/event_handler.go create mode 100644 pkg/controllers/backendrouting/suit_test.go create mode 100644 pkg/controllers/backendrouting/sync_context.go create mode 100644 pkg/utils/accessor/accessor.go diff --git a/Makefile b/Makefile index 19e0ba2..83a7c5a 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ lint: fmt $(GOLANGCI) run .PHONY: test -test: manifests generate lint envtest ## Run tests. +test: lint envtest ## Run tests. @KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./pkg/... -coverprofile cover.out @@ -67,11 +67,11 @@ e2e-test: manifests envtest ##@ Build .PHONY: build -build: test manifests generate lint ## Build manager binary. +build: lint test ## Build manager binary. go build -o bin/manager kusionstack.io/rollout/cmd/rollout .PHONY: run -run: test manifests generate lint ## Run a controller from your host. +run: lint test ## Run a controller from your host. go run kusionstack.io/rollout/cmd/rollout # If you wish built the manager image targeting other platforms you can use the --platform flag. diff --git a/apis/rollout/v1alpha1/validation/rollout.go b/apis/rollout/v1alpha1/validation/rollout.go index 608fb7b..3d19422 100644 --- a/apis/rollout/v1alpha1/validation/rollout.go +++ b/apis/rollout/v1alpha1/validation/rollout.go @@ -21,7 +21,6 @@ import ( metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/apis/rollout/v1alpha1/validation/rollout_test.go b/apis/rollout/v1alpha1/validation/rollout_test.go index d148020..b09eb82 100644 --- a/apis/rollout/v1alpha1/validation/rollout_test.go +++ b/apis/rollout/v1alpha1/validation/rollout_test.go @@ -21,7 +21,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/apis/rollout/v1alpha1/validation/rolloutrun.go b/apis/rollout/v1alpha1/validation/rolloutrun.go index 79efbcc..95cebf5 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun.go @@ -22,7 +22,6 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/validation/field" appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/apis/rollout/v1alpha1/validation/rolloutrun_test.go b/apis/rollout/v1alpha1/validation/rolloutrun_test.go index 3da32ae..9414704 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun_test.go @@ -22,7 +22,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) @@ -306,7 +305,9 @@ func TestValidateRolloutRunUpdate(t *testing.T) { obj.Spec.Canary.Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Canary.Traffic = &rolloutv1alpha1.TrafficStrategy{ HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ - Weight: ptr.To[int32](10), + CanaryHTTPRouteRule: rolloutv1alpha1.CanaryHTTPRouteRule{ + Weight: ptr.To[int32](10), + }, }, } return obj @@ -353,7 +354,9 @@ func TestValidateRolloutRunUpdate(t *testing.T) { obj.Spec.Batch.Batches[0].Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Batch.Batches[0].Traffic = &rolloutv1alpha1.TrafficStrategy{ HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ - Weight: ptr.To[int32](10), + CanaryHTTPRouteRule: rolloutv1alpha1.CanaryHTTPRouteRule{ + Weight: ptr.To[int32](10), + }, }, } return obj @@ -392,7 +395,9 @@ func TestValidateRolloutRunUpdate(t *testing.T) { obj.Spec.Batch.Batches[0].Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Batch.Batches[0].Traffic = &rolloutv1alpha1.TrafficStrategy{ HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ - Weight: ptr.To[int32](10), + CanaryHTTPRouteRule: rolloutv1alpha1.CanaryHTTPRouteRule{ + Weight: ptr.To[int32](10), + }, }, } return obj diff --git a/apis/rollout/v1alpha1/validation/rolloutstrategy.go b/apis/rollout/v1alpha1/validation/rolloutstrategy.go index d0f4258..d56ac2b 100644 --- a/apis/rollout/v1alpha1/validation/rolloutstrategy.go +++ b/apis/rollout/v1alpha1/validation/rolloutstrategy.go @@ -22,7 +22,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) @@ -144,7 +143,7 @@ func validateTrafficStrategy(traffic *rolloutv1alpha1.TrafficStrategy, fldPath * if len(traffic.HTTP.Matches) > 0 { allErrs = append(allErrs, field.Forbidden(fldPath, "weight and http rule matches cannot be specified together")) } - if traffic.HTTP.BaseTraffic != nil { + if traffic.HTTP.StableTraffic != nil { allErrs = append(allErrs, field.Forbidden(fldPath, "weight and base traffic cannot be specified together")) } } diff --git a/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go b/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go index f7d75d8..38b0e63 100644 --- a/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutstrategy_test.go @@ -22,22 +22,23 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) var validTraffic = &rolloutv1alpha1.TrafficStrategy{ HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ - Weight: ptr.To[int32](10), - HTTPRouteRule: rolloutv1alpha1.HTTPRouteRule{ - Filters: []gatewayapiv1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "foo", - Value: "bar", + CanaryHTTPRouteRule: rolloutv1alpha1.CanaryHTTPRouteRule{ + Weight: ptr.To[int32](10), + HTTPRouteRule: rolloutv1alpha1.HTTPRouteRule{ + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "foo", + Value: "bar", + }, }, }, }, @@ -49,22 +50,12 @@ var validTraffic = &rolloutv1alpha1.TrafficStrategy{ var invalidTraffic = &rolloutv1alpha1.TrafficStrategy{ HTTP: &rolloutv1alpha1.HTTPTrafficStrategy{ - Weight: ptr.To[int32](10), - HTTPRouteRule: rolloutv1alpha1.HTTPRouteRule{ - Matches: []rolloutv1alpha1.HTTPRouteMatch{ - { - Headers: []gatewayapiv1.HTTPHeaderMatch{ - { - Name: "foo", - Value: "bar", - }, - }, - }, - }, - Filters: []gatewayapiv1.HTTPRouteFilter{ - { - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ + CanaryHTTPRouteRule: rolloutv1alpha1.CanaryHTTPRouteRule{ + Weight: ptr.To[int32](10), + HTTPRouteRule: rolloutv1alpha1.HTTPRouteRule{ + Matches: []rolloutv1alpha1.HTTPRouteMatch{ + { + Headers: []gatewayapiv1.HTTPHeaderMatch{ { Name: "foo", Value: "bar", @@ -72,6 +63,18 @@ var invalidTraffic = &rolloutv1alpha1.TrafficStrategy{ }, }, }, + Filters: []gatewayapiv1.HTTPRouteFilter{ + { + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ + { + Name: "foo", + Value: "bar", + }, + }, + }, + }, + }, }, }, }, diff --git a/apis/rollout/v1alpha1/validation/traffic_topology.go b/apis/rollout/v1alpha1/validation/traffic_topology.go index 0a37902..48b0e06 100644 --- a/apis/rollout/v1alpha1/validation/traffic_topology.go +++ b/apis/rollout/v1alpha1/validation/traffic_topology.go @@ -19,7 +19,6 @@ package validation import ( apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/util/validation/field" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/apis/rollout/v1alpha1/validation/validation.go b/apis/rollout/v1alpha1/validation/validation.go index f649133..acccb61 100644 --- a/apis/rollout/v1alpha1/validation/validation.go +++ b/apis/rollout/v1alpha1/validation/validation.go @@ -7,10 +7,10 @@ import ( ) // ValidateWebhookURL validates webhook's URL. -func ValidateWebhookURL(fldPath *field.Path, URL string, forceHttps bool) field.ErrorList { +func ValidateWebhookURL(fldPath *field.Path, urlStr string, forceHttps bool) field.ErrorList { var allErrors field.ErrorList const form = "; desired format: https://host[/path]" - if u, err := url.Parse(URL); err != nil { + if u, err := url.Parse(urlStr); err != nil { allErrors = append(allErrors, field.Required(fldPath, "url must be a valid URL: "+err.Error()+form)) } else { if forceHttps && u.Scheme != "https" { diff --git a/cmd/rollout/app/options/controller.go b/cmd/rollout/app/options/controller.go index 15c34d1..c37712d 100644 --- a/cmd/rollout/app/options/controller.go +++ b/cmd/rollout/app/options/controller.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/pflag" corev1 "k8s.io/api/core/v1" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/cmd/rollout/import_known_versions.go b/cmd/rollout/import_known_versions.go index 0bc79b4..85ba542 100644 --- a/cmd/rollout/import_known_versions.go +++ b/cmd/rollout/import_known_versions.go @@ -18,7 +18,6 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml b/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml index 960a4f1..0a9001a 100644 --- a/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml +++ b/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml @@ -83,504 +83,858 @@ spec: - kind - name type: object + forkedBackends: + description: ForkedBackends + properties: + canary: + description: the temporary canary backend service name, generally it is the {originServiceName}-canary + properties: + extraLabelSelector: + additionalProperties: + type: string + description: ExtraLabelSelector defines the extra label selector for the temporary backend to select specific pods + type: object + name: + description: the temporary backend name + type: string + required: + - name + type: object + stable: + description: the temporary stable backend service name, generally it is the {originServiceName}-stable + properties: + extraLabelSelector: + additionalProperties: + type: string + description: ExtraLabelSelector defines the extra label selector for the temporary backend to select specific pods + type: object + name: + description: the temporary backend name + type: string + required: + - name + type: object + required: + - canary + - stable + type: object forwarding: description: Forwarding defines the forwarding rules for canary scenario properties: - canary: + http: properties: - http: + canary: properties: - baseTraffic: - description: BaseTraffic indicate the base traffic rule - properties: - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + backendName: + type: string + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. + + + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Conformance-levels at this level are defined based on the type of filter: + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + This filter can be used multiple times within the same rule. - Support: Core - items: + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + + Support: Core properties: - extensionRef: + add: description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - This filter can be used multiple times within the same rule. + Input: + GET /foo HTTP/1.1 + my-header: foo - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: - name - type: object - requestHeaderModifier: + x-kubernetes-list-type: map + remove: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. - - - Input: - GET /foo HTTP/1.1 - my-header: foo - - - Config: - add: - - name: "my-header" - value: "bar,baz" - - - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - - - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Config: + set: + - name: "my-header" + value: "bar" - Config: - remove: ["my-header1", "my-header3"] + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. - - - Input: - GET /foo HTTP/1.1 - my-header: foo - - - Config: - set: - - name: "my-header" - value: "bar" - - - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - - - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Support: Extended - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + + Support: Extended for Kubernetes Service + + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + + + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). + + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + + + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + + If no port is specified, the redirect port MUST be derived using the + following rules: + + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + add: + - name: "my-header" + value: "bar,baz" + + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Support: Extended for Kubernetes Service + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Defaults to "Service" when not specified. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Support: Core (Services with a type other than ExternalName) + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: + Support: Extended + 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 + path: description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Path defines a path rewrite. - Support: Core + Support: Extended properties: - hostname: + replaceFullPath: description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. - - - 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])?)*$ + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 type: string - path: - description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. - - - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. - - - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. - - - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. - - - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: |- - Port is the port to be used in the value of the `Location` - header in the response. - - - If no port is specified, the redirect port MUST be derived using the - following rules: - - - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. - - - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: - - - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. - - - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: + replacePrefixMatch: description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. - - - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Support: Extended - enum: - - http - - https + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 type: string - statusCode: - default: 302 + type: description: |- - StatusCode is the HTTP status code to be used in response. + Type defines the type of path modifier. Additional types may be + added in a future release of the API. Note that values may be added to this enum, implementations @@ -590,536 +944,274 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. - - - Support: Core enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. - - - Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. - - - Input: - GET /foo HTTP/1.1 - my-header: foo - - - Config: - add: - - name: "my-header" - value: "bar,baz" - - - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - - - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - - - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz - - - Config: - remove: ["my-header1", "my-header3"] - - - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. - - - Input: - GET /foo HTTP/1.1 - my-header: foo - - - Config: - set: - - name: "my-header" - value: "bar" - - - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - - - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: - - - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. - - - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. - - - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. - - - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. - - - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. - - - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. - - - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. - - - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. - - - Support: Extended - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + - ReplaceFullPath + - ReplacePrefixMatch type: string - path: - description: |- - Path defines a path rewrite. - - - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. - - - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. - - - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. - - - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object + required: + - type type: object - required: - - type type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - For example, take the following matches configuration: + For example, take the following matches configuration: - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - Note: The precedence of RegularExpression path matches are implementation-specific. + Note: The precedence of RegularExpression path matches are implementation-specific. - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. - Support: Core (Exact) + Support: Core (Exact) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - Support: Core (Exact, PathPrefix) + Support: Core (Exact, PathPrefix) - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Support: Extended - items: + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Support: Extended (Exact) + Support: Extended (Exact) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + weight: + description: Weight indicate how many percentage of traffic the canary pods should receive + format: int32 + maximum: 100 + minimum: 0 + type: integer + type: object + origin: + properties: + backendName: + type: string + type: object + stable: + properties: + backendName: + type: string filters: description: |- Filters define the filters that are applied to requests that match @@ -1352,6 +1444,9 @@ spec: Support: Extended + + + properties: backendRef: description: |- @@ -1454,6 +1549,46 @@ spec: required: - name type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + + + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). + + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + + + + format: int32 + maximum: 100 + minimum: 0 + type: integer required: - backendRef type: object @@ -2146,22 +2281,7 @@ spec: type: object maxItems: 8 type: array - weight: - description: Weight indicate how many percentage of traffic the canary pods should receive - format: int32 - maximum: 100 - minimum: 0 - type: integer type: object - name: - description: the temporary canary backend service name, generally it is the {originServiceName}-canary - type: string - type: object - stable: - properties: - name: - description: the temporary stable backend service name, generally it is the {originServiceName}-stable - type: string type: object type: object routes: @@ -2199,7 +2319,10 @@ spec: status: properties: backends: - description: current backends routing + description: |- + Phase indicates the current phase of this object. + Phase BackendRoutingPhase `json:"phase,omitempty"` + current backends routing properties: canary: description: Canary backend status @@ -2283,14 +2406,87 @@ spec: - name type: object type: object + conditions: + description: Conditions is the list of conditions + items: + description: |- + Condition contains details for one aspect of the current state of this API Resource. + --- + This struct is intended for direct use as an array at the field path .status.conditions. For example, + type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: "Available", "Progressing", and "Degraded" + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + + + // other fields + } + 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. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + type: array observedGeneration: description: ObservedGeneration is the most recent generation observed. format: int64 type: integer - phase: - description: Phase indicates the current phase of this object. - type: string - routeStatuses: + routes: description: route statuses items: description: BackendRouteStatus defines the status of a backend route. @@ -2304,15 +2500,166 @@ spec: cluster: description: Cluster indicates the name of cluster type: string + condition: + description: |- + Condition contains details for one aspect of the current state of this API Resource. + --- + This struct is intended for direct use as an array at the field path .status.conditions. For example, + type FooStatus struct{ + // Represents the observations of a foo's current state. + // Known .status.conditions.type are: "Available", "Progressing", and "Degraded" + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + + + // other fields + } + 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. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + forwarding: + properties: + canary: + properties: + backendName: + description: Name is the name of the referent. + type: string + conditions: + description: Backendonditions represents the current condition of an backend. + properties: + ready: + description: |- + ready indicates that this endpoint is prepared to receive traffic, + according to whatever system is managing the endpoint. A nil value + indicates an unknown state. In most cases consumers should interpret this + unknown state as ready. For compatibility reasons, ready should never be + "true" for terminating endpoints. + type: boolean + terminating: + description: |- + terminating indicates that this endpoint is terminating. A nil value + indicates an unknown state. Consumers should interpret this unknown state + to mean that the endpoint is not terminating. + type: boolean + type: object + required: + - backendName + type: object + origin: + properties: + backendName: + description: Name is the name of the referent. + type: string + conditions: + description: Backendonditions represents the current condition of an backend. + properties: + ready: + description: |- + ready indicates that this endpoint is prepared to receive traffic, + according to whatever system is managing the endpoint. A nil value + indicates an unknown state. In most cases consumers should interpret this + unknown state as ready. For compatibility reasons, ready should never be + "true" for terminating endpoints. + type: boolean + terminating: + description: |- + terminating indicates that this endpoint is terminating. A nil value + indicates an unknown state. Consumers should interpret this unknown state + to mean that the endpoint is not terminating. + type: boolean + type: object + required: + - backendName + type: object + stable: + properties: + backendName: + description: Name is the name of the referent. + type: string + conditions: + description: Backendonditions represents the current condition of an backend. + properties: + ready: + description: |- + ready indicates that this endpoint is prepared to receive traffic, + according to whatever system is managing the endpoint. A nil value + indicates an unknown state. In most cases consumers should interpret this + unknown state as ready. For compatibility reasons, ready should never be + "true" for terminating endpoints. + type: boolean + terminating: + description: |- + terminating indicates that this endpoint is terminating. A nil value + indicates an unknown state. Consumers should interpret this unknown state + to mean that the endpoint is not terminating. + type: boolean + type: object + required: + - backendName + type: object + type: object kind: description: Kind is the type of resource being referenced type: string name: description: Name is the resource name type: string - synced: - description: Synced indicates whether the backend route is synced. - type: boolean required: - kind - name diff --git a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml index 7b7c114..f008832 100644 --- a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml @@ -110,497 +110,460 @@ spec: properties: http: properties: - baseTraffic: - description: BaseTraffic indicate the base traffic rule - properties: - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Conformance-levels at this level are defined based on the type of filter: + Conformance-levels at this level are defined based on the type of filter: - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Support: Core - items: + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - This filter can be used multiple times within the same rule. + This filter can be used multiple times within the same rule. - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: + Support: Implementation-specific + properties: + group: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Input: - GET /foo HTTP/1.1 - my-header: foo + Input: + GET /foo HTTP/1.1 + my-header: foo - Config: - add: - - name: "my-header" - value: "bar,baz" + Config: + add: + - name: "my-header" + value: "bar,baz" - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Config: - remove: ["my-header1", "my-header3"] + Config: + remove: ["my-header1", "my-header3"] - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Input: - GET /foo HTTP/1.1 - my-header: foo + Input: + GET /foo HTTP/1.1 + my-header: foo - Config: - set: - - name: "my-header" - value: "bar" + Config: + set: + - name: "my-header" + value: "bar" - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Support: Extended - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Support: Extended for Kubernetes Service + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + Support: Extended for Kubernetes Service - Defaults to "Service" when not specified. + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + Defaults to "Service" when not specified. - Support: Core (Services with a type other than ExternalName) + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Support: Core (Services with a type other than ExternalName) - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. - - - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. Support: Core - maxLength: 253 + maxLength: 63 minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string - path: + port: description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. - - - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. - - - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: |- - Port is the port to be used in the value of the `Location` - header in the response. + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - If no port is specified, the redirect port MUST be derived using the - following rules: + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: + Support: Extended + properties: + replaceFullPath: description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. - - - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Support: Extended - enum: - - http - - https + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 type: string - statusCode: - default: 302 + type: description: |- - StatusCode is the HTTP status code to be used in response. + Type defines the type of path modifier. Additional types may be + added in a future release of the API. Note that values may be added to this enum, implementations @@ -610,378 +573,201 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. - - - Support: Core enum: - - 301 - - 302 - type: integer + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type type: object - responseHeaderModifier: + port: description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + Port is the port to be used in the value of the `Location` + header in the response. - Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + If no port is specified, the redirect port MUST be derived using the + following rules: - Input: - GET /foo HTTP/1.1 - my-header: foo + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Config: - add: - - name: "my-header" - value: "bar,baz" + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - Config: - remove: ["my-header1", "my-header3"] + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. - Config: - set: - - name: "my-header" - value: "bar" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Input: + GET /foo HTTP/1.1 + my-header: foo - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Config: + add: + - name: "my-header" + value: "bar,baz" - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + Config: + remove: ["my-header1", "my-header3"] - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Support: Extended - 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 - path: - description: |- - Path defines a path rewrite. - - - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. - - - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. - - - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. - - - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. - - - For example, take the following matches configuration: - - - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` - - - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: - - - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` - - - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. - - - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. - - - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: - - - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. - - - Note: The precedence of RegularExpression path matches are implementation-specific. - - - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: - - - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + Input: + GET /foo HTTP/1.1 + my-header: foo - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + Config: + set: + - name: "my-header" + value: "bar" - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. + Output: + GET /foo HTTP/1.1 + my-header: bar items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- @@ -989,42 +775,15 @@ spec: case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. - - - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. - - - Support: Core (Exact) - - - Support: Implementation-specific (RegularExpression) - - - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 @@ -1039,1133 +798,1460 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - Support: Core (Exact, PathPrefix) + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Extended (Exact) + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. - Support: Implementation-specific (RegularExpression) + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Conformance-levels at this level are defined based on the type of filter: + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Core - items: - description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - This filter can be used multiple times within the same rule. + For example, take the following matches configuration: - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: - description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - Input: - GET /foo HTTP/1.1 - my-header: foo + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - Config: - add: - - name: "my-header" - value: "bar,baz" + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - Config: - remove: ["my-header1", "my-header3"] + Note: The precedence of RegularExpression path matches are implementation-specific. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - Input: - GET /foo HTTP/1.1 - my-header: foo + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - Config: - set: - - name: "my-header" - value: "bar" + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Support: Core (Exact) - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + Support: Implementation-specific (RegularExpression) - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + Support: Core (Exact, PathPrefix) - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Support: Extended for Kubernetes Service + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - Defaults to "Service" when not specified. + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. - - - Support: Core (Services with a type other than ExternalName) + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Support: Extended (Exact) - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Implementation-specific (RegularExpression) - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + stableTraffic: + description: StableTraffic indicate the base traffic rule + properties: + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - 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 - path: - description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. + Conformance-levels at this level are defined based on the type of filter: - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + This filter can be used multiple times within the same rule. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 type: string required: - - type + - group + - kind + - name type: object - port: + requestHeaderModifier: description: |- - Port is the port to be used in the value of the `Location` - header in the response. - - - If no port is specified, the redirect port MUST be derived using the - following rules: + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Input: + GET /foo HTTP/1.1 + my-header: foo - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + Config: + add: + - name: "my-header" + value: "bar,baz" - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Config: + remove: ["my-header1", "my-header3"] - Support: Extended - enum: - - http - - https - type: string - statusCode: - default: 302 - description: |- - StatusCode is the HTTP status code to be used in response. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Input: + GET /foo HTTP/1.1 + my-header: foo - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Config: + set: + - name: "my-header" + value: "bar" - Support: Core - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Support: Extended - properties: - add: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - Input: - GET /foo HTTP/1.1 - my-header: foo + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Config: - add: - - name: "my-header" - value: "bar,baz" + Support: Extended - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - Config: - remove: ["my-header1", "my-header3"] + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended for Kubernetes Service - Config: - set: - - name: "my-header" - value: "bar" + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - - - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + Defaults to "Service" when not specified. - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Support: Core (Services with a type other than ExternalName) - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Support: Extended - 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 - path: + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: description: |- - Path defines a path rewrite. + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - Support: Extended + Support: Core properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: + hostname: description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 + 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 - type: + path: description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - For example, take the following matches configuration: + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` - - - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: - - - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + If no port is specified, the redirect port MUST be derived using the + following rules: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Note: The precedence of RegularExpression path matches are implementation-specific. + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. - Support: Core (Exact) + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Implementation-specific (RegularExpression) + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: description: |- - Type specifies how to match against the path Value. - - - Support: Core (Exact, PathPrefix) + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + Input: + GET /foo HTTP/1.1 + my-header: foo - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Config: + add: + - name: "my-header" + value: "bar,baz" - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: description: |- - Type specifies how to match against the value of the query parameter. + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - Support: Extended (Exact) + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - Support: Implementation-specific (RegularExpression) + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object weight: description: Weight indicate how many percentage of traffic the canary pods should receive format: int32 @@ -2261,477 +2347,460 @@ spec: properties: http: properties: - baseTraffic: - description: BaseTraffic indicate the base traffic rule - properties: - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Conformance-levels at this level are defined based on the type of filter: + Conformance-levels at this level are defined based on the type of filter: - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Support: Core - items: + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - This filter can be used multiple times within the same rule. + This filter can be used multiple times within the same rule. - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: + Support: Implementation-specific + properties: + group: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. - - - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. - - - Input: - GET /foo HTTP/1.1 - my-header: foo - - - Config: - add: - - name: "my-header" - value: "bar,baz" + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Input: + GET /foo HTTP/1.1 + my-header: foo - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Config: + add: + - name: "my-header" + value: "bar,baz" - Config: - remove: ["my-header1", "my-header3"] + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. - - - Input: - GET /foo HTTP/1.1 - my-header: foo + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Config: - set: - - name: "my-header" - value: "bar" + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Config: + remove: ["my-header1", "my-header3"] - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + Set overwrites the request with the given header (name, value) + before the action. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + Input: + GET /foo HTTP/1.1 + my-header: foo - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Config: + set: + - name: "my-header" + value: "bar" - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + Support: Extended - Support: Extended for Kubernetes Service + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - Defaults to "Service" when not specified. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Support: Core (Services with a type other than ExternalName) + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Support: Extended for Kubernetes Service - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Defaults to "Service" when not specified. - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - Support: Core + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. 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 - path: + namespace: description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: |- - Port is the port to be used in the value of the `Location` - header in the response. + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - If no port is specified, the redirect port MUST be derived using the - following rules: + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. - - - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + Type defines the type of path modifier. Additional types may be + added in a future release of the API. Note that values may be added to this enum, implementations @@ -2741,1582 +2810,1685 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. - - - Support: Extended enum: - - http - - https + - ReplaceFullPath + - ReplacePrefixMatch type: string - statusCode: - default: 302 - description: |- - StatusCode is the HTTP status code to be used in response. + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + If no port is specified, the redirect port MUST be derived using the + following rules: - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Support: Core - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - Input: - GET /foo HTTP/1.1 - my-header: foo + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - Config: - add: - - name: "my-header" - value: "bar,baz" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Config: - remove: ["my-header1", "my-header3"] + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - set: - - name: "my-header" - value: "bar" + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Config: + add: + - name: "my-header" + value: "bar,baz" - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Config: + remove: ["my-header1", "my-header3"] - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + Input: + GET /foo HTTP/1.1 + my-header: foo - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + Config: + set: + - name: "my-header" + value: "bar" - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Support: Extended - 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 - path: - description: |- - Path defines a path rewrite. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. - For example, take the following matches configuration: + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + For example, take the following matches configuration: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - Note: The precedence of RegularExpression path matches are implementation-specific. + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + Note: The precedence of RegularExpression path matches are implementation-specific. - Support: Core (Exact) + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - Support: Implementation-specific (RegularExpression) + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - Support: Core (Exact, PathPrefix) + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - Support: Extended - items: + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + Type specifies how to match against the value of the header. - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Support: Core (Exact) - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + Support: Implementation-specific (RegularExpression) - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - Support: Extended (Exact) + Support: Core (Exact, PathPrefix) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - Conformance-levels at this level are defined based on the type of filter: + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Support: Extended (Exact) - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + Support: Implementation-specific (RegularExpression) - Support: Core - items: - description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + stableTraffic: + description: StableTraffic indicate the base traffic rule + properties: + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - This filter can be used multiple times within the same rule. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: - description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - Input: - GET /foo HTTP/1.1 - my-header: foo + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Config: - add: - - name: "my-header" - value: "bar,baz" + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + This filter can be used multiple times within the same rule. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind - name - x-kubernetes-list-type: map - remove: + type: object + requestHeaderModifier: description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - remove: ["my-header1", "my-header3"] + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Config: + add: + - name: "my-header" + value: "bar,baz" - Input: - GET /foo HTTP/1.1 - my-header: foo + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Config: - set: - - name: "my-header" - value: "bar" + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + Config: + remove: ["my-header1", "my-header3"] - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Input: + GET /foo HTTP/1.1 + my-header: foo - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + Config: + set: + - name: "my-header" + value: "bar" - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Support: Extended for Kubernetes Service + Support: Extended - Support: Implementation-specific for any other resource + properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service + backendRef: description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + BackendRef references a resource where mirrored requests are sent. - Defaults to "Service" when not specified. + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - Support: Core (Services with a type other than ExternalName) + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Extended for Kubernetes Service - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + Defaults to "Service" when not specified. - 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 - path: - description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + Support: Core (Services with a type other than ExternalName) - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + + + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string + + format: int32 + maximum: 100 + minimum: 0 + type: integer required: - - type + - backendRef type: object - port: + requestRedirect: description: |- - Port is the port to be used in the value of the `Location` - header in the response. + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - If no port is specified, the redirect port MUST be derived using the - following rules: + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. - Support: Extended - enum: - - http - - https - type: string - statusCode: - default: 302 - description: |- - StatusCode is the HTTP status code to be used in response. + If no port is specified, the redirect port MUST be derived using the + following rules: - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: - Support: Core - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. - Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - Input: - GET /foo HTTP/1.1 - my-header: foo + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - Config: - add: - - name: "my-header" - value: "bar,baz" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - remove: ["my-header1", "my-header3"] + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Config: + add: + - name: "my-header" + value: "bar,baz" - Input: - GET /foo HTTP/1.1 - my-header: foo + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Config: - set: - - name: "my-header" - value: "bar" + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + Input: + GET /foo HTTP/1.1 + my-header: foo - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Config: + set: + - name: "my-header" + value: "bar" - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Support: Extended - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef type: string - path: + urlRewrite: description: |- - Path defines a path rewrite. + URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: - replaceFullPath: + hostname: description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 - replacePrefixMatch: + path: description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + Path defines a path rewrite. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object type: object + required: + - type type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - For example, take the following matches configuration: + For example, take the following matches configuration: - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - Note: The precedence of RegularExpression path matches are implementation-specific. + Note: The precedence of RegularExpression path matches are implementation-specific. - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. - Support: Core (Exact) + Support: Core (Exact) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - Support: Core (Exact, PathPrefix) + Support: Core (Exact, PathPrefix) - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: + Support: Extended + items: description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Support: Extended (Exact) + Support: Extended (Exact) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object weight: description: Weight indicate how many percentage of traffic the canary pods should receive format: int32 diff --git a/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml b/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml index dfa14c7..1d221c6 100644 --- a/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rolloutstrategies.yaml @@ -123,497 +123,460 @@ spec: properties: http: properties: - baseTraffic: - description: BaseTraffic indicate the base traffic rule - properties: - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Conformance-levels at this level are defined based on the type of filter: + Conformance-levels at this level are defined based on the type of filter: - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Support: Core - items: + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - This filter can be used multiple times within the same rule. + This filter can be used multiple times within the same rule. - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: + Support: Implementation-specific + properties: + group: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Input: - GET /foo HTTP/1.1 - my-header: foo + Input: + GET /foo HTTP/1.1 + my-header: foo - Config: - add: - - name: "my-header" - value: "bar,baz" + Config: + add: + - name: "my-header" + value: "bar,baz" - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Config: - remove: ["my-header1", "my-header3"] + Config: + remove: ["my-header1", "my-header3"] - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Input: - GET /foo HTTP/1.1 - my-header: foo + Input: + GET /foo HTTP/1.1 + my-header: foo - Config: - set: - - name: "my-header" - value: "bar" + Config: + set: + - name: "my-header" + value: "bar" - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Support: Extended - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Support: Extended for Kubernetes Service + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + Support: Extended for Kubernetes Service - Defaults to "Service" when not specified. + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + Defaults to "Service" when not specified. - Support: Core (Services with a type other than ExternalName) + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Support: Core (Services with a type other than ExternalName) - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. - - - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. Support: Core - maxLength: 253 + maxLength: 63 minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string - path: + port: description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. - - - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. - - - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: |- - Port is the port to be used in the value of the `Location` - header in the response. + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - If no port is specified, the redirect port MUST be derived using the - following rules: + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: + Support: Extended + properties: + replaceFullPath: description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. - - - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Support: Extended - enum: - - http - - https + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 type: string - statusCode: - default: 302 + type: description: |- - StatusCode is the HTTP status code to be used in response. + Type defines the type of path modifier. Additional types may be + added in a future release of the API. Note that values may be added to this enum, implementations @@ -623,378 +586,201 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. - - - Support: Core enum: - - 301 - - 302 - type: integer + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type type: object - responseHeaderModifier: + port: description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + Port is the port to be used in the value of the `Location` + header in the response. - Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + If no port is specified, the redirect port MUST be derived using the + following rules: - Input: - GET /foo HTTP/1.1 - my-header: foo + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Config: - add: - - name: "my-header" - value: "bar,baz" + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - Config: - remove: ["my-header1", "my-header3"] + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. - Config: - set: - - name: "my-header" - value: "bar" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Input: + GET /foo HTTP/1.1 + my-header: foo - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Config: + add: + - name: "my-header" + value: "bar,baz" - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + Config: + remove: ["my-header1", "my-header3"] - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Support: Extended - 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 - path: - description: |- - Path defines a path rewrite. - - - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. - - - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. - - - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. - - - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. - - - For example, take the following matches configuration: - - - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` - - - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: - - - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` - - - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. - - - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. - - - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: - - - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. - - - Note: The precedence of RegularExpression path matches are implementation-specific. - - - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: - - - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + Input: + GET /foo HTTP/1.1 + my-header: foo - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + Config: + set: + - name: "my-header" + value: "bar" - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. + Output: + GET /foo HTTP/1.1 + my-header: bar items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- @@ -1002,42 +788,15 @@ spec: case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. - - - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. - - - Support: Core (Exact) - - - Support: Implementation-specific (RegularExpression) - - - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 @@ -1052,1133 +811,1460 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - Support: Core (Exact, PathPrefix) + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Extended (Exact) + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. - Support: Implementation-specific (RegularExpression) + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Conformance-levels at this level are defined based on the type of filter: + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Core - items: - description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - This filter can be used multiple times within the same rule. + For example, take the following matches configuration: - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: - description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - Input: - GET /foo HTTP/1.1 - my-header: foo + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - Config: - add: - - name: "my-header" - value: "bar,baz" + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - Config: - remove: ["my-header1", "my-header3"] + Note: The precedence of RegularExpression path matches are implementation-specific. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - Input: - GET /foo HTTP/1.1 - my-header: foo + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - Config: - set: - - name: "my-header" - value: "bar" + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Support: Core (Exact) - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + Support: Implementation-specific (RegularExpression) - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + Support: Core (Exact, PathPrefix) - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Support: Extended for Kubernetes Service + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - Defaults to "Service" when not specified. + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. - - - Support: Core (Services with a type other than ExternalName) + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Support: Extended (Exact) - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Implementation-specific (RegularExpression) - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + stableTraffic: + description: StableTraffic indicate the base traffic rule + properties: + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - 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 - path: - description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. + Conformance-levels at this level are defined based on the type of filter: - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + This filter can be used multiple times within the same rule. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 type: string required: - - type + - group + - kind + - name type: object - port: + requestHeaderModifier: description: |- - Port is the port to be used in the value of the `Location` - header in the response. - - - If no port is specified, the redirect port MUST be derived using the - following rules: + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Input: + GET /foo HTTP/1.1 + my-header: foo - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + Config: + add: + - name: "my-header" + value: "bar,baz" - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Config: + remove: ["my-header1", "my-header3"] - Support: Extended - enum: - - http - - https - type: string - statusCode: - default: 302 - description: |- - StatusCode is the HTTP status code to be used in response. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Input: + GET /foo HTTP/1.1 + my-header: foo - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Config: + set: + - name: "my-header" + value: "bar" - Support: Core - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Support: Extended - properties: - add: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - Input: - GET /foo HTTP/1.1 - my-header: foo + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Config: - add: - - name: "my-header" - value: "bar,baz" + Support: Extended - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - Config: - remove: ["my-header1", "my-header3"] + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended for Kubernetes Service - Config: - set: - - name: "my-header" - value: "bar" + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - - - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + Defaults to "Service" when not specified. - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Support: Core (Services with a type other than ExternalName) - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Support: Extended - 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 - path: + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: description: |- - Path defines a path rewrite. + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - Support: Extended + Support: Core properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: + hostname: description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". - - - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. - - - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 + 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 - type: + path: description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - For example, take the following matches configuration: + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` - - - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: - - - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + If no port is specified, the redirect port MUST be derived using the + following rules: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Note: The precedence of RegularExpression path matches are implementation-specific. + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. - Support: Core (Exact) + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Support: Implementation-specific (RegularExpression) + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: description: |- - Type specifies how to match against the path Value. - - - Support: Core (Exact, PathPrefix) + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + Input: + GET /foo HTTP/1.1 + my-header: foo - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Config: + add: + - name: "my-header" + value: "bar,baz" - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + + Input: + GET /foo HTTP/1.1 + my-header: foo + + + Config: + set: + - name: "my-header" + value: "bar" + + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + + For example, take the following matches configuration: + + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + + Note: The precedence of RegularExpression path matches are implementation-specific. + + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + + Support: Core (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + + Support: Core (Exact, PathPrefix) + + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + + Support: Extended + items: description: |- - Type specifies how to match against the value of the query parameter. + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - Support: Extended (Exact) + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - Support: Implementation-specific (RegularExpression) + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + + Support: Extended (Exact) + + + Support: Implementation-specific (RegularExpression) + + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object weight: description: Weight indicate how many percentage of traffic the canary pods should receive format: int32 @@ -2312,477 +2398,460 @@ spec: properties: http: properties: - baseTraffic: - description: BaseTraffic indicate the base traffic rule - properties: - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Conformance-levels at this level are defined based on the type of filter: + Conformance-levels at this level are defined based on the type of filter: - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Support: Core - items: + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - This filter can be used multiple times within the same rule. + This filter can be used multiple times within the same rule. - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: + Support: Implementation-specific + properties: + group: description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. - - - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. - - - Input: - GET /foo HTTP/1.1 - my-header: foo - - - Config: - add: - - name: "my-header" - value: "bar,baz" + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Input: + GET /foo HTTP/1.1 + my-header: foo - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Config: + add: + - name: "my-header" + value: "bar,baz" - Config: - remove: ["my-header1", "my-header3"] + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. - - - Input: - GET /foo HTTP/1.1 - my-header: foo + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Config: - set: - - name: "my-header" - value: "bar" + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Config: + remove: ["my-header1", "my-header3"] - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + Set overwrites the request with the given header (name, value) + before the action. - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + Input: + GET /foo HTTP/1.1 + my-header: foo - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Config: + set: + - name: "my-header" + value: "bar" - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + Support: Extended - Support: Extended for Kubernetes Service + + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. - Support: Implementation-specific for any other resource - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service - description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - Defaults to "Service" when not specified. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Support: Core (Services with a type other than ExternalName) + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + Support: Extended for Kubernetes Service - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Defaults to "Service" when not specified. - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - Support: Core + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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. 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 - path: + namespace: description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: |- - Port is the port to be used in the value of the `Location` - header in the response. + + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - If no port is specified, the redirect port MUST be derived using the - following rules: + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. - - - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + Type defines the type of path modifier. Additional types may be + added in a future release of the API. Note that values may be added to this enum, implementations @@ -2792,1582 +2861,1685 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. - - - Support: Extended enum: - - http - - https + - ReplaceFullPath + - ReplacePrefixMatch type: string - statusCode: - default: 302 - description: |- - StatusCode is the HTTP status code to be used in response. + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + If no port is specified, the redirect port MUST be derived using the + following rules: - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Support: Core - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - Input: - GET /foo HTTP/1.1 - my-header: foo + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - Config: - add: - - name: "my-header" - value: "bar,baz" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Config: - remove: ["my-header1", "my-header3"] + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - Input: - GET /foo HTTP/1.1 - my-header: foo + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - set: - - name: "my-header" - value: "bar" + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Config: + add: + - name: "my-header" + value: "bar,baz" - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Config: + remove: ["my-header1", "my-header3"] - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + Input: + GET /foo HTTP/1.1 + my-header: foo - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + Config: + set: + - name: "my-header" + value: "bar" - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Support: Extended - 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 - path: - description: |- - Path defines a path rewrite. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. - For example, take the following matches configuration: + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + For example, take the following matches configuration: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - Note: The precedence of RegularExpression path matches are implementation-specific. + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + Note: The precedence of RegularExpression path matches are implementation-specific. - Support: Core (Exact) + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - Support: Implementation-specific (RegularExpression) + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - Support: Core (Exact, PathPrefix) + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - Support: Extended - items: + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: - description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + Type specifies how to match against the value of the header. - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + Support: Core (Exact) - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + Support: Implementation-specific (RegularExpression) - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - Support: Extended (Exact) + Support: Core (Exact, PathPrefix) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - filters: - description: |- - Filters define the filters that are applied to requests that match - this rule. + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - The effects of ordering of multiple behaviors are currently unspecified. - This can change in the future based on feedback during the alpha stage. + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - Conformance-levels at this level are defined based on the type of filter: + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - - ALL core filters MUST be supported by all implementations. - - Implementers are encouraged to support extended filters. - - Implementation-specific custom filters have no API guarantees across - implementations. + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Specifying the same filter multiple times is not supported unless explicitly - indicated in the filter. + Support: Extended (Exact) - All filters are expected to be compatible with each other except for the - URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation can not support other combinations of filters, they must clearly - document that limitation. In cases where incompatible or unsupported - filters are specified and cause the `Accepted` condition to be set to status - `False`, implementations may use the `IncompatibleFilters` reason to specify - this configuration error. + Support: Implementation-specific (RegularExpression) - Support: Core - items: - description: |- - HTTPRouteFilter defines processing steps that must be completed during the - request or response lifecycle. HTTPRouteFilters are meant as an extension - point to express processing that may be done in Gateway implementations. Some - examples include request or response modification, implementing - authentication strategies, rate-limiting, and traffic shaping. API - guarantee/conformance is defined based on the type of the filter. - properties: - extensionRef: - description: |- - ExtensionRef is an optional, implementation-specific extension to the - "filter" behavior. For example, resource "myroutefilter" in group - "networking.example.net"). ExtensionRef MUST NOT be used for core and - extended filters. + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + stableTraffic: + description: StableTraffic indicate the base traffic rule + properties: + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. - This filter can be used multiple times within the same rule. + The effects of ordering of multiple behaviors are currently unspecified. + This can change in the future based on feedback during the alpha stage. - Support: Implementation-specific - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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 referent. For example "HTTPRoute" or "Service". - 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. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: - description: |- - RequestHeaderModifier defines a schema for a filter that modifies request - headers. + Conformance-levels at this level are defined based on the type of filter: + + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. - Support: Core - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. - Input: - GET /foo HTTP/1.1 - my-header: foo + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation can not support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. - Config: - add: - - name: "my-header" - value: "bar,baz" + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + This filter can be used multiple times within the same rule. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind - name - x-kubernetes-list-type: map - remove: + type: object + requestHeaderModifier: description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - + RequestHeaderModifier defines a schema for a filter that modifies request + headers. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - remove: ["my-header1", "my-header3"] + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Config: + add: + - name: "my-header" + value: "bar,baz" - Input: - GET /foo HTTP/1.1 - my-header: foo + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Config: - set: - - name: "my-header" - value: "bar" + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: |- - RequestMirror defines a schema for a filter that mirrors requests. - Requests are sent to the specified destination, but responses from - that destination are ignored. + Config: + remove: ["my-header1", "my-header3"] - This filter can be used multiple times within the same rule. Note that - not all implementations will be able to support mirroring to multiple - backends. + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - Support: Extended - properties: - backendRef: - description: |- - BackendRef references a resource where mirrored requests are sent. + Input: + GET /foo HTTP/1.1 + my-header: foo - Mirrored requests must be sent only to a single destination endpoint - within this BackendRef, irrespective of how many endpoints are present - within this BackendRef. + Config: + set: + - name: "my-header" + value: "bar" - If the referent cannot be found, this BackendRef is invalid and must be - dropped from the Gateway. The controller must ensure the "ResolvedRefs" - condition on the Route status is set to `status: False` and not configure - this backend in the underlying implementation. + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If there is a cross-namespace reference to an *existing* object - that is not allowed by a ReferenceGrant, the controller must ensure the - "ResolvedRefs" condition on the Route is set to `status: False`, - with the "RefNotPermitted" reason and not configure this backend in the - underlying implementation. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. - In either error case, the Message of the `ResolvedRefs` Condition - should be used to provide more detail about the problem. + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. - Support: Extended for Kubernetes Service + Support: Extended - Support: Implementation-specific for any other resource + properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - 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: Service + backendRef: description: |- - Kind is the Kubernetes resource kind of the referent. For example - "Service". + BackendRef references a resource where mirrored requests are sent. - Defaults to "Service" when not specified. + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. - ExternalName services can refer to CNAME DNS records that may live - outside of the cluster and as such are difficult to reason about in - terms of conformance. They also may not be safe to forward to (see - CVE-2021-25740 for more information). Implementations SHOULD NOT - support ExternalName Services. + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. - Support: Core (Services with a type other than ExternalName) + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. - Support: Implementation-specific (Services with type ExternalName) - 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. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the backend. When unspecified, the local - namespace is inferred. + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. + Support: Extended for Kubernetes Service - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port specifies the destination port number to use for this resource. - Port is required when the referent is a Kubernetes Service. In this - case, the port number is the service port number, not the target port. - For other resources, destination port might be derived from the referent - resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: |- - RequestRedirect defines a schema for a filter that responds to the - request with an HTTP redirection. + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". - Support: Core - properties: - hostname: - description: |- - Hostname is the hostname to be used in the value of the `Location` - header in the response. - When empty, the hostname in the `Host` header of the request is used. + Defaults to "Service" when not specified. - 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 - path: - description: |- - Path defines parameters used to modify the path of the incoming request. - The modified path is then used to construct the `Location` header. When - empty, the request path is used as-is. + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. - Support: Extended - properties: - replaceFullPath: - description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 - type: string - replacePrefixMatch: - description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + Support: Core (Services with a type other than ExternalName) - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + + + + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string + + format: int32 + maximum: 100 + minimum: 0 + type: integer required: - - type + - backendRef type: object - port: + requestRedirect: description: |- - Port is the port to be used in the value of the `Location` - header in the response. + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. - If no port is specified, the redirect port MUST be derived using the - following rules: + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. - * If redirect scheme is not-empty, the redirect port MUST be the well-known - port associated with the redirect scheme. Specifically "http" to port 80 - and "https" to port 443. If the redirect scheme does not have a - well-known port, the listener port of the Gateway SHOULD be used. - * If redirect scheme is empty, the redirect port MUST be the Gateway - Listener port. + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. - Implementations SHOULD NOT add the port number in the 'Location' - header in the following cases: + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - * A Location header that will use HTTP (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 80. - * A Location header that will use HTTPS (whether that is determined via - the Listener protocol or the Scheme field) _and_ use port 443. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: |- - Scheme is the scheme to be used in the value of the `Location` header in - the response. When empty, the scheme of the request is used. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Scheme redirects can affect the port of the redirect, for more information, - refer to the documentation for the port field of this filter. + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. - Support: Extended - enum: - - http - - https - type: string - statusCode: - default: 302 - description: |- - StatusCode is the HTTP status code to be used in response. + If no port is specified, the redirect port MUST be derived using the + following rules: - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: - Support: Core - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: |- - ResponseHeaderModifier defines a schema for a filter that modifies response - headers. + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. - Support: Extended - properties: - add: - description: |- - Add adds the given header(s) (name, value) to the request - before the action. It appends to any existing values associated - with the header name. + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. - Input: - GET /foo HTTP/1.1 - my-header: foo + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. - Config: - add: - - name: "my-header" - value: "bar,baz" + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. - Output: - GET /foo HTTP/1.1 - my-header: foo,bar,baz - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: description: |- - Remove the given header(s) from the HTTP request before the action. The - value of Remove is a list of HTTP header names. Note that the header - names are case-insensitive (see - https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. - Input: - GET /foo HTTP/1.1 - my-header1: foo - my-header2: bar - my-header3: baz + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. - Config: - remove: ["my-header1", "my-header3"] + Input: + GET /foo HTTP/1.1 + my-header: foo - Output: - GET /foo HTTP/1.1 - my-header2: bar - items: - type: string - maxItems: 16 - type: array - x-kubernetes-list-type: set - set: - description: |- - Set overwrites the request with the given header (name, value) - before the action. + Config: + add: + - name: "my-header" + value: "bar,baz" - Input: - GET /foo HTTP/1.1 - my-header: foo + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Config: - set: - - name: "my-header" - value: "bar" + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). - Output: - GET /foo HTTP/1.1 - my-header: bar - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz - If multiple entries specify equivalent header names, the first entry with - an equivalent name MUST be considered for a match. Subsequent entries - with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 + Config: + remove: ["my-header1", "my-header3"] + + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - type: - description: |- - Type identifies the type of filter to apply. As with other API fields, - types are classified into three conformance levels: + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. - - Core: Filter types and their corresponding configuration defined by - "Support: Core" in this package, e.g. "RequestHeaderModifier". All - implementations must support core filters. + Input: + GET /foo HTTP/1.1 + my-header: foo - - Extended: Filter types and their corresponding configuration defined by - "Support: Extended" in this package, e.g. "RequestMirror". Implementers - are encouraged to support extended filters. + Config: + set: + - name: "my-header" + value: "bar" - - Implementation-specific: Filters that are defined and supported by - specific vendors. - In the future, filters showing convergence in behavior across multiple - implementations will be considered for inclusion in extended or core - conformance levels. Filter-specific configuration for such filters - is specified using the ExtensionRef field. `Type` should be set to - "ExtensionRef" for custom filters. + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - Implementers are encouraged to define custom implementation types to - extend the core API with implementation-specific behavior. + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: - If a reference to a custom filter type cannot be resolved, the filter - MUST NOT be skipped. Instead, requests that would have been processed by - that filter MUST receive a HTTP error response. + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: |- - URLRewrite defines a schema for a filter that modifies a request during forwarding. + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. - Support: Extended - properties: - hostname: - description: |- - Hostname is the value to be used to replace the Host header value during - forwarding. + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. - Support: Extended - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef type: string - path: + urlRewrite: description: |- - Path defines a path rewrite. + URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: - replaceFullPath: + hostname: description: |- - ReplaceFullPath specifies the value with which to replace the full path - of a request during a rewrite or redirect. - maxLength: 1024 + Hostname is the value to be used to replace the Host header value during + forwarding. + + + Support: Extended + 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 - replacePrefixMatch: + path: description: |- - ReplacePrefixMatch specifies the value with which to replace the prefix - match of a request during a rewrite or redirect. For example, a request - to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch - of "/xyz" would be modified to "/xyz/bar". + Path defines a path rewrite. - Note that this matches the behavior of the PathPrefix match type. This - matches full path elements. A path element refers to the list of labels - in the path split by the `/` separator. When specified, a trailing `/` is - ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all - match the prefix `/abc`, but the path `/abcd` would not. + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". - ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. - Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in - the implementation setting the Accepted Condition for the Route to `status: False`. + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. - Request Path | Prefix Match | Replace Prefix | Modified Path - -------------|--------------|----------------|---------- - /foo/bar | /foo | /xyz | /xyz/bar - /foo/bar | /foo | /xyz/ | /xyz/bar - /foo/bar | /foo/ | /xyz | /xyz/bar - /foo/bar | /foo/ | /xyz/ | /xyz/bar - /foo | /foo | /xyz | /xyz - /foo/ | /foo | /xyz | /xyz/ - /foo/bar | /foo | | /bar - /foo/ | /foo | | / - /foo | /foo | | / - /foo/ | /foo | / | / - /foo | /foo | / | / - maxLength: 1024 - type: string - type: - description: |- - Type defines the type of path modifier. Additional types may be - added in a future release of the API. + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. - Note that values may be added to this enum, implementations - must ensure that unknown values will not cause a crash. + Request Path | Prefix Match | Replace Prefix | Modified Path + -------------|--------------|----------------|---------- + /foo/bar | /foo | /xyz | /xyz/bar + /foo/bar | /foo | /xyz/ | /xyz/bar + /foo/bar | /foo/ | /xyz | /xyz/bar + /foo/bar | /foo/ | /xyz/ | /xyz/bar + /foo | /foo | /xyz | /xyz + /foo/ | /foo | /xyz | /xyz/ + /foo/bar | /foo | | /bar + /foo/ | /foo | | / + /foo | /foo | | / + /foo/ | /foo | / | / + /foo | /foo | / | / + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. - Unknown values here must result in the implementation setting the - Accepted Condition for the Route to `status: False`, with a - Reason of `UnsupportedValue`. - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object type: object + required: + - type type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - description: |- - Matches define conditions used for matching the rule against incoming - HTTP requests. Each match is independent, i.e. this rule will be matched - if **any** one of the matches is satisfied. + maxItems: 16 + type: array + matches: + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. - For example, take the following matches configuration: + For example, take the following matches configuration: - ``` - matches: - - path: - value: "/foo" - headers: - - name: "version" - value: "v2" - - path: - value: "/v2/foo" - ``` + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` - For a request to match against this rule, a request must satisfy - EITHER of the two conditions: + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: - - path prefixed with `/foo` AND contains the header `version: v2` - - path prefix of `/v2/foo` + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` - See the documentation for HTTPRouteMatch on how to specify multiple - match conditions that should be ANDed together. + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. - If no matches are specified, the default is a prefix - path match on "/", which has the effect of matching every - HTTP request. + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. - Proxy or Load Balancer routing configuration generated from HTTPRoutes - MUST prioritize matches based on the following criteria, continuing on - ties. Across all rules specified on applicable Routes, precedence must be - given to the match having: + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: - * "Exact" path match. - * "Prefix" path match with largest number of characters. - * Method match. - * Largest number of header matches. - * Largest number of query param matches. + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. - Note: The precedence of RegularExpression path matches are implementation-specific. + Note: The precedence of RegularExpression path matches are implementation-specific. - If ties still exist across multiple Routes, matching precedence MUST be - determined in order of the following criteria, continuing on ties: + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: - * The oldest Route based on creation timestamp. - * The Route appearing first in alphabetical order by - "{namespace}/{name}". + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". - If ties still exist within an HTTPRoute, matching precedence MUST be granted - to the FIRST matching rule (in list order) with a match meeting the above - criteria. + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. - When no rules matching a request have been successfully attached to the - parent a request is coming from, a HTTP 404 status code MUST be returned. - items: - properties: - headers: - description: |- - Headers specifies HTTP request header matchers. Multiple match values are - ANDed together, meaning, a request must match all the specified headers - to select the route. - items: - description: |- - HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request - headers. - properties: - name: + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: description: |- - Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). - If multiple entries specify equivalent header names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent header name MUST be ignored. Due to the - case-insensitivity of header names, "foo" and "Foo" are considered - equivalent. + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. - When a header is repeated in an HTTP request, it is - implementation-specific behavior as to how this is represented. - Generally, proxies should follow the guidance from the RFC: - https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding - processing a repeated header, with special handling for "Set-Cookie". - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the header. + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. - Support: Core (Exact) + Support: Core (Exact) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) - Since RegularExpression HeaderMatchType has implementation-specific - conformance, implementations can support POSIX, PCRE or any other dialects - of regular expressions. Please read the implementation's documentation to - determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - path: - description: Path specifies a HTTP request path matcher. - properties: - type: - default: PathPrefix - description: |- - Type specifies how to match against the path Value. + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + path: + description: Path specifies a HTTP request path matcher. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. - Support: Core (Exact, PathPrefix) + Support: Core (Exact, PathPrefix) - Support: Implementation-specific (RegularExpression) - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: |- - QueryParams specifies HTTP query parameter matchers. Multiple match - values are ANDed together, meaning, a request must match all the - specified query parameters to select the route. + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. - Support: Extended - items: - description: |- - HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP - query parameters. - properties: - name: + Support: Extended + items: description: |- - Name is the name of the HTTP query param to be matched. This must be an - exact string match. (See - https://tools.ietf.org/html/rfc7230#section-2.7.3). + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). - If multiple entries specify equivalent query param names, only the first - entry with an equivalent name MUST be considered for a match. Subsequent - entries with an equivalent query param name MUST be ignored. + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. - If a query param is repeated in an HTTP request, the behavior is - purposely left undefined, since different data planes have different - capabilities. However, it is *recommended* that implementations should - match against the first value of the param if the data plane supports it, - as this behavior is expected in other load balancing contexts outside of - the Gateway API. + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. - Users SHOULD NOT route traffic based on repeated query params to guard - themselves against potential differences in the implementations. - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: |- - Type specifies how to match against the value of the query parameter. + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. - Support: Extended (Exact) + Support: Extended (Exact) - Support: Implementation-specific (RegularExpression) + Support: Implementation-specific (RegularExpression) - Since RegularExpression QueryParamMatchType has Implementation-specific - conformance, implementations can support POSIX, PCRE or any other - dialects of regular expressions. Please read the implementation's - documentation to determine the supported dialect. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object weight: description: Weight indicate how many percentage of traffic the canary pods should receive format: int32 diff --git a/go.mod b/go.mod index 52fd8f3..1991575 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v1.22.2 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 - kusionstack.io/kube-api v0.6.7-0.20250715075952-2aa7e2e576af + kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6 kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 kusionstack.io/resourceconsist v0.0.2 sigs.k8s.io/controller-runtime v0.20.4 @@ -75,7 +75,7 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/samber/lo v1.47.0 github.com/spf13/afero v1.11.0 // indirect - go.uber.org/multierr v1.11.0 + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.39.0 // indirect @@ -138,4 +138,5 @@ replace ( k8s.io/system-validators => k8s.io/system-validators v1.5.0 k8s.io/utils => k8s.io/utils v0.0.0-20240102154912-e7106e64919e sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.10.3 + sigs.k8s.io/gateway-api => sigs.k8s.io/gateway-api v1.2.0 ) diff --git a/go.sum b/go.sum index e14b56b..a3337ce 100644 --- a/go.sum +++ b/go.sum @@ -1018,8 +1018,8 @@ k8s.io/sample-apiserver v0.22.2/go.mod h1:h+/DIV5EmuNq4vfPr5TSXy9mIBVXXlPAKQMPbj k8s.io/system-validators v1.5.0/go.mod h1:bPldcLgkIUK22ALflnsXk8pvkTEndYdNuaHH6gRrl0Q= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -kusionstack.io/kube-api v0.6.7-0.20250715075952-2aa7e2e576af h1:i8Qxd6NIH7o6cLgt8usKnoFNOKrqgxSbPK2zfVoah1A= -kusionstack.io/kube-api v0.6.7-0.20250715075952-2aa7e2e576af/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= +kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6 h1:ZXP+K55y4j9SKmLr8TMTgza5w1Zeu5Fmevk4MrTvFSQ= +kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 h1:HYE6Wa8EzSlA6UmaTLtNKUgkB2mmasp6Ul69d3/SpK0= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6/go.mod h1:5Uy3GCJ1JEGqZw/Sp/uVnHBJN1t9wjY6USPSZ9s4idk= kusionstack.io/resourceconsist v0.0.2 h1:gf+c/LOMsiKoVR+GLzOomw8qcUbZbPckQLczZllNdVM= @@ -1034,8 +1034,8 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.10.3 h1:s5Ttmw/B4AuIbwrXD3sfBkXwnPMMWrqpVj4WRt1dano= sigs.k8s.io/controller-runtime v0.10.3/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= -sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= -sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= +sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8= +sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs= sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go= diff --git a/pkg/backend/interface.go b/pkg/backend/interface.go index 81495ff..3b5f349 100644 --- a/pkg/backend/interface.go +++ b/pkg/backend/interface.go @@ -15,25 +15,15 @@ package backend import ( - "context" - - "k8s.io/apimachinery/pkg/runtime/schema" + "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" + + "kusionstack.io/rollout/pkg/utils/accessor" ) -type IBackend interface { - GetBackendObject() client.Object - // todo: discussion: maybe Fork can be replaced by Create/Delete, and using Create/Delete to check if ready or deleted - ForkStable(stableName string) client.Object - ForkCanary(canaryName string) client.Object -} +type InClusterBackend interface { + accessor.ObjectAccessor -type Store interface { - GroupVersionKind() schema.GroupVersionKind - // NewObject returns a new instance of the backend type - NewObject() client.Object - // Wrap get a client.Object and returns a backend interface - Wrap(cluster string, obj client.Object) (IBackend, error) - // Get returns a wrapped backend interface - Get(ctx context.Context, cluster, namespace, name string) (IBackend, error) + // Fork returns a new object with the given backend. + Fork(original client.Object, config v1alpha1.ForkedBackend) client.Object } diff --git a/pkg/backend/service/accessor.go b/pkg/backend/service/accessor.go new file mode 100644 index 0000000..8fd1136 --- /dev/null +++ b/pkg/backend/service/accessor.go @@ -0,0 +1,61 @@ +// Copyright 2023 The KusionStack 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 service + +import ( + "maps" + + corev1 "k8s.io/api/core/v1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "kusionstack.io/rollout/pkg/backend" + "kusionstack.io/rollout/pkg/utils/accessor" +) + +var GVK = corev1.SchemeGroupVersion.WithKind("Service") + +var _ backend.InClusterBackend = &accessorImpl{} + +type accessorImpl struct { + accessor.ObjectAccessor +} + +func New() backend.InClusterBackend { + return &accessorImpl{ + ObjectAccessor: accessor.NewObjectAccessor(GVK, &corev1.Service{}, &corev1.ServiceList{}), + } +} + +func (s *accessorImpl) Fork(original client.Object, config rolloutv1alpha1.ForkedBackend) client.Object { + obj := original.(*corev1.Service) + forkedbackend := &corev1.Service{} + forkedbackend.Name = config.Name + forkedbackend.Namespace = obj.Namespace + forkedbackend.Spec.Ports = obj.Spec.Ports + forkedbackend.Spec.Type = obj.Spec.Type + forkedbackend.Spec.Selector = obj.Spec.Selector + if forkedbackend.Spec.Selector == nil { + forkedbackend.Spec.Selector = make(map[string]string) + } + maps.Copy(forkedbackend.Spec.Selector, config.ExtraLabelSelector) + if forkedbackend.Labels == nil { + forkedbackend.Labels = make(map[string]string) + } + maps.Copy(forkedbackend.Labels, config.ExtraLabelSelector) + forkedbackend.Labels[rolloutapi.LabelTemporaryResource] = "true" + return forkedbackend +} diff --git a/pkg/backend/service/backend.go b/pkg/backend/service/backend.go deleted file mode 100644 index e08f4d4..0000000 --- a/pkg/backend/service/backend.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2023 The KusionStack 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 service - -import ( - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - "kusionstack.io/kube-api/rollout" - "kusionstack.io/rollout/pkg/backend" -) - -var GVK = corev1.SchemeGroupVersion.WithKind("Service") - -type serviceBackend struct { - client client.Client - obj *corev1.Service -} - -var _ backend.IBackend = &serviceBackend{} - -func (s *serviceBackend) GetBackendObject() client.Object { - return s.obj -} - -func (s *serviceBackend) ForkCanary(canaryName string) client.Object { - canaryBackend := &corev1.Service{} - canaryBackend.Name = canaryName - canaryBackend.Namespace = s.obj.Namespace - canaryBackend.Spec.Ports = s.obj.Spec.Ports - canaryBackend.Spec.Selector = s.obj.Spec.Selector - if canaryBackend.Spec.Selector == nil { - canaryBackend.Spec.Selector = make(map[string]string) - } - canaryBackend.Spec.Selector[rollout.LabelTrafficRevision] = rollout.LabelValueTrafficRevisionCanary - return canaryBackend -} - -func (s *serviceBackend) ForkStable(stableName string) client.Object { - stableBackend := &corev1.Service{} - stableBackend.Name = stableName - stableBackend.Namespace = s.obj.Namespace - stableBackend.Spec.Ports = s.obj.Spec.Ports - stableBackend.Spec.Selector = s.obj.Spec.Selector - if stableBackend.Spec.Selector == nil { - stableBackend.Spec.Selector = make(map[string]string) - } - stableBackend.Spec.Selector[rollout.LabelTrafficRevision] = rollout.LabelValueTrafficRevisionBase - return stableBackend -} diff --git a/pkg/backend/service/store.go b/pkg/backend/service/store.go deleted file mode 100644 index cb535ea..0000000 --- a/pkg/backend/service/store.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2023 The KusionStack 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 service - -import ( - "context" - "fmt" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "kusionstack.io/kube-utils/multicluster/clusterinfo" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - - "kusionstack.io/rollout/pkg/backend" -) - -type SvcStore struct { - client client.Client -} - -func NewStorage(mgr manager.Manager) backend.Store { - return &SvcStore{ - client: mgr.GetClient(), - } -} - -func (s *SvcStore) GroupVersionKind() schema.GroupVersionKind { - return GVK -} - -func (s *SvcStore) NewObject() client.Object { - return &corev1.Service{} -} - -func (s *SvcStore) NewObjectList() client.ObjectList { - return &corev1.ServiceList{} -} - -func (s *SvcStore) Wrap(cluster string, obj client.Object) (backend.IBackend, error) { - svc, ok := obj.(*corev1.Service) - if !ok { - return nil, fmt.Errorf("not Service") - } - return &serviceBackend{ - client: s.client, - obj: svc, - }, nil -} - -func (s *SvcStore) Get(ctx context.Context, cluster, namespace, name string) (backend.IBackend, error) { - var svc corev1.Service - err := s.client.Get(clusterinfo.WithCluster(ctx, cluster), types.NamespacedName{ - Namespace: namespace, - Name: name, - }, &svc) - if err != nil { - return nil, err - } - return s.Wrap(cluster, &svc) -} - -var _ backend.Store = &SvcStore{} diff --git a/pkg/controllers/backendrouting/backendrouting_controller.go b/pkg/controllers/backendrouting/backendrouting_controller.go index 94087d8..b07c58e 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller.go +++ b/pkg/controllers/backendrouting/backendrouting_controller.go @@ -17,22 +17,28 @@ package backendrouting import ( "context" "fmt" - "reflect" + "time" - "go.uber.org/multierr" + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + clientutil "kusionstack.io/kube-utils/client" + "kusionstack.io/kube-utils/controller/expectations" "kusionstack.io/kube-utils/controller/mixin" "kusionstack.io/kube-utils/multicluster/clusterinfo" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/backend" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/route" @@ -46,6 +52,8 @@ type BackendRoutingReconciler struct { *mixin.ReconcilerMixin backendRegistry registry.BackendRegistry routeRegistry registry.RouteRegistry + + rvExpectation expectations.ResourceVersionExpectationInterface } func NewReconciler(mgr manager.Manager, backendRegistry registry.BackendRegistry, routeRegistry registry.RouteRegistry) *BackendRoutingReconciler { @@ -53,6 +61,7 @@ func NewReconciler(mgr manager.Manager, backendRegistry registry.BackendRegistry ReconcilerMixin: mixin.NewReconcilerMixin(ControllerName, mgr), backendRegistry: backendRegistry, routeRegistry: routeRegistry, + rvExpectation: expectations.NewResourceVersionExpectation(), } } @@ -65,7 +74,7 @@ func (b *BackendRoutingReconciler) SetupWithManager(mgr manager.Manager) error { } return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1.BackendRouting{}, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})). + For(&rolloutv1alpha1.BackendRouting{}, builder.WithPredicates(predicate.ResourceVersionChangedPredicate{})). Complete(b) } @@ -76,503 +85,365 @@ func (b *BackendRoutingReconciler) SetupWithManager(mgr manager.Manager) error { //+kubebuilder:rbac:groups="networking.k8s.io",resources=ingresses,verbs=get;list;watch;create;update;patch;delete func (b *BackendRoutingReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { - br := &v1alpha1.BackendRouting{} - err := b.Client.Get(clusterinfo.WithCluster(ctx, clusterinfo.Fed), types.NamespacedName{ - Name: request.Name, - Namespace: request.Namespace, - }, br) + key := request.String() + logger := b.Logger.WithValues("backendrouting", key) + // set logger into context + ctx = logr.NewContext(ctx, logger) + logger.V(4).Info("started reconciling backendrouting") + defer logger.V(4).Info("finished reconciling backendrouting") + + obj := &rolloutv1alpha1.BackendRouting{} + err := b.Client.Get(clusterinfo.WithCluster(ctx, clusterinfo.Fed), request.NamespacedName, obj) if err != nil { - if errors.IsNotFound(err) { - return reconcile.Result{}, nil - } - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } // todo: finalizers' management - if br.GetDeletionTimestamp() != nil { - return b.reconcileTerminatingBackendRouting(ctx, br) + // terminating + if obj.GetDeletionTimestamp() != nil { + return b.reconcileTerminatingBackendRouting(ctx, obj) } - if br.Spec.TrafficType == v1alpha1.MultiClusterTrafficType { - return b.reconcileMultiClusterType(ctx, br) + // check resourceVersion expectation + if !b.satisfiedExpectations(ctx, obj) { + return ctrl.Result{}, nil } - // InClusterTrafficType - if br.Spec.Forwarding != nil { - // todo webhook should reject adding spec.forwarding if backendrouting.status.phase is not ready? - return b.reconcileInClusterWithForwarding(ctx, br) - } + syncCtx, err := b.initSyncContext(ctx, obj) - return b.reconcileInClusterWithoutForwarding(ctx, br) -} + if err == nil { + switch obj.Spec.TrafficType { + case rolloutv1alpha1.InClusterTrafficType: + err = b.syncInCluster(ctx, syncCtx) + case rolloutv1alpha1.MultiClusterTrafficType: + // TODO: implement multi cluster traffic type + } + } -func (b *BackendRoutingReconciler) reconcileTerminatingBackendRouting(_ context.Context, _ *v1alpha1.BackendRouting) (reconcile.Result, error) { - // todo - return reconcile.Result{}, nil -} + if err != nil { + logger.Error(err, "failed to reconcile backendrouting") + } -func (b *BackendRoutingReconciler) reconcileInClusterWithoutForwarding(ctx context.Context, br *v1alpha1.BackendRouting) (reconcile.Result, error) { - backendsStatuses := br.Status.Backends - // todo: discussion: if canary backend still exist, should we clean it? - if backendsStatuses.Canary.Name != "" { - return reconcile.Result{}, fmt.Errorf("canary backend still exist without forwarding spec") - } - - routesStatuses := br.Status.RouteStatuses - phase := br.Status.Phase - - needUpdateStatus := false - if br.Status.ObservedGeneration != br.Generation { - needUpdateStatus = true - } - - // clean stable backends and routes - if backendsStatuses.Stable.Name != "" { - if !ptr.Deref(backendsStatuses.Stable.Conditions.Terminating, false) { - // not deleting, do backend delete, change route's backend first - var routeBackendChangeErr []error - for i, currentRoute := range routesStatuses { - iRoute, err := b.getRoute(ctx, br.Namespace, currentRoute.CrossClusterObjectReference) - if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - err = iRoute.ChangeBackend(ctx, route.BackendChangeDetail{ - Src: backendsStatuses.Stable.Name, - Dst: backendsStatuses.Origin.Name, - Kind: br.Spec.Backend.Kind, - ApiVersion: br.Spec.Backend.APIVersion, - }) - if err != nil { - routeBackendChangeErr = append(routeBackendChangeErr, err) - } - // todo: discussion - // only changed by route spec here, we haven't check if it synced, - // and actually we can't delete stable backend if route not changed from stable -> origin - currentRoute.Synced = false - routesStatuses[i] = currentRoute - } + // update status firstly + updateStatusErr := b.updateStatusOnly(ctx, syncCtx) + if updateStatusErr != nil { + return reconcile.Result{}, updateStatusErr + } - if len(routeBackendChangeErr) > 0 { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, multierr.Combine(routeBackendChangeErr...)) - } + // NOTE: we need to use IsStatusConditionTrue here rather than IsStatusConditionFalse + // to make sure backendrouting is not ready. + if !meta.IsStatusConditionTrue(syncCtx.Object.Status.Conditions, rolloutv1alpha1.BackendRoutingReady) { + return reconcile.Result{RequeueAfter: 5 * time.Second}, nil + } - stableBackend, err := b.getBackend(ctx, br, backendsStatuses.Stable.Name) - if err != nil { - if !errors.IsNotFound(err) { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - } else { - if stableBackend.GetBackendObject().GetDeletionTimestamp() == nil { - err = b.Client.Delete(clusterinfo.WithCluster(ctx, br.Spec.Backend.Cluster), stableBackend.GetBackendObject()) - if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - } - } + return reconcile.Result{}, err +} - terminating := true - backendsStatuses.Stable.Conditions.Terminating = &terminating +func (r *BackendRoutingReconciler) satisfiedExpectations(ctx context.Context, obj client.Object) bool { + key := clientutil.ObjectKeyString(obj) + logger := logr.FromContextOrDiscard(ctx) + if !r.rvExpectation.SatisfiedExpectations(key, obj.GetResourceVersion()) { + logger.Info("object does not statisfy resourceVersion expectation, skip reconciling") + return false + } + return true +} - phase = v1alpha1.RouteUpgrading - needUpdateStatus = true - } else { - // deleting, check deleted - var routeBackendChangeErr []error - for _, currentRoute := range routesStatuses { - iRoute, err := b.getRoute(ctx, br.Namespace, currentRoute.CrossClusterObjectReference) - if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - err = iRoute.ChangeBackend(ctx, route.BackendChangeDetail{ - Src: backendsStatuses.Stable.Name, - Dst: backendsStatuses.Origin.Name, - Kind: br.Spec.Backend.Kind, - ApiVersion: br.Spec.Backend.APIVersion, - }) - if err != nil { - routeBackendChangeErr = append(routeBackendChangeErr, err) - } - // todo: discussion - // only changed by route spec here, we haven't check if it synced, - // and actually we can't delete stable backend if route not changed from stable -> origin - } - // route not synced yet - if len(routeBackendChangeErr) > 0 { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, multierr.Combine(routeBackendChangeErr...)) - } +func (r *BackendRoutingReconciler) initSyncContext(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) (*syncContext, error) { + syncCtx := &syncContext{ + Object: obj, + Routes: make([]route.RouteControl, len(obj.Spec.Routes)), + } + syncCtx.Initialize() - _, err := b.getBackend(ctx, br, backendsStatuses.Stable.Name) - if !errors.IsNotFound(err) { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, fmt.Errorf("stable backend not deleted yet")) - } + // find origin backend - // finished - backendsStatuses.Stable = v1alpha1.BackendStatus{} - for i, currentRoute := range routesStatuses { - currentRoute.Synced = true - routesStatuses[i] = currentRoute - } - phase = v1alpha1.Ready - needUpdateStatus = true - } + backend, backendObj, err := r.findBackend(ctx, obj, obj.Spec.Backend.Name) + if err != nil { + syncCtx.NewStatus.Backends.Origin.Conditions.Ready = ptr.To(false) + return syncCtx, err } else { - // check backend ready(now just check exist) - originBackend, err := b.getBackend(ctx, br, br.Spec.Backend.Name) + syncCtx.NewStatus.Backends.Origin.Conditions.Ready = ptr.To(true) + } + + syncCtx.BackendInterface = backend + syncCtx.BackendObject = backendObj + + for i, routeSpec := range obj.Spec.Routes { + rctl, err := r.findRoute(ctx, obj.Namespace, routeSpec) if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - if backendsStatuses.Origin.Name != originBackend.GetBackendObject().GetName() { - needUpdateStatus = true - backendsStatuses.Origin.Name = originBackend.GetBackendObject().GetName() - conditionTrue := true - backendsStatuses.Origin.Conditions.Ready = &conditionTrue - } - // todo maybe we should check route -> origin? - routesStatusesCur := make([]v1alpha1.BackendRouteStatus, len(br.Spec.Routes)) - for idx, curRoute := range br.Spec.Routes { - routesStatusesCur[idx] = v1alpha1.BackendRouteStatus{ - CrossClusterObjectReference: v1alpha1.CrossClusterObjectReference{ - ObjectTypeRef: v1alpha1.ObjectTypeRef{ - APIVersion: curRoute.APIVersion, - Kind: curRoute.Kind, - }, - CrossClusterObjectNameReference: v1alpha1.CrossClusterObjectNameReference{ - Cluster: curRoute.Cluster, - Name: curRoute.Name, - }, - }, - Synced: true, - } + syncCtx.setRouteCondition(i, metav1.ConditionUnknown, "Unknown", err.Error()) + return syncCtx, err } - if !reflect.DeepEqual(routesStatuses, routesStatusesCur) { - routesStatuses = routesStatusesCur - needUpdateStatus = true + if syncCtx.NewStatus.Routes[i].Condition.Status == metav1.ConditionUnknown { + syncCtx.setRouteCondition(i, metav1.ConditionTrue, "RouteFound", "") } - if phase != v1alpha1.Ready { - phase = v1alpha1.Ready - needUpdateStatus = true - } - } - - if needUpdateStatus { - return reconcile.Result{}, b.updateBackendRoutingStatus(ctx, br, backendsStatuses, routesStatuses, phase) + syncCtx.Routes[i] = rctl } - return reconcile.Result{}, nil + return syncCtx, nil } -func (b *BackendRoutingReconciler) reconcileInClusterWithForwarding(ctx context.Context, br *v1alpha1.BackendRouting) (reconcile.Result, error) { - if br.Spec.Forwarding.Canary.Name != "" { - return reconcile.Result{}, b.ensureCanaryAdd(ctx, br) - } else { - // no canary, which means only has stable - // if status has canary, delete it and update route - if br.Status.Backends.Canary.Name != "" { - return reconcile.Result{}, b.ensureCanaryRemove(ctx, br) - } else { - needUpdateStatus := false - if br.Status.ObservedGeneration != br.Generation { - needUpdateStatus = true - } - backendsStatuses := br.Status.Backends - routesStatuses := br.Status.RouteStatuses - if len(routesStatuses) == 0 { - routesStatuses = make([]v1alpha1.BackendRouteStatus, len(br.Spec.Routes)) - } - phase := br.Status.Phase - - // if status hasn't canary, check stable ready, check route -> stable - _, err := b.getBackend(ctx, br, br.Spec.Forwarding.Stable.Name) - if err != nil { - if errors.IsNotFound(err) { - // stable not create, do create - originBackend, err := b.getBackend(ctx, br, br.Spec.Backend.Name) - if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - stableForked := originBackend.ForkStable(br.Spec.Forwarding.Stable.Name) - err = b.Client.Create(clusterinfo.WithCluster(ctx, br.Spec.Backend.Cluster), stableForked) - if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - } else { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - } - - if !ptr.Deref(backendsStatuses.Stable.Conditions.Ready, false) { - needUpdateStatus = true - conditionTrue := true - backendsStatuses.Stable.Conditions.Ready = &conditionTrue - backendsStatuses.Stable.Name = br.Spec.Forwarding.Stable.Name - } - - var routeBackendChangeErr []error - for idx, routeSpec := range br.Spec.Routes { - iRoute, err := b.getRoute(ctx, br.Namespace, routeSpec) - if err != nil { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, err) - } - err = iRoute.ChangeBackend(ctx, route.BackendChangeDetail{ - Src: br.Spec.Backend.Name, - Dst: br.Spec.Forwarding.Stable.Name, - Kind: br.Spec.Backend.Kind, - ApiVersion: br.Spec.Backend.APIVersion, - }) - if err != nil { - if routesStatuses[idx].Synced { - routesStatuses[idx].Synced = false - needUpdateStatus = true - } - routeBackendChangeErr = append(routeBackendChangeErr, err) - continue - } - if !routesStatuses[idx].Synced { - routesStatuses[idx].Synced = true - needUpdateStatus = true - } - } +func (b *BackendRoutingReconciler) updateStatusOnly(ctx context.Context, syncCtx *syncContext) error { + logger := logr.FromContextOrDiscard(ctx) + newStatus := syncCtx.Status() + if equality.Semantic.DeepEqual(syncCtx.Object.Status, newStatus) { + return nil + } + _, err := clientutil.UpdateOnConflict(ctx, b.Client, b.Client.Status(), syncCtx.Object, func(in *rolloutv1alpha1.BackendRouting) error { + in.Status = newStatus + return nil + }) + if err != nil { + logger.Error(err, "failed to update status", "status", newStatus) + return err + } - if len(routeBackendChangeErr) > 0 { - return reconcile.Result{}, b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.BackendUpgrading, multierr.Combine(routeBackendChangeErr...)) - } + logger.V(4).Info("backendRouting status updated") + key := clientutil.ObjectKeyString(syncCtx.Object) + b.rvExpectation.ExpectUpdate(key, syncCtx.Object.ResourceVersion) // nolint + return nil +} - if phase != v1alpha1.Ready { - phase = v1alpha1.Ready - needUpdateStatus = true - } - if needUpdateStatus { - return reconcile.Result{}, b.updateBackendRoutingStatus(ctx, br, backendsStatuses, routesStatuses, phase) - } - } - } +func (b *BackendRoutingReconciler) reconcileTerminatingBackendRouting(_ context.Context, _ *rolloutv1alpha1.BackendRouting) (reconcile.Result, error) { + // todo return reconcile.Result{}, nil } -func (b *BackendRoutingReconciler) ensureCanaryRemove(ctx context.Context, br *v1alpha1.BackendRouting) error { - needUpdateStatus := false - backendsStatuses := br.Status.Backends - routesStatuses := br.Status.RouteStatuses - if len(routesStatuses) == 0 { - routesStatuses = make([]v1alpha1.BackendRouteStatus, len(br.Spec.Routes)) +func (b *BackendRoutingReconciler) syncInCluster(ctx context.Context, syncCtx *syncContext) error { + // sync backends + err := b.syncInClusterBackends(ctx, syncCtx) + if err != nil { + return err } - phase := br.Status.Phase - if backendsStatuses.Canary.Conditions.Terminating == nil || !*backendsStatuses.Canary.Conditions.Terminating { - // delete canary route - var routeCanaryRemoveErr []error - for idx, routeSpec := range br.Spec.Routes { - iRoute, err := b.getRoute(ctx, br.Namespace, routeSpec) - if err != nil { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, err) - } - err = iRoute.RemoveCanaryRoute(ctx) - if err != nil { - if routesStatuses[idx].Synced { - routesStatuses[idx].Synced = false - } - routeCanaryRemoveErr = append(routeCanaryRemoveErr, err) - continue - } - if !routesStatuses[idx].Synced { - routesStatuses[idx].Synced = true - } - } - if len(routeCanaryRemoveErr) > 0 { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, multierr.Combine(routeCanaryRemoveErr...)) - } + // sync route + err = b.syncInClusterRoutes(ctx, syncCtx) + if err != nil { + return err + } - // delete canary backend - canaryBackend, err := b.getBackend(ctx, br, backendsStatuses.Canary.Name) + return nil +} + +func (b *BackendRoutingReconciler) syncInClusterBackends(ctx context.Context, syncCtx *syncContext) error { + obj := syncCtx.Object + logger := logr.FromContextOrDiscard(ctx) + + if obj.Spec.ForkedBackends == nil { + deleted, err := b.deleteBackendResource(ctx, syncCtx, syncCtx.NewStatus.Backends.Canary.Name) if err != nil { - if errors.IsNotFound(err) { - // already deleted - conditionTrue := true - conditionFalse := false - backendsStatuses.Canary.Conditions.Terminating = &conditionTrue - backendsStatuses.Canary.Conditions.Ready = &conditionFalse - phase = v1alpha1.Ready - needUpdateStatus = true - } else { - return err - } + logger.Error(err, "failed delete canary backend resource", "backend", syncCtx.NewStatus.Backends.Canary.Name) + return err + } + if deleted { + syncCtx.NewStatus.Backends.Canary = rolloutv1alpha1.BackendStatus{} } else { - if canaryBackend.GetBackendObject().GetDeletionTimestamp() == nil { - err = b.Client.Delete(clusterinfo.WithCluster(ctx, br.Spec.Backend.Cluster), canaryBackend.GetBackendObject()) - if err != nil { - return err - } + syncCtx.NewStatus.Backends.Canary.Conditions = rolloutv1alpha1.BackendConditions{ + Terminating: ptr.To(true), } - conditionTrue := true - conditionFalse := false - backendsStatuses.Canary.Conditions.Terminating = &conditionTrue - backendsStatuses.Canary.Conditions.Ready = &conditionFalse - phase = v1alpha1.RouteUpgrading - needUpdateStatus = true } - } else { - // check canary route deleted - var routeCanaryRemoveErr []error - for idx, routeSpec := range br.Spec.Routes { - iRoute, err := b.getRoute(ctx, br.Namespace, routeSpec) - if err != nil { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, err) - } - err = iRoute.RemoveCanaryRoute(ctx) - if err != nil { - if routesStatuses[idx].Synced { - routesStatuses[idx].Synced = false - } - routeCanaryRemoveErr = append(routeCanaryRemoveErr, err) - continue - } - if !routesStatuses[idx].Synced { - routesStatuses[idx].Synced = true - } + deleted, err = b.deleteBackendResource(ctx, syncCtx, syncCtx.NewStatus.Backends.Stable.Name) + if err != nil { + logger.Error(err, "failed delete stable backend resource", "backend", syncCtx.NewStatus.Backends.Canary.Name) + return err } - if len(routeCanaryRemoveErr) > 0 { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, multierr.Combine(routeCanaryRemoveErr...)) + if deleted { + syncCtx.NewStatus.Backends.Stable = rolloutv1alpha1.BackendStatus{} + } else { + syncCtx.NewStatus.Backends.Stable.Conditions = rolloutv1alpha1.BackendConditions{ + Terminating: ptr.To(true), + } } + return nil + } - // check canary backend deleted - _, err := b.getBackend(ctx, br, backendsStatuses.Canary.Name) - if !errors.IsNotFound(err) { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, fmt.Errorf("canary backend not deleted yet")) - } - backendsStatuses.Canary = v1alpha1.BackendStatus{} - needUpdateStatus = true + // ensure canary and stable backends + canaryConfig := obj.Spec.ForkedBackends.Canary.DeepCopy() + if canaryConfig.ExtraLabelSelector == nil { + canaryConfig.ExtraLabelSelector = make(map[string]string) + } + canaryConfig.ExtraLabelSelector[rolloutapi.LabelTrafficLane] = rolloutapi.LabelValueTrafficLaneCanary + err := b.ensureBackendResource(ctx, syncCtx, *canaryConfig) + if err != nil { + return err } + // change status + syncCtx.NewStatus.Backends.Canary.Conditions.Ready = ptr.To(true) - if needUpdateStatus { - return b.updateBackendRoutingStatus(ctx, br, backendsStatuses, routesStatuses, phase) + stableConfig := obj.Spec.ForkedBackends.Stable.DeepCopy() + if stableConfig.ExtraLabelSelector == nil { + stableConfig.ExtraLabelSelector = make(map[string]string) } + stableConfig.ExtraLabelSelector[rolloutapi.LabelTrafficLane] = rolloutapi.LabelValueTrafficLaneStable + err = b.ensureBackendResource(ctx, syncCtx, *stableConfig) + if err != nil { + return err + } + + // change status + syncCtx.NewStatus.Backends.Stable.Conditions.Ready = ptr.To(true) return nil } -func (b *BackendRoutingReconciler) ensureCanaryAdd(ctx context.Context, br *v1alpha1.BackendRouting) error { - needUpdateStatus := false - if br.Status.ObservedGeneration != br.Generation { - needUpdateStatus = true +func (b *BackendRoutingReconciler) findBackend(ctx context.Context, obj *rolloutv1alpha1.BackendRouting, name string) (backend.InClusterBackend, client.Object, error) { + gvk := schema.FromAPIVersionAndKind(obj.Spec.Backend.APIVersion, obj.Spec.Backend.Kind) + backendStore, err := b.backendRegistry.Get(gvk) + if err != nil { + return nil, nil, err } - backendsStatuses := br.Status.Backends - routesStatuses := br.Status.RouteStatuses - if len(routesStatuses) == 0 { - routesStatuses = make([]v1alpha1.BackendRouteStatus, len(br.Spec.Routes)) + newObj := backendStore.NewObject() + ctx = clusterinfo.WithCluster(ctx, obj.Spec.Backend.Cluster) + err = b.Client.Get(ctx, client.ObjectKey{ + Namespace: obj.Namespace, + Name: name, + }, newObj) + return backendStore, newObj, err +} + +func (b *BackendRoutingReconciler) deleteBackendResource(ctx context.Context, syncCtx *syncContext, name string) (bool, error) { + if len(name) == 0 { + return true, nil } - phase := br.Status.Phase - // todo: discussion - // should we check origin & stable here? - // check canary backend and route - _, err := b.getBackend(ctx, br, br.Spec.Forwarding.Canary.Name) - if err != nil { - if !errors.IsNotFound(err) { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, err) - } - // canary backend not exist, create canary backend, get origin backend first - originBackend, err := b.getBackend(ctx, br, br.Spec.Backend.Name) - if err != nil { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, err) - } + obj := syncCtx.Object + ctx = clusterinfo.WithCluster(ctx, obj.Spec.Backend.Cluster) - canaryForked := originBackend.ForkCanary(br.Spec.Forwarding.Canary.Name) - err = b.Client.Create(clusterinfo.WithCluster(ctx, br.Spec.Backend.Cluster), canaryForked) - if err != nil { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, err) + _, backendObj, err := b.findBackend(ctx, obj, name) + if err != nil { + if errors.IsNotFound(err) { + // not found means already deleted + return true, nil } - } - if backendsStatuses.Canary.Name != br.Spec.Forwarding.Canary.Name || !ptr.Deref(backendsStatuses.Canary.Conditions.Ready, false) { - backendsStatuses.Canary.Name = br.Spec.Forwarding.Canary.Name - conditionTrue := true - backendsStatuses.Canary.Conditions.Ready = &conditionTrue - needUpdateStatus = true + return true, nil } - var routeCanaryCreateErr []error - for idx, routeSpec := range br.Spec.Routes { - iRoute, err := b.getRoute(ctx, br.Namespace, routeSpec) - if err != nil { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, err) - } - err = iRoute.AddCanaryRoute(ctx, br.Spec.Forwarding) - if err != nil { - if routesStatuses[idx].Synced { - routesStatuses[idx].Synced = false - needUpdateStatus = true - } - routeCanaryCreateErr = append(routeCanaryCreateErr, err) - continue - } - if !routesStatuses[idx].Synced { - routesStatuses[idx].Synced = true - needUpdateStatus = true - } + if backendObj.GetDeletionTimestamp() != nil { + // waiting for finalizers + return false, nil } - if len(routeCanaryCreateErr) > 0 { - return b.handleErr(ctx, br, backendsStatuses, routesStatuses, phase, v1alpha1.RouteUpgrading, multierr.Combine(routeCanaryCreateErr...)) - } + err = b.Client.Delete(ctx, backendObj) + return false, err +} - if phase != v1alpha1.Ready { - phase = v1alpha1.Ready - needUpdateStatus = true +func (b *BackendRoutingReconciler) ensureBackendResource(ctx context.Context, syncCtx *syncContext, config rolloutv1alpha1.ForkedBackend) error { + obj := syncCtx.Object + logger := logr.FromContextOrDiscard(ctx) + ctx = clusterinfo.WithCluster(ctx, obj.Spec.Backend.Cluster) + backendStore, _, err := b.findBackend(ctx, obj, config.Name) + if err == nil { + // found + return nil + } + if !errors.IsNotFound(err) { + logger.Error(err, "failed to get backend resource", "backend", obj.Spec.Backend.Name) + return err } - if needUpdateStatus { - return b.updateBackendRoutingStatus(ctx, br, backendsStatuses, routesStatuses, phase) + // need to create + newBackend := backendStore.Fork(syncCtx.BackendObject, config) + // set owner + newBackend.SetOwnerReferences([]metav1.OwnerReference{*metav1.NewControllerRef(obj, backendStore.GroupVersionKind())}) + // add label + labels := newBackend.GetLabels() + if labels == nil { + labels = make(map[string]string) } + labels[rolloutapi.LabelTemporaryResource] = "true" + newBackend.SetLabels(labels) + err = b.Client.Create(ctx, newBackend) + if err != nil { + logger.Error(err, "failed to create backend resource", "backend", newBackend.GetName()) + return err + } return nil } -func (b *BackendRoutingReconciler) handleErr(ctx context.Context, br *v1alpha1.BackendRouting, backendsStatuses v1alpha1.BackendStatuses, - routesStatuses []v1alpha1.BackendRouteStatus, phase, desiredPhase v1alpha1.BackendRoutingPhase, err error, -) error { - if phase != desiredPhase { - phase = desiredPhase - _ = b.updateBackendRoutingStatus(ctx, br, backendsStatuses, routesStatuses, phase) - } - return err -} +func (b *BackendRoutingReconciler) syncInClusterRoutes(ctx context.Context, syncCtx *syncContext) error { + obj := syncCtx.Object -func (b *BackendRoutingReconciler) getBackend(ctx context.Context, br *v1alpha1.BackendRouting, backendName string) (backend.IBackend, error) { - backendStore, err := b.backendRegistry.Get(schema.FromAPIVersionAndKind(br.Spec.Backend.APIVersion, br.Spec.Backend.Kind)) - if err != nil { - return nil, err + syncRoute := func(i int, fn func(routeCtl route.RouteControl) error) error { + routeCtl := syncCtx.Routes[i] + err := fn(routeCtl) + if err != nil { + // TODO: record error + syncCtx.setRouteCondition(i, metav1.ConditionFalse, "SyncFailed", err.Error()) + return err + } + // TODO: add synced logic, read condition from annotations + syncCtx.setRouteCondition(i, metav1.ConditionTrue, "Synced", "") + return nil } - return backendStore.Get(ctx, br.Spec.Backend.Cluster, br.Namespace, backendName) + for i, routeStatus := range syncCtx.NewStatus.Routes { + needCreate, needDelete := syncCtx.checkOriginRoute(routeStatus.Forwarding) + + if needCreate { + err := syncRoute(i, func(routeCtl route.RouteControl) error { + return routeCtl.ChangeOrigin(ctx, obj.Spec.Backend, obj.Spec.Forwarding.HTTP.Origin.BackendName) + }) + if err != nil { + return err + } + syncCtx.NewStatus.Routes[i].Forwarding.Origin.Conditions.Ready = ptr.To(true) + } + if needDelete { + // set condition firstly + syncCtx.NewStatus.Routes[i].Forwarding.Origin.Conditions.Ready = nil + syncCtx.NewStatus.Routes[i].Forwarding.Origin.Conditions.Terminating = ptr.To(true) + + err := syncRoute(i, func(routeCtl route.RouteControl) error { + return routeCtl.ResetOrigin(ctx, obj.Spec.Backend, syncCtx.NewStatus.Routes[i].Forwarding.Origin.BackendName) + }) + if err != nil { + return err + } + + // delete forwarding status + syncCtx.NewStatus.Routes[i].Forwarding.Origin = nil + } + + needCreate, needDelete = syncCtx.checkCanaryRoute(routeStatus.Forwarding) + if needCreate { + err := syncRoute(i, func(routeCtl route.RouteControl) error { + return routeCtl.AddCanary(ctx, obj) + }) + if err != nil { + return err + } + syncCtx.NewStatus.Routes[i].Forwarding.Canary.Conditions.Ready = ptr.To(true) + } + if needDelete { + // set condition firstly + syncCtx.NewStatus.Routes[i].Forwarding.Canary.Conditions.Ready = nil + syncCtx.NewStatus.Routes[i].Forwarding.Canary.Conditions.Terminating = ptr.To(true) + + err := syncRoute(i, func(routeCtl route.RouteControl) error { + return routeCtl.DeleteCanary(ctx, obj) + }) + if err != nil { + return err + } + syncCtx.NewStatus.Routes[i].Forwarding.Canary = nil + } + } + return nil } -func (b *BackendRoutingReconciler) getRoute(ctx context.Context, namespace string, routeInfo v1alpha1.CrossClusterObjectReference) (route.IRoute, error) { - routeStore, err := b.routeRegistry.Get(schema.FromAPIVersionAndKind(routeInfo.APIVersion, routeInfo.Kind)) +func (b *BackendRoutingReconciler) findRoute(ctx context.Context, namespace string, routeInfo rolloutv1alpha1.CrossClusterObjectReference) (route.RouteControl, error) { + routeAccessor, err := b.routeRegistry.Get(schema.FromAPIVersionAndKind(routeInfo.APIVersion, routeInfo.Kind)) if err != nil { return nil, err } - return routeStore.Get(ctx, routeInfo.Cluster, namespace, routeInfo.Name) -} - -func (b *BackendRoutingReconciler) updateBackendRoutingStatus(ctx context.Context, br *v1alpha1.BackendRouting, - backends v1alpha1.BackendStatuses, routes []v1alpha1.BackendRouteStatus, phase v1alpha1.BackendRoutingPhase, -) error { - brGet := &v1alpha1.BackendRouting{} - err := b.Client.Get(clusterinfo.WithCluster(ctx, clusterinfo.Fed), types.NamespacedName{ - Name: br.Name, - Namespace: br.Namespace, - }, brGet) + routeObj := routeAccessor.NewObject() + err = b.Client.Get(clusterinfo.WithCluster(ctx, routeInfo.Cluster), client.ObjectKey{Namespace: namespace, Name: routeInfo.Name}, routeObj) if err != nil { - return err + return nil, err } - brGet.Status.ObservedGeneration = br.Generation - brGet.Status.Backends = backends - brGet.Status.RouteStatuses = routes - brGet.Status.Phase = phase - return b.Client.Status().Update(clusterinfo.WithCluster(ctx, clusterinfo.Fed), brGet) -} -func (b *BackendRoutingReconciler) reconcileMultiClusterType(_ context.Context, _ *v1alpha1.BackendRouting) (reconcile.Result, error) { - // todo - return reconcile.Result{}, nil + return routeAccessor.Wrap(b.Client, routeInfo.Cluster, routeObj) } diff --git a/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go b/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go deleted file mode 100644 index 5ad1d60..0000000 --- a/pkg/controllers/backendrouting/backendrouting_controller_suite_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2023 The KusionStack 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 backendrouting - -import ( - "context" - "os" - "path/filepath" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "kusionstack.io/kube-utils/multicluster" - "kusionstack.io/kube-utils/multicluster/clusterinfo" - "kusionstack.io/kube-utils/multicluster/clusterprovider" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/cluster" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/manager" - - "kusionstack.io/kube-api/rollout/v1alpha1" - "kusionstack.io/rollout/pkg/controllers/registry" -) - -var ( - fedEnv *envtest.Environment - fedClient client.Client - fedClientSet *kubernetes.Clientset - - clusterEnv1 *envtest.Environment - clusterClient1 client.Client // cluster 1 client - - clusterEnv2 *envtest.Environment - clusterClient2 client.Client // cluster 2 client - - ctx context.Context - cancel context.CancelFunc - - mgr *multicluster.Manager -) - -var _ = BeforeSuite(func() { - defer GinkgoRecover() - - logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) - - ctx, cancel = context.WithCancel(context.TODO()) - By("bootstrapping test environment") - - // fed - testscheme := scheme.Scheme - err := v1alpha1.AddToScheme(testscheme) - Expect(err).NotTo(HaveOccurred()) - - fedEnv = &envtest.Environment{ - Scheme: testscheme, - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, - } - fedConfig, err := fedEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(fedConfig).NotTo(BeNil()) - - fedClientSet, _ = kubernetes.NewForConfig(fedConfig) - - fedClient, err = client.New(fedConfig, client.Options{Scheme: testscheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(fedClient).NotTo(BeNil()) - - clusterEnv1 = &envtest.Environment{ - Scheme: testscheme, - } - clusterConfig1, err := clusterEnv1.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(clusterConfig1).NotTo(BeNil()) - - clusterClient1, err = client.New(clusterConfig1, client.Options{Scheme: testscheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(clusterClient1).NotTo(BeNil()) - - // cluster 2 - clusterEnv2 = &envtest.Environment{ - Scheme: testscheme, - } - clusterConfig2, err := clusterEnv2.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(clusterConfig2).NotTo(BeNil()) - - clusterClient2, err = client.New(clusterConfig2, client.Options{Scheme: testscheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(clusterClient2).NotTo(BeNil()) - - // manager - var ( - newCacheFunc cache.NewCacheFunc - newClientFunc cluster.NewClientFunc - ) - os.Setenv(clusterinfo.EnvClusterAllowList, "cluster1,cluster2") - - mgr, newCacheFunc, newClientFunc, err = multicluster.NewManager(&multicluster.ManagerConfig{ - ClusterProvider: clusterprovider.NewSimpleClusterProvider(map[string]*rest.Config{ - "cluster1": clusterConfig1, - "cluster2": clusterConfig2, - "fed": fedConfig, - }), - FedConfig: fedConfig, - ClusterScheme: testscheme, - ResyncPeriod: 10 * time.Minute, - }, multicluster.Options{}) - Expect(err).NotTo(HaveOccurred()) - Expect(mgr).NotTo(BeNil()) - Expect(newCacheFunc).NotTo(BeNil()) - Expect(newClientFunc).NotTo(BeNil()) - - go func() { - mgr.Run(ctx) - Expect(err).ToNot(HaveOccurred()) - }() - - ctx = clusterinfo.WithCluster(ctx, clusterinfo.Fed) - - scheme := scheme.Scheme - err = v1alpha1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - ctrlMgr, err := manager.New(fedConfig, manager.Options{ - NewClient: newClientFunc, - NewCache: newCacheFunc, - MetricsBindAddress: "0", - HealthProbeBindAddress: "0", - }) - Expect(err).NotTo(HaveOccurred()) - - _, err = registry.InitWorkloadRegistry(ctrlMgr) - Expect(err).NotTo(HaveOccurred()) - _, err = registry.InitBackendRegistry(ctrlMgr) - Expect(err).NotTo(HaveOccurred()) - _, err = registry.InitRouteRegistry(ctrlMgr) - Expect(err).NotTo(HaveOccurred()) - - _, err = InitFunc(ctrlMgr) - Expect(err).NotTo(HaveOccurred()) - - go ctrlMgr.Start(ctx) -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - - if fedEnv != nil { - err := fedEnv.Stop() - Expect(err).NotTo(HaveOccurred()) - } - if clusterEnv1 != nil { - err := clusterEnv1.Stop() - Expect(err).NotTo(HaveOccurred()) - } - if clusterEnv2 != nil { - err := clusterEnv2.Stop() - Expect(err).NotTo(HaveOccurred()) - } -}) diff --git a/pkg/controllers/backendrouting/backendrouting_controller_test.go b/pkg/controllers/backendrouting/backendrouting_controller_test.go index 56c2329..5fd85d4 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller_test.go +++ b/pkg/controllers/backendrouting/backendrouting_controller_test.go @@ -1,533 +1,676 @@ -// Copyright 2023 The KusionStack 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 backendrouting import ( - "testing" + "context" + "flag" + "os" + "path/filepath" "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - appsv1 "k8s.io/api/apps/v1" + "github.com/stretchr/testify/suite" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" + "k8s.io/klog/v2/klogr" + "k8s.io/utils/ptr" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + clientutil "kusionstack.io/kube-utils/client" + "kusionstack.io/kube-utils/multicluster" "kusionstack.io/kube-utils/multicluster/clusterinfo" + "kusionstack.io/kube-utils/multicluster/clusterprovider" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" - "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/controllers/registry" ) -var _ = Describe("backend-routing-controller", func() { - Context("InCluster BackendRouting", func() { - It("create local cluster", func() { - var replicas int32 = 1 - err := fedClient.Create(ctx, &appsv1.Deployment{ - ObjectMeta: v1.ObjectMeta{ - Namespace: "default", - Name: "cluster1", +type backendRoutingTestSuite struct { + suite.Suite + + fedClient client.Client + cluster1Client client.Client + cluster2Client client.Client + + backendRouting *rolloutv1alpha1.BackendRouting + service *corev1.Service + ingress *networkingv1.Ingress +} + +func (s *backendRoutingTestSuite) setupCluster(env *envtest.Environment) (*rest.Config, client.Client) { + config, err := env.Start() + s.Require().NoError(err) + s.Require().NotNil(config) + + c, err := client.New(config, client.Options{Scheme: env.Scheme}) + s.Require().NoError(err) + s.Require().NotNil(c) + + return config, c +} + +func (s *backendRoutingTestSuite) SetupSuite() { + local := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + klog.InitFlags(local) + local.Set("v", "3") + + logf.SetLogger(klogr.New()) + + ctx := context.Background() + + // fed + testscheme := scheme.Scheme + err := rolloutv1alpha1.AddToScheme(testscheme) + s.Require().NoError(err) + + fedEnv := &envtest.Environment{ + Scheme: testscheme, + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + } + + var ( + fedConfig *rest.Config + cluster1Config *rest.Config + cluster2Config *rest.Config + ) + + fedConfig, s.fedClient = s.setupCluster(fedEnv) + cluster1Config, s.cluster1Client = s.setupCluster(&envtest.Environment{ + Scheme: testscheme, + }) + cluster2Config, s.cluster2Client = s.setupCluster(&envtest.Environment{ + Scheme: testscheme, + }) + + // manager + os.Setenv(clusterinfo.EnvClusterAllowList, "cluster1,cluster2") + + clusterMgr, newCacheFunc, newClientFunc, err := multicluster.NewManager(&multicluster.ManagerConfig{ + ClusterProvider: clusterprovider.NewSimpleClusterProvider(map[string]*rest.Config{ + "cluster1": cluster1Config, + "cluster2": cluster2Config, + "fed": fedConfig, + }), + FedConfig: fedConfig, + ClusterScheme: testscheme, + ResyncPeriod: 10 * time.Minute, + }, multicluster.Options{}) + + s.Require().NoError(err) + + go func() { + // start multi cluster client manager + err := clusterMgr.Run(ctx) + if err != nil { + panic(err) + } + }() + + // wait for cache synced + s.Require().True(clusterMgr.WaitForSynced(ctx)) + + ctx = clusterinfo.WithCluster(ctx, clusterinfo.Fed) + + mgr, err := manager.New(fedConfig, manager.Options{ + Scheme: testscheme, + NewClient: newClientFunc, + NewCache: newCacheFunc, + MetricsBindAddress: "0", + HealthProbeBindAddress: "0", + }) + + s.Require().NoError(err) + + _, err = registry.InitWorkloadRegistry(mgr) + s.Require().NoError(err) + _, err = registry.InitBackendRegistry(mgr) + s.Require().NoError(err) + _, err = registry.InitRouteRegistry(mgr) + s.Require().NoError(err) + + _, err = InitFunc(mgr) + s.Require().NoError(err) + + go func() { + err := mgr.Start(ctx) + if err != nil { + panic(err) + } + }() +} + +func (s *backendRoutingTestSuite) SetupTest() { + namespace := "default" + s.service = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-incluster-service", + Namespace: namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Protocol: corev1.ProtocolTCP, + Port: 80, + TargetPort: intstr.IntOrString{IntVal: 80}, }, - Spec: appsv1.DeploymentSpec{ - Selector: &v1.LabelSelector{ - MatchLabels: map[string]string{ - "cluster": "cluster1", - }, - }, - Replicas: &replicas, - Template: corev1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "cluster": "cluster1", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ + }, + }, + } + + s.ingress = &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-incluster-ingress", + Namespace: namespace, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ { - Name: "cluster1", - Image: "kennethreitz/httpbin", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: s.service.Name, + Port: networkingv1.ServiceBackendPort{ + Number: int32(80), + }, + }, + }, + Path: "/", + PathType: ptr.To(networkingv1.PathTypePrefix), }, }, }, }, }, - }) - Expect(err).NotTo(HaveOccurred()) - }) - - br0 := v1alpha1.BackendRouting{ - ObjectMeta: v1.ObjectMeta{ - Name: "br-controller-ut-br0", - Namespace: "default", - Generation: int64(1), }, - Spec: v1alpha1.BackendRoutingSpec{ - TrafficType: v1alpha1.InClusterTrafficType, - Backend: v1alpha1.CrossClusterObjectReference{ - ObjectTypeRef: v1alpha1.ObjectTypeRef{ - APIVersion: "v1", - Kind: "Service", + }, + } + + s.backendRouting = &rolloutv1alpha1.BackendRouting{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-incluster-backendrouting", + Namespace: namespace, + Generation: int64(1), + }, + Spec: rolloutv1alpha1.BackendRoutingSpec{ + TrafficType: rolloutv1alpha1.InClusterTrafficType, + Backend: rolloutv1alpha1.CrossClusterObjectReference{ + ObjectTypeRef: rolloutv1alpha1.ObjectTypeRef{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Service", + }, + CrossClusterObjectNameReference: rolloutv1alpha1.CrossClusterObjectNameReference{ + Cluster: "cluster1", + Name: s.service.Name, + }, + }, + Routes: []rolloutv1alpha1.CrossClusterObjectReference{ + { + ObjectTypeRef: rolloutv1alpha1.ObjectTypeRef{ + APIVersion: networkingv1.SchemeGroupVersion.String(), + Kind: "Ingress", }, - CrossClusterObjectNameReference: v1alpha1.CrossClusterObjectNameReference{ + CrossClusterObjectNameReference: rolloutv1alpha1.CrossClusterObjectNameReference{ Cluster: "cluster1", - Name: "br-controller-ut-svc1", - }, - }, - Routes: []v1alpha1.CrossClusterObjectReference{ - { - ObjectTypeRef: v1alpha1.ObjectTypeRef{ - APIVersion: "networking.k8s.io/v1", - Kind: "Ingress", - }, - CrossClusterObjectNameReference: v1alpha1.CrossClusterObjectNameReference{ - Cluster: "cluster1", - Name: "br-controller-ut-igs1", - }, + Name: s.ingress.Name, }, }, }, + }, + } +} + +func (s *backendRoutingTestSuite) TearDownTest() { + err := s.fedClient.Delete(context.Background(), s.backendRouting) + s.Require().NoError(client.IgnoreNotFound(err)) + err = s.cluster1Client.DeleteAllOf(context.Background(), s.service, &client.DeleteAllOfOptions{ListOptions: client.ListOptions{Namespace: s.service.Namespace}}) + s.Require().NoError(err) + err = s.cluster1Client.DeleteAllOf(context.Background(), s.ingress, &client.DeleteAllOfOptions{ListOptions: client.ListOptions{Namespace: s.ingress.Namespace}}) + s.Require().NoError(err) +} + +func (s *backendRoutingTestSuite) backendRoutingShouldBeReady(obj *rolloutv1alpha1.BackendRouting) bool { + if obj.Generation != obj.Status.ObservedGeneration { + logf.Log.Info("generation not equal", "observedGeneration", obj.Status.ObservedGeneration, "generation", obj.Generation) + return false + } + if !meta.IsStatusConditionTrue(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingReady) { + logf.Log.Info("Ready condition should be true", "conditions", obj.Status.Conditions) + return false + } + return true +} + +type BackendRoutingInitializationTestSuite struct { + backendRoutingTestSuite +} + +func (s *BackendRoutingInitializationTestSuite) Test_WithoutBackendAndRoute() { + // create backendrouting + err := s.fedClient.Create(context.Background(), s.backendRouting) + s.Require().NoError(err) + + s.Require().Eventually(func() bool { + obj := &rolloutv1alpha1.BackendRouting{} + err := s.fedClient.Get(context.Background(), client.ObjectKeyFromObject(s.backendRouting), obj) + if err != nil { + return false } - It("Initialization of traffic", func() { - err := fedClient.Create(clusterinfo.WithCluster(ctx, clusterinfo.Fed), &br0) - Expect(err).ShouldNot(HaveOccurred()) - - time.Sleep(3 * time.Second) - - // service not created yet - Eventually(func() bool { - brTmp := &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.BackendUpgrading - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - // service created but ingress not created yet - err = clusterClient1.Create(ctx, &corev1.Service{ - ObjectMeta: v1.ObjectMeta{ - Name: "br-controller-ut-svc1", - Namespace: "default", - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Protocol: corev1.ProtocolTCP, - Port: 80, - TargetPort: intstr.IntOrString{IntVal: 80}, - }, - }, + if obj.Generation != obj.Status.ObservedGeneration { + logf.Log.Info("generation not equal", "observedGeneration", obj.Status.ObservedGeneration, "generation", obj.Generation) + return false + } + + if !meta.IsStatusConditionFalse(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingBackendReady) { + logf.Log.Info("BackendReady condition should be false", "condition", meta.FindStatusCondition(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingBackendReady)) + return false + } + if !meta.IsStatusConditionFalse(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingRouteReady) { + logf.Log.Info("RouteReady condition should be false", "condition", meta.FindStatusCondition(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingRouteReady)) + return false + } + if !meta.IsStatusConditionFalse(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingReady) { + logf.Log.Info("Ready condition should be false", "condition", meta.FindStatusCondition(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingReady)) + return false + } + return true + }, 10*time.Second, 5*time.Second) +} + +func (s *BackendRoutingInitializationTestSuite) Test_CreateWtihoutRoute() { + // create backendrouting + err := s.fedClient.Create(context.Background(), s.backendRouting) + s.Require().NoError(err) + // create origin service + err = s.cluster1Client.Create(context.Background(), s.service) + s.Require().NoError(err) + + s.Require().Eventually(func() bool { + svc := &corev1.Service{} + err := s.cluster1Client.Get(context.Background(), client.ObjectKeyFromObject(s.service), svc) + if err != nil { + logf.Log.Info("svc not found") + return false + } + + obj := &rolloutv1alpha1.BackendRouting{} + err = s.fedClient.Get(context.Background(), client.ObjectKeyFromObject(s.backendRouting), obj) + if err != nil { + return false + } + + if obj.Generation != obj.Status.ObservedGeneration { + logf.Log.Info("generation not equal", "observedGeneration", obj.Status.ObservedGeneration, "generation", obj.Generation) + return false + } + + if !meta.IsStatusConditionTrue(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingBackendReady) { + logf.Log.Info("BackendReady condition should be true", "condition", meta.FindStatusCondition(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingBackendReady)) + return false + } + if !meta.IsStatusConditionFalse(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingRouteReady) { + logf.Log.Info("RouteReady condition should be false", "condition", meta.FindStatusCondition(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingRouteReady)) + return false + } + if !meta.IsStatusConditionFalse(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingReady) { + logf.Log.Info("Ready condition should be false", "condition", meta.FindStatusCondition(obj.Status.Conditions, rolloutv1alpha1.BackendRoutingReady)) + return false + } + return true + }, 60*time.Second, 5*time.Second) +} + +func (s *BackendRoutingInitializationTestSuite) Test_Create() { + // create backendrouting + err := s.fedClient.Create(context.Background(), s.backendRouting) + s.Require().NoError(err) + // create service + err = s.cluster1Client.Create(context.Background(), s.service) + s.Require().NoError(err) + // create ingress + err = s.cluster1Client.Create(context.Background(), s.ingress) + s.Require().NoError(err) + + s.Require().Eventually(func() bool { + svc := &corev1.Service{} + err := s.cluster1Client.Get(context.Background(), client.ObjectKeyFromObject(s.service), svc) + if err != nil { + logf.Log.Info("svc not found") + return false + } + ingress := &networkingv1.Ingress{} + err = s.cluster1Client.Get(context.Background(), client.ObjectKeyFromObject(s.ingress), ingress) + if err != nil { + logf.Log.Info("ingress not found") + return false + } + obj := &rolloutv1alpha1.BackendRouting{} + err = s.fedClient.Get(context.Background(), client.ObjectKeyFromObject(s.backendRouting), obj) + if err != nil { + return false + } + + return s.backendRoutingShouldBeReady(obj) + }, 60*time.Second, 5*time.Second) +} + +type BackendRoutingControllerTestSuite struct { + backendRoutingTestSuite +} + +func (s *BackendRoutingControllerTestSuite) SetupTest() { + s.backendRoutingTestSuite.SetupTest() + // create service + err := s.cluster1Client.Create(context.Background(), s.service) + s.Require().NoError(err) + // create ingress + err = s.cluster1Client.Create(context.Background(), s.ingress) + s.Require().NoError(err) + // create backendrouting + err = s.fedClient.Create(context.Background(), s.backendRouting) + s.Require().NoError(err) +} + +func (s *BackendRoutingControllerTestSuite) Test_ForBackends() { + stableSvcName := s.backendRouting.Spec.Backend.Name + "-stable" + canarySvcName := s.backendRouting.Spec.Backend.Name + "-canary" + _, err := clientutil.UpdateOnConflict(context.Background(), s.fedClient, s.fedClient, s.backendRouting, func(in *rolloutv1alpha1.BackendRouting) error { + // add fored backends + in.Spec.ForkedBackends = &rolloutv1alpha1.ForkedBackends{ + Stable: rolloutv1alpha1.ForkedBackend{ + Name: stableSvcName, + }, + Canary: rolloutv1alpha1.ForkedBackend{ + Name: canarySvcName, + }, + } + return nil + }) + s.Require().NoError(err) + + var ( + stableSVC = &corev1.Service{} + canarySVC = &corev1.Service{} + ) + + s.Require().Eventually(func() bool { + err = s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: stableSvcName}, stableSVC) + if err != nil { + logf.Log.Info("stable svc not found") + return false + } + err = s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: canarySvcName}, canarySVC) + if err != nil { + logf.Log.Info("canary svc not found") + return false + } + + s.Require().Equal(rolloutapi.LabelValueTrafficLaneStable, stableSVC.Spec.Selector[rolloutapi.LabelTrafficLane]) + s.Require().Contains(stableSVC.Labels, rolloutapi.LabelTemporaryResource) + s.Require().Equal(rolloutapi.LabelValueTrafficLaneCanary, canarySVC.Spec.Selector[rolloutapi.LabelTrafficLane]) + s.Require().Contains(canarySVC.Labels, rolloutapi.LabelTemporaryResource) + + return true + }, 60*time.Second, 5*time.Second, "stable and canary should be created") + + s.Require().Eventually(func() bool { + obj := &rolloutv1alpha1.BackendRouting{} + err := s.fedClient.Get(context.Background(), client.ObjectKeyFromObject(s.backendRouting), obj) + if err != nil { + return false + } + + ready := s.backendRoutingShouldBeReady(obj) + if !ready { + return false + } + + s.Require().NotNil(obj.Status.Backends.Canary.Conditions.Ready, "canary backend should be ready") + s.Require().True(*obj.Status.Backends.Canary.Conditions.Ready, "canary backend should be ready") + s.Require().NotNil(obj.Status.Backends.Stable.Conditions.Ready, "stable backend should be ready") + s.Require().True(*obj.Status.Backends.Stable.Conditions.Ready, "stable backend should be ready") + + return true + }, 60*time.Second, 5*time.Second, "backend routing should be ready") + + // delete fored backends + _, err = clientutil.UpdateOnConflict(context.Background(), s.fedClient, s.fedClient, s.backendRouting, func(in *rolloutv1alpha1.BackendRouting) error { + // add fored backends + in.Spec.ForkedBackends = nil + return nil + }) + s.Require().NoError(err) + + s.Require().Eventually(func() bool { + err = s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: stableSvcName}, stableSVC) + if err == nil || !errors.IsNotFound(err) { + return false + } + err = s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: canarySvcName}, canarySVC) + if err == nil || !errors.IsNotFound(err) { + return false + } + return true + }, 60*time.Second, 5*time.Second, "stable and canary should be deleted") + + s.Require().Eventually(func() bool { + obj := &rolloutv1alpha1.BackendRouting{} + err := s.fedClient.Get(context.Background(), client.ObjectKeyFromObject(s.backendRouting), obj) + if err != nil { + return false + } + + ready := s.backendRoutingShouldBeReady(obj) + if !ready { + return false + } + + s.Require().Nil(obj.Status.Backends.Canary.Conditions.Ready, "canary backend should be deleted") + s.Require().Nil(obj.Status.Backends.Stable.Conditions.Ready, "stable backend should be deleted") + + return true + }, 60*time.Second, 5*time.Second) +} + +func (s *BackendRoutingControllerTestSuite) Test_Route() { + stableSvcName := s.backendRouting.Spec.Backend.Name + "-stable" + canarySvcName := s.backendRouting.Spec.Backend.Name + "-canary" + + s.changeBackendRouting(func(in *rolloutv1alpha1.BackendRouting) error { + // add fored backends + in.Spec.ForkedBackends = &rolloutv1alpha1.ForkedBackends{ + Stable: rolloutv1alpha1.ForkedBackend{ + Name: stableSvcName, + }, + Canary: rolloutv1alpha1.ForkedBackend{ + Name: canarySvcName, + }, + } + return nil + }) + + var ( + stableSVC = &corev1.Service{} + canarySVC = &corev1.Service{} + ) + + // wating for svc created + s.Require().Eventually(func() bool { + err := s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: stableSvcName}, stableSVC) + if err != nil { + return false + } + err = s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: canarySvcName}, canarySVC) + if err != nil { + return false + } + return true + }, 60*time.Second, 5*time.Second, "stable and canary should be created") + + // change original backend + s.changeBackendRouting(func(in *rolloutv1alpha1.BackendRouting) error { + in.Spec.Forwarding = &rolloutv1alpha1.BackendForwarding{ + HTTP: &rolloutv1alpha1.HTTPForwarding{ + Origin: &rolloutv1alpha1.OriginHTTPForwarding{ + BackendName: stableSvcName, }, - }) - Expect(err).ShouldNot(HaveOccurred()) - // trigger traffictopology reconcile - brTmp := &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - if brTmp.Labels == nil { - brTmp.Labels = make(map[string]string) + }, + } + return nil + }) + + // waiting for ingress changed + s.Require().Eventually(func() bool { + ingress := &networkingv1.Ingress{} + err := s.cluster1Client.Get(context.Background(), client.ObjectKeyFromObject(s.ingress), ingress) + if err != nil { + return false + } + s.Require().Equal(stableSvcName, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name) + return true + }, 60*time.Second, 5*time.Second, "ingress should be ready") + + // backendrouding should be ready + s.checkBackendRoutingReady() + + // add canary route + s.changeBackendRouting(func(in *rolloutv1alpha1.BackendRouting) error { + // add fored backends + if in.Spec.Forwarding.HTTP.Canary == nil { + in.Spec.Forwarding.HTTP.Canary = &rolloutv1alpha1.CanaryHTTPForwarding{ + BackendName: canarySvcName, } - brTmp.Labels["trigger-reconcile-ut"] = "x" - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - // status would be ready since we didn't check whether route -> origin - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration && - *brTmp.Status.Backends.Origin.Conditions.Ready - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - // create ingress - pathType := networkingv1.PathTypePrefix - err = clusterClient1.Create(ctx, &networkingv1.Ingress{ - ObjectMeta: v1.ObjectMeta{ - Name: "br-controller-ut-igs1", - Namespace: "default", - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{ + } + in.Spec.Forwarding.HTTP.Canary.Weight = ptr.To[int32](50) + in.Spec.Forwarding.HTTP.Canary.Filters = []gatewayapiv1.HTTPRouteFilter{ + { + Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, + RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ + Set: []gatewayapiv1.HTTPHeader{ { - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: []networkingv1.HTTPIngressPath{ - { - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: "br-controller-ut-svc1", - Port: networkingv1.ServiceBackendPort{ - Number: int32(80), - }, - }, - }, - Path: "/", - PathType: &pathType, - }, - }, - }, - }, - }, - }, - }, - }) - Expect(err).ShouldNot(HaveOccurred()) - }) - - It("Stable route ready", func() { - // add forwarding to backendrouting - brTmp := &v1alpha1.BackendRouting{} - err := fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - brTmp.Spec.Forwarding = &v1alpha1.BackendForwarding{ - Stable: v1alpha1.StableBackendRule{ - Name: "br-controller-ut-svc1-stable", - }, - } - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - igsTmp := &networkingv1.Ingress{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-igs1", - Namespace: "default", - }, igsTmp) - if err != nil { - return false - } - return igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-stable" - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - brTmp := &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - }) - - It("Canary By Weight", func() { - // add canary to backendrouting - brTmp := &v1alpha1.BackendRouting{} - err := fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - canaryWeight := int32(50) - brTmp.Spec.Forwarding.Canary = v1alpha1.CanaryBackendRule{ - Name: "br-controller-ut-svc1-canary", - TrafficStrategy: v1alpha1.TrafficStrategy{ - HTTP: &v1alpha1.HTTPTrafficStrategy{ - Weight: &canaryWeight, - HTTPRouteRule: v1alpha1.HTTPRouteRule{ - Filters: []gatewayapiv1.HTTPRouteFilter{ - { - Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "x-mse-tag", - Value: "canary", - }, - }, - }, - }, - }, - }, - }, - }, - } - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - igsTmp := &networkingv1.Ingress{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-igs1-canary", - Namespace: "default", - }, igsTmp) - if err != nil { - return false - } - return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary"] == "true" && - igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-weight"] == "50" && - igsTmp.Annotations["mse.ingress.kubernetes.io/request-header-control-update"] == "" && - igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-canary" - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - // update weight - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - canaryWeight = int32(20) - brTmp.Spec.Forwarding.Canary = v1alpha1.CanaryBackendRule{ - Name: "br-controller-ut-svc1-canary", - TrafficStrategy: v1alpha1.TrafficStrategy{ - HTTP: &v1alpha1.HTTPTrafficStrategy{ - Weight: &canaryWeight, - HTTPRouteRule: v1alpha1.HTTPRouteRule{ - Filters: []gatewayapiv1.HTTPRouteFilter{ - { - Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "x-mse-tag", - Value: "canary", - }, - }, - }, - }, - }, + Name: "x-mse-tag", + Value: "canary", }, }, }, - } - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - igsTmp := &networkingv1.Ingress{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-igs1-canary", - Namespace: "default", - }, igsTmp) - if err != nil { - return false - } - return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-weight"] == "20" && - igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-canary" - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - }) - - It("Canary By Header", func() { - // add canary to backendrouting - brTmp := &v1alpha1.BackendRouting{} - err := fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - brTmp.Spec.Forwarding.Canary = v1alpha1.CanaryBackendRule{ - Name: "br-controller-ut-svc1-canary", - TrafficStrategy: v1alpha1.TrafficStrategy{ - HTTP: &v1alpha1.HTTPTrafficStrategy{ - HTTPRouteRule: v1alpha1.HTTPRouteRule{ - Matches: []v1alpha1.HTTPRouteMatch{ - { - Headers: []gatewayapiv1.HTTPHeaderMatch{ - { - Name: "env", - Value: "canary", - }, - }, - }, - }, - Filters: []gatewayapiv1.HTTPRouteFilter{ - { - Type: gatewayapiv1.HTTPRouteFilterRequestHeaderModifier, - RequestHeaderModifier: &gatewayapiv1.HTTPHeaderFilter{ - Set: []gatewayapiv1.HTTPHeader{ - { - Name: "x-mse-tag", - Value: "canary", - }, - }, - }, - }, - }, - }, - }, - }, - } - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - igsTmp := &networkingv1.Ingress{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-igs1-canary", - Namespace: "default", - }, igsTmp) - if err != nil { - return false - } - return igsTmp.Annotations["nginx.ingress.kubernetes.io/canary"] == "true" && - igsTmp.Annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] == "canary" && - igsTmp.Annotations["mse.ingress.kubernetes.io/request-header-control-update"] == "" && - igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1-canary" - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - }) - - It("End Canary", func() { - brTmp := &v1alpha1.BackendRouting{} - err := fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - brTmp.Spec.Forwarding = &v1alpha1.BackendForwarding{ - Stable: brTmp.Spec.Forwarding.Stable, - } - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - igsTmp := &networkingv1.Ingress{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-igs1-canary", - Namespace: "default", - }, igsTmp) - return errors.IsNotFound(err) - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - svcTmp := &corev1.Service{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-svc1-canary", - Namespace: "default", - }, svcTmp) - return errors.IsNotFound(err) - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - }) - - It("forwarding deleted", func() { - brTmp := &v1alpha1.BackendRouting{} - err := fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - brTmp.Spec.Forwarding = nil - err = fedClient.Update(ctx, brTmp) - Expect(err).ShouldNot(HaveOccurred()) - - Eventually(func() bool { - igsTmp := &networkingv1.Ingress{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-igs1", - Namespace: "default", - }, igsTmp) - if err != nil { - return false - } - return igsTmp.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name == "br-controller-ut-svc1" - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - svcTmp := &corev1.Service{} - err = clusterClient1.Get(ctx, types.NamespacedName{ - Name: "br-controller-ut-svc1-stable", - Namespace: "default", - }, svcTmp) - return errors.IsNotFound(err) - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - brTmp = &v1alpha1.BackendRouting{} - err = fedClient.Get(ctx, types.NamespacedName{ - Name: br0.Name, - Namespace: br0.Namespace, - }, brTmp) - if err != nil { - return false - } - return brTmp.Status.Phase == v1alpha1.Ready && brTmp.Generation == brTmp.Status.ObservedGeneration - }, 3*time.Second, 100*time.Millisecond).Should(BeTrue()) - }) + }, + } + return nil + }) + + // waiting for canary ingress created + s.Require().Eventually(func() bool { + ingress := &networkingv1.Ingress{} + key := client.ObjectKeyFromObject(s.ingress) + key.Name = s.ingress.Name + "-canary" + err := s.cluster1Client.Get(context.Background(), key, ingress) + if err != nil { + return false + } + + s.Require().Contains(ingress.Labels, rolloutapi.LabelCanary) + s.Require().Contains(ingress.Labels, rolloutapi.LabelTemporaryResource) + return true + }, 60*time.Second, 5*time.Second, "canary ingress should be ready") + + // backendrouding should be ready + s.checkBackendRoutingReady() + + // delete canary route + s.changeBackendRouting(func(in *rolloutv1alpha1.BackendRouting) error { + in.Spec.Forwarding.HTTP.Canary = nil + return nil }) -}) -func TestBackendRoutingController(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "backend-routing-controller test") + // backendrouding should be ready + s.checkBackendRoutingReady() + + // waiting for canary ingress deleted + s.Require().Eventually(func() bool { + ingress := &networkingv1.Ingress{} + key := client.ObjectKeyFromObject(s.ingress) + key.Name = s.ingress.Name + "-canary" + err := s.cluster1Client.Get(context.Background(), key, ingress) + if err == nil { + return false + } + + if !errors.IsNotFound(err) { + s.Require().NoError(err) + return false + } + return true + }, 60*time.Second, 5*time.Second, "canary ingress should be deleted") + + // revert original backend + s.changeBackendRouting(func(in *rolloutv1alpha1.BackendRouting) error { + in.Spec.Forwarding = nil + return nil + }) + + s.checkBackendRoutingReady() + + s.Require().Eventually(func() bool { + ingress := &networkingv1.Ingress{} + err := s.cluster1Client.Get(context.Background(), client.ObjectKeyFromObject(s.ingress), ingress) + if err != nil { + return false + } + s.Require().Equal(s.backendRouting.Spec.Backend.Name, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name) + return true + }, 60*time.Second, 5*time.Second, "ingress should be revert") + + // delete fored backends + s.changeBackendRouting(func(in *rolloutv1alpha1.BackendRouting) error { + // add fored backends + in.Spec.ForkedBackends = nil + return nil + }) + + s.Require().Eventually(func() bool { + err := s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: stableSvcName}, stableSVC) + if err == nil || !errors.IsNotFound(err) { + return false + } + err = s.cluster1Client.Get(context.Background(), client.ObjectKey{Namespace: s.backendRouting.Namespace, Name: canarySvcName}, canarySVC) + if err == nil || !errors.IsNotFound(err) { + return false + } + return true + }, 60*time.Second, 5*time.Second, "stable and canary should be deleted") + + s.checkBackendRoutingReady() +} + +func (s *BackendRoutingControllerTestSuite) changeBackendRouting(fn func(in *rolloutv1alpha1.BackendRouting) error) { + _, err := clientutil.UpdateOnConflict(context.Background(), s.fedClient, s.fedClient, s.backendRouting, fn) + s.Require().NoError(err) +} + +func (s *BackendRoutingControllerTestSuite) checkBackendRoutingReady() { + s.Require().Eventually(func() bool { + err := s.fedClient.Get(context.Background(), client.ObjectKeyFromObject(s.backendRouting), s.backendRouting) + if err != nil { + return false + } + + ready := s.backendRoutingShouldBeReady(s.backendRouting) + if !ready { + return false + } + + return true + }, 60*time.Second, 5*time.Second) } diff --git a/pkg/controllers/backendrouting/event_handler.go b/pkg/controllers/backendrouting/event_handler.go deleted file mode 100644 index 0b14c51..0000000 --- a/pkg/controllers/backendrouting/event_handler.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2023 The KusionStack 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 backendrouting - -import ( - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/util/workqueue" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -var _ handler.EventHandler = &EnqueueBR{} - -type EnqueueBR struct{} - -func (e *EnqueueBR) Create(createEvent event.CreateEvent, q workqueue.RateLimitingInterface) { - if createEvent.Object == nil { - return - } - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: createEvent.Object.GetName(), - Namespace: createEvent.Object.GetNamespace(), - }}) -} - -func (e *EnqueueBR) Update(updateEvent event.UpdateEvent, q workqueue.RateLimitingInterface) { - if updateEvent.ObjectOld != nil { - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: updateEvent.ObjectOld.GetName(), - Namespace: updateEvent.ObjectOld.GetNamespace(), - }}) - } - - if updateEvent.ObjectNew != nil { - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: updateEvent.ObjectNew.GetName(), - Namespace: updateEvent.ObjectNew.GetNamespace(), - }}) - } -} - -func (e *EnqueueBR) Delete(deleteEvent event.DeleteEvent, q workqueue.RateLimitingInterface) { - if deleteEvent.Object == nil { - return - } - - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: deleteEvent.Object.GetName(), - Namespace: deleteEvent.Object.GetNamespace(), - }}) -} - -func (e *EnqueueBR) Generic(genericEvent event.GenericEvent, q workqueue.RateLimitingInterface) { - if genericEvent.Object == nil { - return - } - - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: genericEvent.Object.GetName(), - Namespace: genericEvent.Object.GetNamespace(), - }}) -} diff --git a/pkg/controllers/backendrouting/suit_test.go b/pkg/controllers/backendrouting/suit_test.go new file mode 100644 index 0000000..e5da686 --- /dev/null +++ b/pkg/controllers/backendrouting/suit_test.go @@ -0,0 +1,19 @@ +package backendrouting + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestBackendRoutingInitializationTestSuite(t *testing.T) { + suite.Run(t, new(BackendRoutingInitializationTestSuite)) +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestBackendRoutingControllerTestSuite(t *testing.T) { + suite.Run(t, new(BackendRoutingControllerTestSuite)) +} diff --git a/pkg/controllers/backendrouting/sync_context.go b/pkg/controllers/backendrouting/sync_context.go new file mode 100644 index 0000000..9a2868b --- /dev/null +++ b/pkg/controllers/backendrouting/sync_context.go @@ -0,0 +1,213 @@ +package backendrouting + +import ( + "sync" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "kusionstack.io/rollout/pkg/backend" + "kusionstack.io/rollout/pkg/route" +) + +type syncContext struct { + once sync.Once + Object *rolloutv1alpha1.BackendRouting + BackendInterface backend.InClusterBackend + BackendObject client.Object + NewStatus *rolloutv1alpha1.BackendRoutingStatus + Routes []route.RouteControl +} + +func (c *syncContext) Initialize() { + c.once.Do(func() { + if c.NewStatus == nil { + c.NewStatus = c.Object.Status.DeepCopy() + } + + newStatus := c.NewStatus + newStatus.ObservedGeneration = c.Object.Generation + if newStatus.Conditions == nil { + newStatus.Conditions = make([]metav1.Condition, 0) + } + + newStatus.Backends.Origin.Name = c.Object.Spec.Backend.Name + + // set defaults + if newStatus.Routes == nil { + newStatus.Routes = make([]rolloutv1alpha1.BackendRouteStatus, len(c.Object.Spec.Routes)) + for i, routeSpec := range c.Object.Spec.Routes { + newStatus.Routes[i] = rolloutv1alpha1.BackendRouteStatus{ + CrossClusterObjectReference: routeSpec, + Condition: &metav1.Condition{ + Type: rolloutv1alpha1.BackendRoutingRouteReady, + Status: metav1.ConditionUnknown, + LastTransitionTime: metav1.Now(), + Reason: "Unknown", + }, + } + } + } + + if c.Object.Spec.Forwarding != nil && c.Object.Spec.Forwarding.HTTP != nil { + for i, routeStatus := range newStatus.Routes { + if routeStatus.Forwarding == nil { + newStatus.Routes[i].Forwarding = &rolloutv1alpha1.BackendRouteForwardingStatuses{} + } + + if c.Object.Spec.Forwarding.HTTP.Origin != nil && newStatus.Routes[i].Forwarding.Origin == nil { + newStatus.Routes[i].Forwarding.Origin = &rolloutv1alpha1.BackendRouteForwardingStatus{ + BackendName: c.Object.Spec.Forwarding.HTTP.Origin.BackendName, + Conditions: rolloutv1alpha1.BackendConditions{ + Ready: ptr.To(false), + }, + } + c.setRouteCondition(i, metav1.ConditionFalse, "WaitForSync", "spec updated, waiting for sync") + } + + if c.Object.Spec.Forwarding.HTTP.Stable != nil && newStatus.Routes[i].Forwarding.Stable == nil { + newStatus.Routes[i].Forwarding.Stable = &rolloutv1alpha1.BackendRouteForwardingStatus{ + BackendName: c.Object.Spec.Forwarding.HTTP.Stable.BackendName, + Conditions: rolloutv1alpha1.BackendConditions{ + Ready: ptr.To(false), + }, + } + c.setRouteCondition(i, metav1.ConditionFalse, "WaitForSync", "spec updated, waiting for sync") + } + + if c.Object.Spec.Forwarding.HTTP.Canary != nil && newStatus.Routes[i].Forwarding.Canary == nil { + newStatus.Routes[i].Forwarding.Canary = &rolloutv1alpha1.BackendRouteForwardingStatus{ + BackendName: c.Object.Spec.Forwarding.HTTP.Canary.BackendName, + Conditions: rolloutv1alpha1.BackendConditions{ + Ready: ptr.To(false), + }, + } + c.setRouteCondition(i, metav1.ConditionFalse, "WaitForSync", "spec updated, waiting for sync") + } + } + } + + if c.Object.Spec.ForkedBackends != nil { + newStatus.Backends.Canary.Name = c.Object.Spec.ForkedBackends.Canary.Name + if newStatus.Backends.Canary.Conditions.Ready == nil { + newStatus.Backends.Canary.Conditions.Ready = ptr.To(false) + } + newStatus.Backends.Stable.Name = c.Object.Spec.ForkedBackends.Stable.Name + if newStatus.Backends.Stable.Conditions.Ready == nil { + newStatus.Backends.Stable.Conditions.Ready = ptr.To(false) + } + } + }) +} + +func (c *syncContext) checkOriginRoute(forwardingStatus *rolloutv1alpha1.BackendRouteForwardingStatuses) (created, deleted bool) { + forwardingSpec := c.Object.Spec.Forwarding + + // origin is not ready and not in terminating + if forwardingSpec != nil && forwardingSpec.HTTP != nil && forwardingSpec.HTTP.Origin != nil && + forwardingStatus.Origin != nil && !ptr.Deref(forwardingStatus.Origin.Conditions.Ready, false) && + !ptr.Deref(forwardingStatus.Origin.Conditions.Terminating, false) { + created = true + } + + // forwardingSpec.Origin is nil and forwardingStatus.Origin is not nil + if (forwardingSpec == nil || forwardingSpec.HTTP == nil || forwardingSpec.HTTP.Origin == nil) && + forwardingStatus != nil && forwardingStatus.Origin != nil { + deleted = true + } + + return created, deleted +} + +func (c *syncContext) checkCanaryRoute(forwardingStatus *rolloutv1alpha1.BackendRouteForwardingStatuses) (created, deleted bool) { + forwardingSpec := c.Object.Spec.Forwarding + + if forwardingSpec != nil && forwardingSpec.HTTP != nil && forwardingSpec.HTTP.Canary != nil && + forwardingStatus.Canary != nil && !ptr.Deref(forwardingStatus.Canary.Conditions.Ready, false) && + !ptr.Deref(forwardingStatus.Canary.Conditions.Terminating, false) { + created = true + } + + if (forwardingSpec == nil || forwardingSpec.HTTP == nil || forwardingSpec.HTTP.Canary == nil) && + forwardingStatus != nil && forwardingStatus.Canary != nil { + deleted = true + } + return created, deleted +} + +func (c *syncContext) setCondition(condType string, status metav1.ConditionStatus, reason string) { + meta.SetStatusCondition(&c.NewStatus.Conditions, metav1.Condition{ + Type: condType, + Status: status, + ObservedGeneration: c.Object.Generation, + Reason: reason, + }) +} + +func (c *syncContext) setRouteCondition(routeIndex int, status metav1.ConditionStatus, reason, msg string) { + if routeIndex >= len(c.NewStatus.Routes) { + return + } + if c.NewStatus.Routes[routeIndex].Condition.Status != status { + c.NewStatus.Routes[routeIndex].Condition.Status = status + c.NewStatus.Routes[routeIndex].Condition.LastTransitionTime = metav1.Now() + } + c.NewStatus.Routes[routeIndex].Condition.Reason = reason + c.NewStatus.Routes[routeIndex].Condition.Message = msg +} + +func (c *syncContext) Status() rolloutv1alpha1.BackendRoutingStatus { + c.Initialize() + + backendReady := true + reason := "Ready" + if len(c.NewStatus.Backends.Origin.Name) > 0 { + if !ptr.Deref(c.NewStatus.Backends.Origin.Conditions.Ready, false) { + backendReady = false + reason = "OriginBackendNotReady" + } + } + if len(c.NewStatus.Backends.Stable.Name) > 0 { + if !ptr.Deref(c.NewStatus.Backends.Stable.Conditions.Ready, false) || c.NewStatus.Backends.Stable.Conditions.Terminating != nil { + backendReady = false + reason = "StableBackendNotReady" + } + } + if len(c.NewStatus.Backends.Canary.Name) > 0 { + if !ptr.Deref(c.NewStatus.Backends.Canary.Conditions.Ready, false) || c.NewStatus.Backends.Canary.Conditions.Terminating != nil { + backendReady = false + reason = "CanaryBackendNotReady" + } + } + + if backendReady { + c.setCondition(rolloutv1alpha1.BackendRoutingBackendReady, metav1.ConditionTrue, "Ready") + } else { + c.setCondition(rolloutv1alpha1.BackendRoutingBackendReady, metav1.ConditionFalse, reason) + } + + routesReady := true + for i := range c.NewStatus.Routes { + if c.NewStatus.Routes[i].Condition.Status != metav1.ConditionTrue { + routesReady = false + reason = c.NewStatus.Routes[i].Condition.Reason + break + } + } + + if routesReady { + c.setCondition(rolloutv1alpha1.BackendRoutingRouteReady, metav1.ConditionTrue, "Ready") + } else { + c.setCondition(rolloutv1alpha1.BackendRoutingRouteReady, metav1.ConditionFalse, reason) + } + + if backendReady && routesReady { + c.setCondition(rolloutv1alpha1.BackendRoutingReady, metav1.ConditionTrue, "Ready") + } else { + c.setCondition(rolloutv1alpha1.BackendRoutingReady, metav1.ConditionFalse, "NotReady") + } + return *c.NewStatus +} diff --git a/pkg/controllers/podcanarylabel/podcanarylabel.go b/pkg/controllers/podcanarylabel/podcanarylabel.go index c081a46..c142b97 100644 --- a/pkg/controllers/podcanarylabel/podcanarylabel.go +++ b/pkg/controllers/podcanarylabel/podcanarylabel.go @@ -20,6 +20,7 @@ import ( "context" corev1 "k8s.io/api/core/v1" + rolloutapi "kusionstack.io/kube-api/rollout" "kusionstack.io/kube-utils/controller/mixin" "kusionstack.io/kube-utils/multicluster" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -29,7 +30,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - rolloutapi "kusionstack.io/kube-api/rollout" "kusionstack.io/rollout/pkg/controllers/registry" rolloutcontroller "kusionstack.io/rollout/pkg/controllers/rollout" "kusionstack.io/rollout/pkg/utils" @@ -104,7 +104,7 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque // this workload is not controlled by rollout, we need to make sure pod revision label is not added updated, err := utils.UpdateOnConflict(ctx, r.Client, r.Client, pod, func() error { utils.MutateLabels(pod, func(labels map[string]string) { - delete(labels, rolloutapi.LabelTrafficRevision) + delete(labels, rolloutapi.LabelTrafficLane) }) return nil }) @@ -120,37 +120,37 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque return reconcile.Result{}, nil } - podRevision := recognizePodRevision(pc, r.Client, workloadObj.Object, pod) + trafficLane := recognizeTrafficLane(pc, r.Client, workloadObj.Object, pod) // patch pod label updated, err := utils.UpdateOnConflict(ctx, r.Client, r.Client, pod, func() error { utils.MutateLabels(pod, func(labels map[string]string) { - labels[rolloutapi.LabelTrafficRevision] = podRevision + labels[rolloutapi.LabelTrafficLane] = trafficLane }) return nil }) if updated { - logger.V(2).Info("updated pod revision label value", "revision", podRevision) + logger.V(2).Info("updated pod traffic lane label value", "traffic-lane", trafficLane) } return reconcile.Result{}, err } -func recognizePodRevision(pc workload.PodControl, reader client.Reader, workloadObj client.Object, pod *corev1.Pod) string { +func recognizeTrafficLane(pc workload.PodControl, reader client.Reader, workloadObj client.Object, pod *corev1.Pod) string { if workload.IsCanary(workloadObj) { // canary workload, always set pod revision to canary - return rolloutapi.LabelValueTrafficRevisionCanary + return rolloutapi.LabelValueTrafficLaneCanary } if !workload.IsProgressing(workloadObj) { // workload is not progressing, set pod revision to base - return rolloutapi.LabelValueTrafficRevisionBase + return rolloutapi.LabelValueTrafficLaneStable } // workload is progressing, set updated pod revision to canary if updated, _ := pc.IsUpdatedPod(reader, workloadObj, pod); updated { - return rolloutapi.LabelValueTrafficRevisionCanary + return rolloutapi.LabelValueTrafficLaneCanary } - return rolloutapi.LabelValueTrafficRevisionBase + return rolloutapi.LabelValueTrafficLaneStable } diff --git a/pkg/controllers/registry/backend.go b/pkg/controllers/registry/backend.go index 51e3bf4..8f357d1 100644 --- a/pkg/controllers/registry/backend.go +++ b/pkg/controllers/registry/backend.go @@ -32,14 +32,14 @@ const ( var Backends = NewBackendRegistry() type BackendRegistry interface { - genericregistry.Registry[schema.GroupVersionKind, backend.Store] + genericregistry.Registry[schema.GroupVersionKind, backend.InClusterBackend] } func NewBackendRegistry() BackendRegistry { - return genericregistry.New[schema.GroupVersionKind, backend.Store]() + return genericregistry.New[schema.GroupVersionKind, backend.InClusterBackend]() } func InitBackendRegistry(mgr manager.Manager) (bool, error) { - Backends.Register(service.GVK, service.NewStorage(mgr)) + Backends.Register(service.GVK, service.New()) return true, nil } diff --git a/pkg/controllers/registry/route.go b/pkg/controllers/registry/route.go index 435e4d6..29e4eba 100644 --- a/pkg/controllers/registry/route.go +++ b/pkg/controllers/registry/route.go @@ -32,14 +32,14 @@ const ( var Routes = NewRouteRegistry() type RouteRegistry interface { - genericregistry.Registry[schema.GroupVersionKind, route.Store] + genericregistry.Registry[schema.GroupVersionKind, route.Route] } func NewRouteRegistry() RouteRegistry { - return genericregistry.New[schema.GroupVersionKind, route.Store]() + return genericregistry.New[schema.GroupVersionKind, route.Route]() } func InitRouteRegistry(mgr manager.Manager) (bool, error) { - Routes.Register(ingress.GVK, ingress.NewStorage(mgr)) + Routes.Register(ingress.GVK, ingress.NewStorage()) return true, nil } diff --git a/pkg/controllers/rollout/event_handler.go b/pkg/controllers/rollout/event_handler.go index f315e29..385d06c 100644 --- a/pkg/controllers/rollout/event_handler.go +++ b/pkg/controllers/rollout/event_handler.go @@ -22,12 +22,12 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/reconcile" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rollout/rollout_controller.go b/pkg/controllers/rollout/rollout_controller.go index c0e2e26..a5a065d 100644 --- a/pkg/controllers/rollout/rollout_controller.go +++ b/pkg/controllers/rollout/rollout_controller.go @@ -32,6 +32,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" errorsutil "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/validation" + "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1/condition" kubeutilclient "kusionstack.io/kube-utils/client" "kusionstack.io/kube-utils/controller/mixin" "kusionstack.io/kube-utils/multicluster" @@ -44,9 +47,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" - "kusionstack.io/kube-api/rollout/v1alpha1/condition" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/pkg/features/ontimestrategy" diff --git a/pkg/controllers/rollout/utils.go b/pkg/controllers/rollout/utils.go index abaab28..eb89d50 100644 --- a/pkg/controllers/rollout/utils.go +++ b/pkg/controllers/rollout/utils.go @@ -26,11 +26,11 @@ import ( "k8s.io/client-go/discovery" memory "k8s.io/client-go/discovery/cached" "k8s.io/client-go/rest" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/pkg/features/ontimestrategy" diff --git a/pkg/controllers/rollout/utils_test.go b/pkg/controllers/rollout/utils_test.go index a51f1c3..168fa8b 100644 --- a/pkg/controllers/rollout/utils_test.go +++ b/pkg/controllers/rollout/utils_test.go @@ -22,8 +22,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/control/control.go b/pkg/controllers/rolloutrun/control/control.go index a54ce1a..36eef36 100644 --- a/pkg/controllers/rolloutrun/control/control.go +++ b/pkg/controllers/rolloutrun/control/control.go @@ -28,13 +28,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" kubeutilclient "kusionstack.io/kube-utils/client" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/batch.go b/pkg/controllers/rolloutrun/executor/batch.go index 4b9849b..e619a02 100644 --- a/pkg/controllers/rolloutrun/executor/batch.go +++ b/pkg/controllers/rolloutrun/executor/batch.go @@ -22,9 +22,9 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/intstr" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/control" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/batch_test.go b/pkg/controllers/rolloutrun/executor/batch_test.go index d3eedab..bb47346 100644 --- a/pkg/controllers/rolloutrun/executor/batch_test.go +++ b/pkg/controllers/rolloutrun/executor/batch_test.go @@ -24,10 +24,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/controllers/rolloutrun/executor/canary.go b/pkg/controllers/rolloutrun/executor/canary.go index 9eed2de..2c646b9 100644 --- a/pkg/controllers/rolloutrun/executor/canary.go +++ b/pkg/controllers/rolloutrun/executor/canary.go @@ -20,11 +20,11 @@ import ( "fmt" "time" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/control" "kusionstack.io/rollout/pkg/workload" ) @@ -136,14 +136,18 @@ func (e *canaryExecutor) modifyTraffic(ctx *ExecutorContext, op string) (bool, t if rolloutRun.Spec.Canary.Traffic != nil { var err error switch op { - case "forkStable": - opResult, err = ctx.TrafficManager.ForkStable() - case "forkCanary": - opResult, err = ctx.TrafficManager.ForkCanary() - case "revertStable": - opResult, err = ctx.TrafficManager.RevertStable() - case "revertCanary": - opResult, err = ctx.TrafficManager.RevertCanary() + case "forkBackends": + opResult, err = ctx.TrafficManager.ForkBackends() + case "initializeRoute": + opResult, err = ctx.TrafficManager.InitializeRoute() + case "addCanaryRoute": + opResult, err = ctx.TrafficManager.AddCanaryRoute() + case "deleteCanaryRoute": + opResult, err = ctx.TrafficManager.DeleteCanaryRoute() + case "resetRoute": + opResult, err = ctx.TrafficManager.ResetRoute() + case "deleteForkedBackends": + opResult, err = ctx.TrafficManager.DeleteForkedBackends() } if err != nil { logger.Error(err, "failed to modify traffic", "operation", op) @@ -171,8 +175,14 @@ func (e *canaryExecutor) doCanary(ctx *ExecutorContext) (bool, time.Duration, er logger := ctx.GetCanaryLogger() rolloutRun := ctx.RolloutRun - // 1. do traffic initialization - prepareDone, retry := e.modifyTraffic(ctx, "forkStable") + // 1. fork backends + prepareDone, retry := e.modifyTraffic(ctx, "forkBackends") + if !prepareDone { + return false, retry, nil + } + + // 2. do traffic initialization + prepareDone, retry = e.modifyTraffic(ctx, "initializeRoute") if !prepareDone { return false, retry, nil } @@ -222,8 +232,8 @@ func (e *canaryExecutor) doCanary(ctx *ExecutorContext) (bool, time.Duration, er } } - // 3 do canary traffic routing - trafficCanaryDone, retry := e.modifyTraffic(ctx, "forkCanary") + // 3. add canary route + trafficCanaryDone, retry := e.modifyTraffic(ctx, "addCanaryRoute") if !trafficCanaryDone { return false, retry, nil } @@ -241,7 +251,7 @@ func appendBuiltinPodTemplateMetadataPatch(patch *rolloutv1alpha1.MetadataPatch) } patch.Labels[rolloutapi.LabelCanary] = "true" - patch.Labels[rolloutapi.LabelTrafficRevision] = "canary" + patch.Labels[rolloutapi.LabelTrafficLane] = rolloutapi.LabelValueTrafficLaneCanary return patch } @@ -249,7 +259,7 @@ func (e *canaryExecutor) release(ctx *ExecutorContext) (bool, time.Duration, err // firstly try to stop webhook e.webhook.Cancel(ctx) - done, retry := e.modifyTraffic(ctx, "revertCanary") + done, retry := e.modifyTraffic(ctx, "deleteCanaryRoute") if !done { return false, retry, nil } @@ -271,7 +281,12 @@ func (e *canaryExecutor) release(ctx *ExecutorContext) (bool, time.Duration, err } } - done, retry = e.modifyTraffic(ctx, "revertStable") + done, retry = e.modifyTraffic(ctx, "resetRoute") + if !done { + return false, retry, nil + } + + done, retry = e.modifyTraffic(ctx, "deleteForkedBackends") if !done { return false, retry, nil } diff --git a/pkg/controllers/rolloutrun/executor/context.go b/pkg/controllers/rolloutrun/executor/context.go index a40a9cc..8817a70 100644 --- a/pkg/controllers/rolloutrun/executor/context.go +++ b/pkg/controllers/rolloutrun/executor/context.go @@ -25,9 +25,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/record" "k8s.io/utils/ptr" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/traffic" "kusionstack.io/rollout/pkg/workload" ) @@ -54,6 +54,7 @@ func (c *ExecutorContext) Initialize() { c.NewStatus = c.RolloutRun.Status.DeepCopy() } newStatus := c.NewStatus + newStatus.ObservedGeneration = c.RolloutRun.Generation if len(newStatus.Phase) == 0 { newStatus.Phase = rolloutv1alpha1.RolloutRunPhaseInitial diff --git a/pkg/controllers/rolloutrun/executor/context_test.go b/pkg/controllers/rolloutrun/executor/context_test.go index a3c7b99..99599a6 100644 --- a/pkg/controllers/rolloutrun/executor/context_test.go +++ b/pkg/controllers/rolloutrun/executor/context_test.go @@ -18,7 +18,6 @@ package executor import ( "github.com/stretchr/testify/suite" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/controllers/rolloutrun/executor/default.go b/pkg/controllers/rolloutrun/executor/default.go index b7693f4..ff5ba99 100644 --- a/pkg/controllers/rolloutrun/executor/default.go +++ b/pkg/controllers/rolloutrun/executor/default.go @@ -4,10 +4,10 @@ import ( "time" "github.com/go-logr/logr" - ctrl "sigs.k8s.io/controller-runtime" - rolloutapis "kusionstack.io/kube-api/rollout" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/controllers/rolloutrun/executor/default_test.go b/pkg/controllers/rolloutrun/executor/default_test.go index 9a5a9f9..5981c7e 100644 --- a/pkg/controllers/rolloutrun/executor/default_test.go +++ b/pkg/controllers/rolloutrun/executor/default_test.go @@ -13,12 +13,12 @@ import ( "k8s.io/client-go/kubernetes/scheme" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/record" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/log/zap" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" "kusionstack.io/rollout/pkg/workload/statefulset" ) diff --git a/pkg/controllers/rolloutrun/executor/do_command.go b/pkg/controllers/rolloutrun/executor/do_command.go index 6417103..a2bad55 100644 --- a/pkg/controllers/rolloutrun/executor/do_command.go +++ b/pkg/controllers/rolloutrun/executor/do_command.go @@ -1,10 +1,9 @@ package executor import ( - ctrl "sigs.k8s.io/controller-runtime" - rolloutapis "kusionstack.io/kube-api/rollout" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" ) // doCommand diff --git a/pkg/controllers/rolloutrun/executor/do_hook.go b/pkg/controllers/rolloutrun/executor/do_hook.go index 96b5d1f..2a7a78e 100644 --- a/pkg/controllers/rolloutrun/executor/do_hook.go +++ b/pkg/controllers/rolloutrun/executor/do_hook.go @@ -5,8 +5,8 @@ import ( "github.com/samber/lo" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/controllers/rolloutrun/executor/do_hook_test.go b/pkg/controllers/rolloutrun/executor/do_hook_test.go index 20b0e1f..02a0ae9 100644 --- a/pkg/controllers/rolloutrun/executor/do_hook_test.go +++ b/pkg/controllers/rolloutrun/executor/do_hook_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/suite" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/controllers/rolloutrun/executor/step_lifecycle.go b/pkg/controllers/rolloutrun/executor/step_lifecycle.go index 193d4cc..588b8e8 100644 --- a/pkg/controllers/rolloutrun/executor/step_lifecycle.go +++ b/pkg/controllers/rolloutrun/executor/step_lifecycle.go @@ -23,9 +23,9 @@ import ( "github.com/samber/lo" corev1 "k8s.io/api/core/v1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/control" ) diff --git a/pkg/controllers/rolloutrun/rolloutrun_controller.go b/pkg/controllers/rolloutrun/rolloutrun_controller.go index 2d858aa..be8633a 100644 --- a/pkg/controllers/rolloutrun/rolloutrun_controller.go +++ b/pkg/controllers/rolloutrun/rolloutrun_controller.go @@ -25,6 +25,9 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/kube-api/rollout/v1alpha1/condition" kubeutilclient "kusionstack.io/kube-utils/client" "kusionstack.io/kube-utils/controller/mixin" "kusionstack.io/kube-utils/multicluster/clusterinfo" @@ -35,9 +38,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" - "kusionstack.io/kube-api/rollout/v1alpha1/condition" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/controllers/rolloutrun/executor" "kusionstack.io/rollout/pkg/controllers/rolloutrun/traffic" diff --git a/pkg/controllers/rolloutrun/traffic/traffic_manager.go b/pkg/controllers/rolloutrun/traffic/traffic_manager.go index 7c0caab..edddd14 100644 --- a/pkg/controllers/rolloutrun/traffic/traffic_manager.go +++ b/pkg/controllers/rolloutrun/traffic/traffic_manager.go @@ -20,12 +20,12 @@ import ( "context" "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/meta" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + clientutil "kusionstack.io/kube-utils/client" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" - "kusionstack.io/rollout/pkg/utils" ) type Manager struct { @@ -75,44 +75,78 @@ func (m *Manager) With(logger logr.Logger, workloads []rolloutv1alpha1.RolloutRu m.strategy = strategy } -func (m *Manager) ForkStable() (controllerutil.OperationResult, error) { +func (m *Manager) ForkBackends() (controllerutil.OperationResult, error) { return m.mutateRouting(func(routing *rolloutv1alpha1.BackendRouting) error { - if routing.Spec.Forwarding == nil { - routing.Spec.Forwarding = &rolloutv1alpha1.BackendForwarding{} + if routing.Spec.ForkedBackends == nil { + routing.Spec.ForkedBackends = &rolloutv1alpha1.ForkedBackends{} } - routing.Spec.Forwarding.Stable = rolloutv1alpha1.StableBackendRule{ + + routing.Spec.ForkedBackends.Stable = rolloutv1alpha1.ForkedBackend{ Name: routing.Spec.Backend.Name + "-stable", } + + routing.Spec.ForkedBackends.Canary = rolloutv1alpha1.ForkedBackend{ + Name: routing.Spec.Backend.Name + "-canary", + } return nil }) } -func (m *Manager) ForkCanary() (controllerutil.OperationResult, error) { +func (m *Manager) DeleteForkedBackends() (controllerutil.OperationResult, error) { + return m.mutateRouting(func(routing *rolloutv1alpha1.BackendRouting) error { + routing.Spec.ForkedBackends = nil + return nil + }) +} + +func (m *Manager) InitializeRoute() (controllerutil.OperationResult, error) { return m.mutateRouting(func(routing *rolloutv1alpha1.BackendRouting) error { if routing.Spec.Forwarding == nil { - routing.Spec.Forwarding = &rolloutv1alpha1.BackendForwarding{} + routing.Spec.Forwarding = &rolloutv1alpha1.BackendForwarding{ + HTTP: &rolloutv1alpha1.HTTPForwarding{}, + } } - routing.Spec.Forwarding.Canary = rolloutv1alpha1.CanaryBackendRule{ - Name: routing.Spec.Backend.Name + "-canary", - TrafficStrategy: *m.strategy, + if m.strategy.HTTP.StableTraffic == nil { + routing.Spec.Forwarding.HTTP.Origin = &rolloutv1alpha1.OriginHTTPForwarding{ + BackendName: routing.Spec.ForkedBackends.Stable.Name, + } + } else { + routing.Spec.Forwarding.HTTP.Stable = &rolloutv1alpha1.StableHTTPForwarding{ + HTTPRouteRule: *m.strategy.HTTP.StableTraffic, + } } return nil }) } -func (m *Manager) RevertCanary() (controllerutil.OperationResult, error) { +func (m *Manager) ResetRoute() (controllerutil.OperationResult, error) { + return m.mutateRouting(func(routing *rolloutv1alpha1.BackendRouting) error { + routing.Spec.Forwarding = nil + return nil + }) +} + +func (m *Manager) AddCanaryRoute() (controllerutil.OperationResult, error) { return m.mutateRouting(func(routing *rolloutv1alpha1.BackendRouting) error { if routing.Spec.Forwarding == nil { - return nil + routing.Spec.Forwarding = &rolloutv1alpha1.BackendForwarding{ + HTTP: &rolloutv1alpha1.HTTPForwarding{}, + } + } + routing.Spec.Forwarding.HTTP.Canary = &rolloutv1alpha1.CanaryHTTPForwarding{ + CanaryHTTPRouteRule: m.strategy.HTTP.CanaryHTTPRouteRule, } - routing.Spec.Forwarding.Canary = rolloutv1alpha1.CanaryBackendRule{} return nil }) } -func (m *Manager) RevertStable() (controllerutil.OperationResult, error) { +func (m *Manager) DeleteCanaryRoute() (controllerutil.OperationResult, error) { return m.mutateRouting(func(routing *rolloutv1alpha1.BackendRouting) error { - routing.Spec.Forwarding = nil + if routing.Spec.Forwarding != nil && + routing.Spec.Forwarding.HTTP != nil && + routing.Spec.Forwarding.HTTP.Canary != nil { + routing.Spec.Forwarding.HTTP.Canary = nil + } return nil }) } @@ -133,7 +167,7 @@ func (m *Manager) mutateRouting(mutateFn func(routing *rolloutv1alpha1.BackendRo } for i := range topo.routings { routing := topo.routings[i] - updated, err := utils.UpdateOnConflict(ctx, m.client, m.client, routing, func() error { + updated, err := clientutil.UpdateOnConflict(ctx, m.client, m.client, routing, func(routing *rolloutv1alpha1.BackendRouting) error { return mutateFn(routing) }) if err != nil { @@ -156,7 +190,7 @@ func (m *Manager) CheckReady() bool { } for _, routing := range topo.routings { if routing.Generation == routing.Status.ObservedGeneration && - routing.Status.Phase == rolloutv1alpha1.Ready { + meta.IsStatusConditionTrue(routing.Status.Conditions, rolloutv1alpha1.BackendRoutingReady) { continue } return false diff --git a/pkg/controllers/rolloutrun/webhook/manager.go b/pkg/controllers/rolloutrun/webhook/manager.go index 249a6d5..53514f2 100644 --- a/pkg/controllers/rolloutrun/webhook/manager.go +++ b/pkg/controllers/rolloutrun/webhook/manager.go @@ -21,7 +21,6 @@ import ( "sync" "k8s.io/apimachinery/pkg/types" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/controllers/rolloutrun/webhook/probe/http/http.go b/pkg/controllers/rolloutrun/webhook/probe/http/http.go index 9408f88..d22707d 100644 --- a/pkg/controllers/rolloutrun/webhook/probe/http/http.go +++ b/pkg/controllers/rolloutrun/webhook/probe/http/http.go @@ -26,8 +26,8 @@ import ( "time" "k8s.io/client-go/transport" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" ) diff --git a/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go b/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go index 0dd8ae5..c365f32 100644 --- a/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go +++ b/pkg/controllers/rolloutrun/webhook/probe/http/http_test.go @@ -20,8 +20,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" ) diff --git a/pkg/controllers/rolloutrun/webhook/worker.go b/pkg/controllers/rolloutrun/webhook/worker.go index 9d7a8d5..306e74b 100644 --- a/pkg/controllers/rolloutrun/webhook/worker.go +++ b/pkg/controllers/rolloutrun/webhook/worker.go @@ -23,8 +23,8 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/utils/ptr" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe/http" ) diff --git a/pkg/controllers/rolloutrun/webhook/worker_test.go b/pkg/controllers/rolloutrun/webhook/worker_test.go index 10f16b8..81f1bd5 100644 --- a/pkg/controllers/rolloutrun/webhook/worker_test.go +++ b/pkg/controllers/rolloutrun/webhook/worker_test.go @@ -22,8 +22,8 @@ import ( "github.com/stretchr/testify/suite" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe" ) diff --git a/pkg/controllers/traffictopology/adapter.go b/pkg/controllers/traffictopology/adapter.go index 13f7da3..08d58af 100644 --- a/pkg/controllers/traffictopology/adapter.go +++ b/pkg/controllers/traffictopology/adapter.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/retry" "k8s.io/utils/ptr" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster/clusterinfo" rsFrameController "kusionstack.io/resourceconsist/pkg/frame/controller" "sigs.k8s.io/controller-runtime/pkg/client" @@ -33,7 +34,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/ratelimiter" - "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload" diff --git a/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go b/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go index 5a27acf..ff891cd 100644 --- a/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go +++ b/pkg/controllers/traffictopology/traffictopology_controller_suite_test.go @@ -30,6 +30,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster" "kusionstack.io/kube-utils/multicluster/clusterinfo" "kusionstack.io/kube-utils/multicluster/clusterprovider" @@ -42,7 +43,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" - "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/registry" ) diff --git a/pkg/controllers/traffictopology/traffictopology_controller_test.go b/pkg/controllers/traffictopology/traffictopology_controller_test.go index 9504a8a..a1d1254 100644 --- a/pkg/controllers/traffictopology/traffictopology_controller_test.go +++ b/pkg/controllers/traffictopology/traffictopology_controller_test.go @@ -28,9 +28,8 @@ import ( "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "kusionstack.io/kube-utils/multicluster/clusterinfo" - "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/kube-utils/multicluster/clusterinfo" ) var _ = Describe("traffic-topology-controller", func() { diff --git a/pkg/controllers/traffictopology/types.go b/pkg/controllers/traffictopology/types.go index e9f2f46..4edb502 100644 --- a/pkg/controllers/traffictopology/types.go +++ b/pkg/controllers/traffictopology/types.go @@ -15,9 +15,8 @@ package traffictopology import ( - rsFrameController "kusionstack.io/resourceconsist/pkg/frame/controller" - "kusionstack.io/kube-api/rollout/v1alpha1" + rsFrameController "kusionstack.io/resourceconsist/pkg/frame/controller" ) var _ rsFrameController.IEmployer = TPEmployer{} diff --git a/pkg/route/ingress/route.go b/pkg/route/ingress/route.go index 1ad0bde..85a8cf6 100644 --- a/pkg/route/ingress/route.go +++ b/pkg/route/ingress/route.go @@ -21,14 +21,15 @@ import ( "github.com/samber/lo" networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + clientutil "kusionstack.io/kube-utils/client" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" v1 "sigs.k8s.io/gateway-api/apis/v1" - "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/route" ) @@ -40,14 +41,10 @@ type ingressRoute struct { cluster string } -func (i *ingressRoute) GetRouteObject() client.Object { - return i.obj -} - -func (i *ingressRoute) AddCanaryRoute(ctx context.Context, forwarding *v1alpha1.BackendForwarding) error { +func (i *ingressRoute) AddCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error { igs := i.obj - strategy := forwarding.Canary.TrafficStrategy + strategy := obj.Spec.Forwarding.HTTP.Canary annosCanaryNeedCheck := map[string]string{ AnnoCanary: "true", @@ -63,22 +60,22 @@ func (i *ingressRoute) AddCanaryRoute(ctx context.Context, forwarding *v1alpha1. isMseIngress := igs.Spec.IngressClassName != nil && *igs.Spec.IngressClassName == MseIngressClass - if strategy.HTTP != nil { - if len(strategy.HTTP.Matches) > 0 { - if len(strategy.HTTP.Matches[0].Headers) > 0 { - annosCanaryNeedCheck[AnnoCanaryHeader] = string(strategy.HTTP.Matches[0].Headers[0].Name) - annosCanaryNeedCheck[AnnoCanaryHeaderValue] = strategy.HTTP.Matches[0].Headers[0].Value + if strategy != nil { + if len(strategy.Matches) > 0 { + if len(strategy.Matches[0].Headers) > 0 { + annosCanaryNeedCheck[AnnoCanaryHeader] = string(strategy.Matches[0].Headers[0].Name) + annosCanaryNeedCheck[AnnoCanaryHeaderValue] = strategy.Matches[0].Headers[0].Value } - if isMseIngress && len(strategy.HTTP.Matches[0].QueryParams) > 0 { - annosCanaryNeedCheck[AnnoMseCanaryQuery] = string(strategy.HTTP.Matches[0].QueryParams[0].Name) - annosCanaryNeedCheck[AnnoMseCanaryQueryValue] = strategy.HTTP.Matches[0].QueryParams[0].Value + if isMseIngress && len(strategy.Matches[0].QueryParams) > 0 { + annosCanaryNeedCheck[AnnoMseCanaryQuery] = string(strategy.Matches[0].QueryParams[0].Name) + annosCanaryNeedCheck[AnnoMseCanaryQueryValue] = strategy.Matches[0].QueryParams[0].Value } - } else if strategy.HTTP.Weight != nil { - annosCanaryNeedCheck[AnnoCanaryWeight] = strconv.Itoa(int(*strategy.HTTP.Weight)) + } else if strategy.Weight != nil { + annosCanaryNeedCheck[AnnoCanaryWeight] = strconv.Itoa(int(*strategy.Weight)) } - if isMseIngress && len(strategy.HTTP.Filters) > 0 { - filter, ok := lo.Find(strategy.HTTP.Filters, func(item v1.HTTPRouteFilter) bool { + if isMseIngress && len(strategy.Filters) > 0 { + filter, ok := lo.Find(strategy.Filters, func(item v1.HTTPRouteFilter) bool { return item.RequestHeaderModifier != nil }) if ok { @@ -100,28 +97,30 @@ func (i *ingressRoute) AddCanaryRoute(ctx context.Context, forwarding *v1alpha1. } canaryIgs := &networkingv1.Ingress{} - canaryIgs.Name = igs.Name + "-canary" + canaryIgs.Name = i.canaryIngressName() canaryIgs.Namespace = igs.Namespace + forked := obj.Spec.ForkedBackends + _, err := controllerutil.CreateOrUpdate(clusterinfo.WithCluster(ctx, i.cluster), i.client, canaryIgs, func() error { canaryIgs.Spec = igs.Spec if canaryIgs.Spec.DefaultBackend != nil { - if canaryIgs.Spec.DefaultBackend.Service != nil && canaryIgs.Spec.DefaultBackend.Service.Name == forwarding.Stable.Name { - canaryIgs.Spec.DefaultBackend.Service.Name = forwarding.Canary.Name + if canaryIgs.Spec.DefaultBackend.Service != nil && canaryIgs.Spec.DefaultBackend.Service.Name == forked.Stable.Name { + canaryIgs.Spec.DefaultBackend.Service.Name = forked.Canary.Name } - if canaryIgs.Spec.DefaultBackend.Resource != nil && canaryIgs.Spec.DefaultBackend.Resource.Name == forwarding.Stable.Name { - canaryIgs.Spec.DefaultBackend.Resource.Name = forwarding.Canary.Name + if canaryIgs.Spec.DefaultBackend.Resource != nil && canaryIgs.Spec.DefaultBackend.Resource.Name == forked.Stable.Name { + canaryIgs.Spec.DefaultBackend.Resource.Name = forked.Canary.Name } } for idx, rule := range canaryIgs.Spec.Rules { for k, path := range rule.HTTP.Paths { - if path.Backend.Service != nil && path.Backend.Service.Name == forwarding.Stable.Name { - path.Backend.Service.Name = forwarding.Canary.Name + if path.Backend.Service != nil && path.Backend.Service.Name == forked.Stable.Name { + path.Backend.Service.Name = forked.Canary.Name } - if path.Backend.Resource != nil && path.Backend.Resource.Name == forwarding.Stable.Name { - path.Backend.Resource.Name = forwarding.Canary.Name + if path.Backend.Resource != nil && path.Backend.Resource.Name == forked.Stable.Name { + path.Backend.Resource.Name = forked.Canary.Name } canaryIgs.Spec.Rules[idx].HTTP.Paths[k] = path } @@ -138,74 +137,82 @@ func (i *ingressRoute) AddCanaryRoute(ctx context.Context, forwarding *v1alpha1. delete(canaryIgs.Annotations, key) } } + + if canaryIgs.Labels == nil { + canaryIgs.Labels = make(map[string]string) + } + canaryIgs.Labels[rolloutapi.LabelCanary] = "true" + canaryIgs.Labels[rolloutapi.LabelTemporaryResource] = "true" return nil }) return err } -func (i *ingressRoute) RemoveCanaryRoute(ctx context.Context) error { - canaryIgsName := i.obj.Name + "-canary" - canaryIgs := &networkingv1.Ingress{} - err := i.client.Get(clusterinfo.WithCluster(ctx, i.cluster), types.NamespacedName{ - Namespace: i.obj.Namespace, - Name: canaryIgsName, - }, canaryIgs) - if err != nil { - if !errors.IsNotFound(err) { - return err - } - return nil - } - return i.client.Delete(clusterinfo.WithCluster(ctx, i.cluster), canaryIgs) +func (i *ingressRoute) canaryIngressName() string { + return i.obj.Name + "-canary" } -func (i *ingressRoute) ChangeBackend(ctx context.Context, detail route.BackendChangeDetail) error { - igs := i.obj - needChange := false +func (i *ingressRoute) DeleteCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error { + canaryIgsName := i.canaryIngressName() + canaryIgs := &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: i.obj.Namespace, + Name: canaryIgsName, + }, + } + err := i.client.Delete(clusterinfo.WithCluster(ctx, i.cluster), canaryIgs) + return client.IgnoreNotFound(err) +} - if igs.Spec.DefaultBackend != nil { - if detail.Kind == "Service" { - if igs.Spec.DefaultBackend.Service != nil && igs.Spec.DefaultBackend.Service.Name == detail.Src { - igs.Spec.DefaultBackend.Service.Name = detail.Dst - needChange = true - } - } else { - if igs.Spec.DefaultBackend.Resource != nil && igs.Spec.DefaultBackend.Resource.Kind == detail.Kind && - igs.Spec.DefaultBackend.Resource.Name == detail.Src { - igs.Spec.DefaultBackend.Resource.Name = detail.Dst - needChange = true +func (i *ingressRoute) changeCanaryBackend(kind, from, to string) func(ingress *networkingv1.Ingress) error { + return func(ingress *networkingv1.Ingress) error { + if ingress.Spec.DefaultBackend != nil { + backend := ingress.Spec.DefaultBackend + switch kind { + case "Service": + if backend.Service != nil && backend.Service.Name == from { + backend.Service.Name = to + } + default: + if backend.Resource != nil && backend.Resource.Name == from { + backend.Resource.Name = to + } } } - } - for idx, rule := range igs.Spec.Rules { - for k, path := range rule.HTTP.Paths { - if detail.Kind == "Service" { - if path.Backend.Service != nil && path.Backend.Service.Name == detail.Src { - path.Backend.Service.Name = detail.Dst - igs.Spec.Rules[idx].HTTP.Paths[k] = path - needChange = true - } - } else { - if path.Backend.Resource != nil && path.Backend.Resource.Kind == detail.Kind && - path.Backend.Resource.Name == detail.Src { - path.Backend.Resource.Name = detail.Dst - igs.Spec.Rules[idx].HTTP.Paths[k] = path - needChange = true + for _, rule := range ingress.Spec.Rules { + for k := range rule.HTTP.Paths { + backend := rule.HTTP.Paths[k].Backend + switch kind { + case "Service": + if backend.Service != nil && backend.Service.Name == from { + backend.Service.Name = to + } + default: + if backend.Resource != nil && backend.Resource.Name == from { + backend.Resource.Name = to + } } } } + return nil } +} - if needChange { - return i.client.Update(clusterinfo.WithCluster(ctx, i.cluster), igs) - } +func (i *ingressRoute) ChangeOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, to string) error { + modify := i.changeCanaryBackend(originBackend.Kind, originBackend.Name, to) + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, i.cluster), i.client, i.client, i.obj, modify) + return err +} - return nil +func (i *ingressRoute) ResetOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, from string) error { + modify := i.changeCanaryBackend(originBackend.Kind, from, originBackend.Name) + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, i.cluster), i.client, i.client, i.obj, modify) + return err } -var _ route.IRoute = &ingressRoute{} +var _ route.RouteControl = &ingressRoute{} func generateMultiHeadersAnno(headers []v1.HTTPHeader) string { if len(headers) == 0 { diff --git a/pkg/route/ingress/store.go b/pkg/route/ingress/store.go index efcd391..220f309 100644 --- a/pkg/route/ingress/store.go +++ b/pkg/route/ingress/store.go @@ -15,59 +15,39 @@ package ingress import ( - "context" "fmt" networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" "kusionstack.io/rollout/pkg/route" + "kusionstack.io/rollout/pkg/utils/accessor" ) type IgsStore struct { - client client.Client + accessor.ObjectAccessor } -func NewStorage(mgr manager.Manager) route.Store { +func NewStorage() route.Route { return &IgsStore{ - client: mgr.GetClient(), + ObjectAccessor: accessor.NewObjectAccessor( + GVK, + &networkingv1.Ingress{}, + &networkingv1.IngressList{}, + ), } } -func (i *IgsStore) GroupVersionKind() schema.GroupVersionKind { - return GVK -} - -func (i *IgsStore) NewObject() client.Object { - return &networkingv1.Ingress{} -} - -func (i *IgsStore) Wrap(cluster string, obj client.Object) (route.IRoute, error) { - igs, ok := obj.(*networkingv1.Ingress) +func (i *IgsStore) Wrap(client client.Client, cluster string, route client.Object) (route.RouteControl, error) { + igs, ok := route.(*networkingv1.Ingress) if !ok { return nil, fmt.Errorf("not Ingress") } return &ingressRoute{ - client: i.client, + client: client, obj: igs, cluster: cluster, }, nil } -func (i *IgsStore) Get(ctx context.Context, cluster, namespace, name string) (route.IRoute, error) { - var igs networkingv1.Ingress - err := i.client.Get(clusterinfo.WithCluster(ctx, cluster), types.NamespacedName{ - Namespace: namespace, - Name: name, - }, &igs) - if err != nil { - return nil, err - } - return i.Wrap(cluster, &igs) -} - -var _ route.Store = &IgsStore{} +var _ route.Route = &IgsStore{} diff --git a/pkg/route/interface.go b/pkg/route/interface.go index 05ca1c0..ccc4fff 100644 --- a/pkg/route/interface.go +++ b/pkg/route/interface.go @@ -18,10 +18,10 @@ package route import ( "context" - "k8s.io/apimachinery/pkg/runtime/schema" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/utils/accessor" ) type BackendChangeDetail struct { @@ -31,19 +31,19 @@ type BackendChangeDetail struct { ApiVersion string } -type IRoute interface { - GetRouteObject() client.Object - AddCanaryRoute(ctx context.Context, forwarding *v1alpha1.BackendForwarding) error - RemoveCanaryRoute(ctx context.Context) error - ChangeBackend(ctx context.Context, detail BackendChangeDetail) error +type RouteControl interface { + ChangeOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, to string) error + ResetOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, from string) error + + AddCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error + DeleteCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error } -type Store interface { - GroupVersionKind() schema.GroupVersionKind - // NewObject returns a new instance of the route type - NewObject() client.Object +type Route interface { + accessor.ObjectAccessor + // Wrap get a client.Object and returns a route interface - Wrap(cluster string, obj client.Object) (IRoute, error) + Wrap(client client.Client, cluster string, route client.Object) (RouteControl, error) // Get returns a wrapped route interface - Get(ctx context.Context, cluster, namespace, name string) (IRoute, error) + // Get(ctx context.Context, cluster, namespace, name string) (RouteControl, error) } diff --git a/pkg/utils/accessor/accessor.go b/pkg/utils/accessor/accessor.go new file mode 100644 index 0000000..232121d --- /dev/null +++ b/pkg/utils/accessor/accessor.go @@ -0,0 +1,41 @@ +package accessor + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type ObjectAccessor interface { + // GroupVersionKind returns the GroupVersionKind of the workload + GroupVersionKind() schema.GroupVersionKind + // NewObject returns a new instance of the workload type + NewObject() client.Object + // NewObjectList returns a new instance of the workload list type + NewObjectList() client.ObjectList +} + +type genericAccessorImpl struct { + gvk schema.GroupVersionKind + obj client.Object + listObj client.ObjectList +} + +func NewObjectAccessor(gvk schema.GroupVersionKind, obj client.Object, listObj client.ObjectList) ObjectAccessor { + return &genericAccessorImpl{ + gvk: gvk, + obj: obj, + listObj: listObj, + } +} + +func (g *genericAccessorImpl) GroupVersionKind() schema.GroupVersionKind { + return g.gvk +} + +func (g *genericAccessorImpl) NewObject() client.Object { + return g.obj.DeepCopyObject().(client.Object) +} + +func (g *genericAccessorImpl) NewObjectList() client.ObjectList { + return g.listObj.DeepCopyObject().(client.ObjectList) +} diff --git a/pkg/utils/progressinginfos/progressing_info.go b/pkg/utils/progressinginfos/progressing_info.go index 458cb26..c620135 100644 --- a/pkg/utils/progressinginfos/progressing_info.go +++ b/pkg/utils/progressinginfos/progressing_info.go @@ -7,10 +7,10 @@ import ( "sort" "github.com/samber/lo" - runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/kube-api/rollout" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/utils/progressinginfos/progressing_info_test.go b/pkg/utils/progressinginfos/progressing_info_test.go index 4f864c2..95aea74 100644 --- a/pkg/utils/progressinginfos/progressing_info_test.go +++ b/pkg/utils/progressinginfos/progressing_info_test.go @@ -21,7 +21,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/utils/slice.go b/pkg/utils/slice.go index a4b78e1..c794ae1 100644 --- a/pkg/utils/slice.go +++ b/pkg/utils/slice.go @@ -17,7 +17,6 @@ package utils import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/utils/slice_test.go b/pkg/utils/slice_test.go index f1dab99..e3e2095 100644 --- a/pkg/utils/slice_test.go +++ b/pkg/utils/slice_test.go @@ -20,7 +20,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" - "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/webhook/mutating/pod/pod_mutating.go b/pkg/webhook/mutating/pod/pod_mutating.go index f7567ca..b432eed 100644 --- a/pkg/webhook/mutating/pod/pod_mutating.go +++ b/pkg/webhook/mutating/pod/pod_mutating.go @@ -27,11 +27,11 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "kusionstack.io/kube-api/rollout" "kusionstack.io/kube-utils/controller/mixin" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "kusionstack.io/kube-api/rollout" "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/utils/progressinginfos" "kusionstack.io/rollout/pkg/webhook/generic" diff --git a/pkg/webhook/validating/rollout/rollout_validating.go b/pkg/webhook/validating/rollout/rollout_validating.go index 581587b..ff466b2 100644 --- a/pkg/webhook/validating/rollout/rollout_validating.go +++ b/pkg/webhook/validating/rollout/rollout_validating.go @@ -24,10 +24,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" rolloutvalidation "kusionstack.io/rollout/apis/rollout/v1alpha1/validation" "kusionstack.io/rollout/pkg/controllers/registry" ) diff --git a/pkg/workload/collaset/accessor.go b/pkg/workload/collaset/accessor.go index d417113..a6a578c 100644 --- a/pkg/workload/collaset/accessor.go +++ b/pkg/workload/collaset/accessor.go @@ -24,6 +24,7 @@ import ( operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" + "kusionstack.io/rollout/pkg/utils/accessor" "kusionstack.io/rollout/pkg/workload" ) @@ -38,14 +39,18 @@ var ObjectTypeError = fmt.Errorf("object must be %s", GVK.GroupKind().String()) var _ workload.Accessor = &accessorImpl{} -type accessorImpl struct{} - -func New() workload.Accessor { - return &accessorImpl{} +type accessorImpl struct { + accessor.ObjectAccessor } -func (w *accessorImpl) GroupVersionKind() schema.GroupVersionKind { - return GVK +func New() workload.Accessor { + return &accessorImpl{ + ObjectAccessor: accessor.NewObjectAccessor( + GVK, + &operatingv1alpha1.CollaSet{}, + &operatingv1alpha1.CollaSetList{}, + ), + } } func (c *accessorImpl) DependentWorkloadGVKs() []schema.GroupVersionKind { @@ -56,14 +61,6 @@ func (w *accessorImpl) Watchable() bool { return true } -func (w *accessorImpl) NewObject() client.Object { - return &operatingv1alpha1.CollaSet{} -} - -func (w *accessorImpl) NewObjectList() client.ObjectList { - return &operatingv1alpha1.CollaSetList{} -} - func (w *accessorImpl) GetInfo(cluster string, object client.Object) (*workload.Info, error) { obj, err := checkObj(object) if err != nil { diff --git a/pkg/workload/collaset/release.go b/pkg/workload/collaset/release.go index cd55c75..918edb3 100644 --- a/pkg/workload/collaset/release.go +++ b/pkg/workload/collaset/release.go @@ -22,9 +22,9 @@ import ( "k8s.io/utils/ptr" operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/workload/info.go b/pkg/workload/info.go index ba777f2..a72ae18 100644 --- a/pkg/workload/info.go +++ b/pkg/workload/info.go @@ -26,11 +26,11 @@ import ( "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" ) diff --git a/pkg/workload/interface.go b/pkg/workload/interface.go index dc0f1bd..bdd865f 100644 --- a/pkg/workload/interface.go +++ b/pkg/workload/interface.go @@ -18,9 +18,10 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/kube-api/rollout/v1alpha1" + "kusionstack.io/rollout/pkg/utils/accessor" ) // Accessor defines the functions to access the workload. @@ -29,14 +30,9 @@ import ( // - BatchReleaseControl // - PodControl type Accessor interface { - // GroupVersionKind returns the GroupVersionKind of the workload - GroupVersionKind() schema.GroupVersionKind + accessor.ObjectAccessor // DependentWorkloadGVKs returns the dependent workloadds' GroupVersionKinds DependentWorkloadGVKs() []schema.GroupVersionKind - // NewObject returns a new instance of the workload type - NewObject() client.Object - // NewObjectList returns a new instance of the workload list type - NewObjectList() client.ObjectList // Watchable indicates whether this workload type can be watched from the API server. Watchable() bool // GetInfo returns a info represent workload diff --git a/pkg/workload/matcher.go b/pkg/workload/matcher.go index a532995..4186b61 100644 --- a/pkg/workload/matcher.go +++ b/pkg/workload/matcher.go @@ -19,7 +19,6 @@ package workload import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/pkg/workload/poddecoration/accessor.go b/pkg/workload/poddecoration/accessor.go index 1d6fb03..2eaf4c5 100644 --- a/pkg/workload/poddecoration/accessor.go +++ b/pkg/workload/poddecoration/accessor.go @@ -23,6 +23,7 @@ import ( operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" + "kusionstack.io/rollout/pkg/utils/accessor" "kusionstack.io/rollout/pkg/workload" ) @@ -35,14 +36,18 @@ var GVK = schema.GroupVersionKind{ var ObjectTypeError = fmt.Errorf("object must be %s", GVK.GroupKind().String()) -type accessorImpl struct{} - -func New() workload.Accessor { - return &accessorImpl{} +type accessorImpl struct { + accessor.ObjectAccessor } -func (w *accessorImpl) GroupVersionKind() schema.GroupVersionKind { - return GVK +func New() workload.Accessor { + return &accessorImpl{ + ObjectAccessor: accessor.NewObjectAccessor( + GVK, + &operatingv1alpha1.PodDecoration{}, + &operatingv1alpha1.PodDecorationList{}, + ), + } } func (c *accessorImpl) DependentWorkloadGVKs() []schema.GroupVersionKind { @@ -53,14 +58,6 @@ func (w *accessorImpl) Watchable() bool { return true } -func (w *accessorImpl) NewObject() client.Object { - return &operatingv1alpha1.PodDecoration{} -} - -func (w *accessorImpl) NewObjectList() client.ObjectList { - return &operatingv1alpha1.PodDecorationList{} -} - func (w *accessorImpl) GetInfo(cluster string, object client.Object) (*workload.Info, error) { obj, err := checkObj(object) if err != nil { diff --git a/pkg/workload/statefulset/accessor.go b/pkg/workload/statefulset/accessor.go index f0b5fd5..52034ac 100644 --- a/pkg/workload/statefulset/accessor.go +++ b/pkg/workload/statefulset/accessor.go @@ -22,6 +22,7 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" + "kusionstack.io/rollout/pkg/utils/accessor" "kusionstack.io/rollout/pkg/workload" ) @@ -29,14 +30,18 @@ var GVK = appsv1.SchemeGroupVersion.WithKind("StatefulSet") var ObjectTypeError = fmt.Errorf("object must be %s", GVK.GroupKind().String()) -type accessorImpl struct{} - -func New() workload.Accessor { - return &accessorImpl{} +type accessorImpl struct { + accessor.ObjectAccessor } -func (s *accessorImpl) GroupVersionKind() schema.GroupVersionKind { - return GVK +func New() workload.Accessor { + return &accessorImpl{ + ObjectAccessor: accessor.NewObjectAccessor( + GVK, + &appsv1.StatefulSet{}, + &appsv1.StatefulSetList{}, + ), + } } func (c *accessorImpl) DependentWorkloadGVKs() []schema.GroupVersionKind { @@ -47,14 +52,6 @@ func (s *accessorImpl) Watchable() bool { return true } -func (s *accessorImpl) NewObject() client.Object { - return &appsv1.StatefulSet{} -} - -func (s *accessorImpl) NewObjectList() client.ObjectList { - return &appsv1.StatefulSetList{} -} - func (s *accessorImpl) GetInfo(cluster string, object client.Object) (*workload.Info, error) { obj, err := checkObj(object) if err != nil { diff --git a/pkg/workload/statefulset/release.go b/pkg/workload/statefulset/release.go index d7f5411..61f3748 100644 --- a/pkg/workload/statefulset/release.go +++ b/pkg/workload/statefulset/release.go @@ -22,9 +22,9 @@ import ( appsv1 "k8s.io/api/apps/v1" "k8s.io/utils/ptr" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/workload" ) diff --git a/pkg/workload/util.go b/pkg/workload/util.go index 058ec8e..f2a5ffb 100644 --- a/pkg/workload/util.go +++ b/pkg/workload/util.go @@ -21,11 +21,11 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/utils" ) diff --git a/test/e2e/builder/rollout_builder.go b/test/e2e/builder/rollout_builder.go index 81959a9..4479ca7 100644 --- a/test/e2e/builder/rollout_builder.go +++ b/test/e2e/builder/rollout_builder.go @@ -17,7 +17,6 @@ package builder import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/test/e2e/builder/rolloutstrategy_builder.go b/test/e2e/builder/rolloutstrategy_builder.go index 8e5b494..1d4d8d6 100644 --- a/test/e2e/builder/rolloutstrategy_builder.go +++ b/test/e2e/builder/rolloutstrategy_builder.go @@ -20,7 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ) diff --git a/test/e2e/collaset_test.go b/test/e2e/collaset_test.go index d064edf..f1b6950 100644 --- a/test/e2e/collaset_test.go +++ b/test/e2e/collaset_test.go @@ -28,10 +28,10 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/utils/ptr" operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" - "sigs.k8s.io/controller-runtime/pkg/client" - rolloutapi "kusionstack.io/kube-api/rollout" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe/http" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload/collaset" diff --git a/test/e2e/statefulset_test.go b/test/e2e/statefulset_test.go index 604be4c..63e110e 100644 --- a/test/e2e/statefulset_test.go +++ b/test/e2e/statefulset_test.go @@ -26,11 +26,11 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - rolloutapi "kusionstack.io/kube-api/rollout" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/rolloutrun/webhook/probe/http" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload/statefulset" diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index acee29a..0a03f0f 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/utils/ptr" operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -37,7 +38,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/rollout/pkg/controllers/initializers" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/test/e2e/controller" From b1921bee3d5180bafe355cb38d016c45e9fc992d Mon Sep 17 00:00:00 2001 From: zoumo Date: Sun, 20 Jul 2025 21:21:10 +0800 Subject: [PATCH 04/10] feat: support HTTPRoute and InferencePool --- cmd/rollout/import_known_versions.go | 8 +- .../gateway.networking.k8s.io_httproutes.yaml | 4878 +++++++++++++++++ ...ce.networking.x-k8s.io_inferencepools.yaml | 279 + config/kind/workload/bases/rollout.yaml | 1 + config/kind/workload/bases/traffic.yaml | 61 + go.mod | 53 +- go.sum | 66 +- .../backendrouting_controller.go | 170 +- .../backendrouting_controller_test.go | 18 +- .../backendrouting/sync_context.go | 18 +- pkg/controllers/registry/backend.go | 6 +- pkg/controllers/registry/route.go | 8 +- pkg/controllers/rolloutrun/executor/canary.go | 57 +- .../rolloutrun/executor/context.go | 12 +- .../rolloutrun/rolloutrun_controller.go | 4 +- .../backend/inferencepool/accessor.go | 48 + pkg/{ => trafficrouting}/backend/interface.go | 0 .../backend/service/accessor.go | 36 +- pkg/trafficrouting/backend/util.go | 15 + .../control}/traffic_manager.go | 12 +- pkg/trafficrouting/route/httproute/control.go | 292 + pkg/trafficrouting/route/httproute/route.go | 58 + .../route/ingress/const.go | 0 .../route/ingress/control.go} | 74 +- .../route/ingress/route.go} | 30 +- pkg/{ => trafficrouting}/route/interface.go | 30 +- 26 files changed, 5978 insertions(+), 256 deletions(-) create mode 100644 config/crd/bases/gateway.networking.k8s.io_httproutes.yaml create mode 100644 config/crd/bases/inference.networking.x-k8s.io_inferencepools.yaml create mode 100644 pkg/trafficrouting/backend/inferencepool/accessor.go rename pkg/{ => trafficrouting}/backend/interface.go (100%) rename pkg/{ => trafficrouting}/backend/service/accessor.go (59%) create mode 100644 pkg/trafficrouting/backend/util.go rename pkg/{controllers/rolloutrun/traffic => trafficrouting/control}/traffic_manager.go (93%) create mode 100644 pkg/trafficrouting/route/httproute/control.go create mode 100644 pkg/trafficrouting/route/httproute/route.go rename pkg/{ => trafficrouting}/route/ingress/const.go (100%) rename pkg/{route/ingress/route.go => trafficrouting/route/ingress/control.go} (74%) rename pkg/{route/ingress/store.go => trafficrouting/route/ingress/route.go} (57%) rename pkg/{ => trafficrouting}/route/interface.go (51%) diff --git a/cmd/rollout/import_known_versions.go b/cmd/rollout/import_known_versions.go index 85ba542..a0b1013 100644 --- a/cmd/rollout/import_known_versions.go +++ b/cmd/rollout/import_known_versions.go @@ -19,10 +19,14 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" operatingv1alpha1 "kusionstack.io/kube-api/apps/v1alpha1" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + gwapiv1alpha2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) func init() { - utilruntime.Must(rolloutv1alpha1.AddToScheme(clientgoscheme.Scheme)) - utilruntime.Must(operatingv1alpha1.AddToScheme(clientgoscheme.Scheme)) + utilruntime.Must(rolloutv1alpha1.Install(clientgoscheme.Scheme)) + utilruntime.Must(operatingv1alpha1.Install(clientgoscheme.Scheme)) + utilruntime.Must(gatewayapiv1.Install(clientgoscheme.Scheme)) + utilruntime.Must(gwapiv1alpha2.Install(clientgoscheme.Scheme)) //+kubebuilder:scaffold:scheme } diff --git a/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml new file mode 100644 index 0000000..3577b52 --- /dev/null +++ b/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml @@ -0,0 +1,4878 @@ +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.3.0 + gateway.networking.k8s.io/channel: standard + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + HTTPRoute provides a way to route HTTP requests. This includes the capability + to match requests by hostname, path, header, or query param. Filters can be + used to specify additional processing steps. Backends specify where matching + requests should be routed. + 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 HTTPRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames that should match against the HTTP Host + header to select a HTTPRoute used to process the request. Implementations + MUST ignore any port value specified in the HTTP Host header while + performing a match and (absent of any applicable header modification + configuration) MUST forward this header unmodified to the backend. + + Valid values for Hostnames are determined by RFC 1123 definition of a + hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + If a hostname is specified by both the Listener and HTTPRoute, there + must be at least one intersecting hostname for the HTTPRoute to be + attached to the Listener. For example: + + * A Listener with `test.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` would + all match. On the other hand, `example.com` and `test.example.net` would + not match. + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + If both the Listener and HTTPRoute have specified hostnames, any + HTTPRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + HTTPRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + If both the Listener and HTTPRoute have specified hostnames, and none + match with the criteria above, then the HTTPRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + overlapping wildcard matching and exact matching hostnames), precedence must + be given to rules from the HTTPRoute with the largest number of: + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + + If ties exist across multiple Routes, the matching precedence rules for + HTTPRouteMatches takes over. + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + 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 + maxItems: 16 + type: array + parentRefs: + description: |- + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + ParentRefs must be _distinct_. This means either that: + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + Some examples: + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. 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 other kinds of cross-namespace reference. + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + 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. + + 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. + + 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 + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: |- + HTTPRouteRule defines semantics for matching an HTTP request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive a 500 status code. + + See the HTTPBackendRef definition for the rules about what makes a single + HTTPBackendRef invalid. + + When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive a 500 status code. + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic must receive a 500. Implementations may + choose how that 50 percent is determined. + + When a HTTPBackendRef refers to a Service that has no ready endpoints, + implementations SHOULD return a 503 for requests to that backend instead. + If an implementation chooses to do this, all of the above rules for 500 responses + MUST also apply for responses that return a 503. + + Support: Core for Kubernetes Service + + Support: Extended for Kubernetes ServiceImport + + Support: Implementation-specific for any other resource + + Support for weight: Core + items: + description: |- + HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + properties: + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + + Support: Implementation-specific (For broader support of filters, use the + Filters field in HTTPRouteRule.) + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + Wherever possible, implementations SHOULD implement filters in the order + they are specified. + + Implementations MAY choose to implement this ordering strictly, rejecting + any combination or order of filters that cannot be supported. If implementations + choose a strict interpretation of filter ordering, they MUST clearly document + that behavior. + + To reject an invalid combination or order of filters, implementations SHOULD + consider the Route Rules with this configuration invalid. If all Route Rules + in a Route are invalid, the entire Route would be considered invalid. If only + a portion of Route Rules are invalid, implementations MUST set the + "PartiallyInvalid" condition for the Route. + + Conformance-levels at this level are defined based on the type of filter: + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation cannot support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + For example, take the following matches configuration: + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + Note: The precedence of RegularExpression path matches are implementation-specific. + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + description: "HTTPRouteMatch defines the predicate used to match requests to a given\naction. Multiple match types are ANDed together, i.e. the match will\nevaluate to true only if all conditions are satisfied.\n\nFor example, the match below will match a HTTP request only if its path\nstarts with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t value \"v1\"\n\n```" + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + Support: Core (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies HTTP method matcher. + When specified, this route will be matched only if the request has the + specified method. + + Support: Extended + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: |- + Path specifies a HTTP request path matcher. If this field is not + specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + Support: Core (Exact, PathPrefix) + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + Support: Extended (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 64 + type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + Support: Extended + 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 + timeouts: + description: |- + Timeouts defines the timeouts that can be configured for an HTTP request. + + Support: Extended + properties: + backendRequest: + description: |- + BackendRequest specifies a timeout for an individual request from the gateway + to a backend. This covers the time from when the request first starts being + sent from the gateway to when the full response has been received from the backend. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + An entire client HTTP transaction with a gateway, covered by the Request timeout, + may result in more than one call from the gateway to the destination backend, + for example, if automatic retries are supported. + + The value of BackendRequest must be a Gateway API Duration string as defined by + GEP-2257. When this field is unspecified, its behavior is implementation-specific; + when specified, the value of BackendRequest must be no more than the value of the + Request timeout (since the Request timeout encompasses the BackendRequest timeout). + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + request: + description: |- + Request specifies the maximum duration for a gateway to respond to an HTTP request. + If the gateway has not been able to respond before this deadline is met, the gateway + MUST return a timeout error. + + For example, setting the `rules.timeouts.request` field to the value `10s` in an + `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds + to complete. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + This timeout is intended to cover as close to the whole request-response transaction + as possible although an implementation MAY choose to start the timeout after the entire + request stream has been received instead of immediately after the transaction is + initiated by the client. + + The value of Request is a Gateway API Duration string as defined by GEP-2257. When this + field is unspecified, request timeout behavior is implementation-specific. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + type: object + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + * The Route refers to a nonexistent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + 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 + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus 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. + + 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. + + 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 + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: |- + HTTPRoute provides a way to route HTTP requests. This includes the capability + to match requests by hostname, path, header, or query param. Filters can be + used to specify additional processing steps. Backends specify where matching + requests should be routed. + 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 HTTPRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of hostnames that should match against the HTTP Host + header to select a HTTPRoute used to process the request. Implementations + MUST ignore any port value specified in the HTTP Host header while + performing a match and (absent of any applicable header modification + configuration) MUST forward this header unmodified to the backend. + + Valid values for Hostnames are determined by RFC 1123 definition of a + hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + If a hostname is specified by both the Listener and HTTPRoute, there + must be at least one intersecting hostname for the HTTPRoute to be + attached to the Listener. For example: + + * A Listener with `test.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames, or have specified at + least one of `test.example.com` or `*.example.com`. + * A Listener with `*.example.com` as the hostname matches HTTPRoutes + that have either not specified any hostnames or have specified at least + one hostname that matches the Listener hostname. For example, + `*.example.com`, `test.example.com`, and `foo.test.example.com` would + all match. On the other hand, `example.com` and `test.example.net` would + not match. + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + + If both the Listener and HTTPRoute have specified hostnames, any + HTTPRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + HTTPRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + If both the Listener and HTTPRoute have specified hostnames, and none + match with the criteria above, then the HTTPRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + overlapping wildcard matching and exact matching hostnames), precedence must + be given to rules from the HTTPRoute with the largest number of: + + * Characters in a matching non-wildcard hostname. + * Characters in a matching hostname. + + If ties exist across multiple Routes, the matching precedence rules for + HTTPRouteMatches takes over. + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + 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 + maxItems: 16 + type: array + parentRefs: + description: |- + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + ParentRefs must be _distinct_. This means either that: + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + Some examples: + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. 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 other kinds of cross-namespace reference. + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + The API object must be valid in the cluster; the Group and Kind must + be registered in the cluster for this reference to be valid. + 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. + + 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. + + 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 + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: |- + HTTPRouteRule defines semantics for matching an HTTP request based on + conditions (matches), processing it (filters), and forwarding the request to + an API object (backendRefs). + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. + + Failure behavior here depends on how many BackendRefs are specified and + how many are invalid. + + If *all* entries in BackendRefs are invalid, and there are also no filters + specified in this route rule, *all* traffic which matches this rule MUST + receive a 500 status code. + + See the HTTPBackendRef definition for the rules about what makes a single + HTTPBackendRef invalid. + + When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + requests that would have otherwise been routed to an invalid backend. If + multiple backends are specified, and some are invalid, the proportion of + requests that would otherwise have been routed to an invalid backend + MUST receive a 500 status code. + + For example, if two backends are specified with equal weights, and one is + invalid, 50 percent of traffic must receive a 500. Implementations may + choose how that 50 percent is determined. + + When a HTTPBackendRef refers to a Service that has no ready endpoints, + implementations SHOULD return a 503 for requests to that backend instead. + If an implementation chooses to do this, all of the above rules for 500 responses + MUST also apply for responses that return a 503. + + Support: Core for Kubernetes Service + + Support: Extended for Kubernetes ServiceImport + + Support: Implementation-specific for any other resource + + Support for weight: Core + items: + description: |- + HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. + + Note that when a namespace different than the local namespace is specified, a + ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + properties: + filters: + description: |- + Filters defined at this level should be executed if and only if the + request is being forwarded to the backend defined here. + + Support: Implementation-specific (For broader support of filters, use the + Filters field in HTTPRouteRule.) + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + filters: + description: |- + Filters define the filters that are applied to requests that match + this rule. + + Wherever possible, implementations SHOULD implement filters in the order + they are specified. + + Implementations MAY choose to implement this ordering strictly, rejecting + any combination or order of filters that cannot be supported. If implementations + choose a strict interpretation of filter ordering, they MUST clearly document + that behavior. + + To reject an invalid combination or order of filters, implementations SHOULD + consider the Route Rules with this configuration invalid. If all Route Rules + in a Route are invalid, the entire Route would be considered invalid. If only + a portion of Route Rules are invalid, implementations MUST set the + "PartiallyInvalid" condition for the Route. + + Conformance-levels at this level are defined based on the type of filter: + + - ALL core filters MUST be supported by all implementations. + - Implementers are encouraged to support extended filters. + - Implementation-specific custom filters have no API guarantees across + implementations. + + Specifying the same filter multiple times is not supported unless explicitly + indicated in the filter. + + All filters are expected to be compatible with each other except for the + URLRewrite and RequestRedirect filters, which may not be combined. If an + implementation cannot support other combinations of filters, they must clearly + document that limitation. In cases where incompatible or unsupported + filters are specified and cause the `Accepted` condition to be set to status + `False`, implementations may use the `IncompatibleFilters` reason to specify + this configuration error. + + Support: Core + items: + description: |- + HTTPRouteFilter defines processing steps that must be completed during the + request or response lifecycle. HTTPRouteFilters are meant as an extension + point to express processing that may be done in Gateway implementations. Some + examples include request or response modification, implementing + authentication strategies, rate-limiting, and traffic shaping. API + guarantee/conformance is defined based on the type of the filter. + properties: + extensionRef: + description: |- + ExtensionRef is an optional, implementation-specific extension to the + "filter" behavior. For example, resource "myroutefilter" in group + "networking.example.net"). ExtensionRef MUST NOT be used for core and + extended filters. + + This filter can be used multiple times within the same rule. + + Support: Implementation-specific + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 referent. For example "HTTPRoute" or "Service". + 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. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + requestHeaderModifier: + description: |- + RequestHeaderModifier defines a schema for a filter that modifies request + headers. + + Support: Core + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: |- + RequestMirror defines a schema for a filter that mirrors requests. + Requests are sent to the specified destination, but responses from + that destination are ignored. + + This filter can be used multiple times within the same rule. Note that + not all implementations will be able to support mirroring to multiple + backends. + + Support: Extended + properties: + backendRef: + description: |- + BackendRef references a resource where mirrored requests are sent. + + Mirrored requests must be sent only to a single destination endpoint + within this BackendRef, irrespective of how many endpoints are present + within this BackendRef. + + If the referent cannot be found, this BackendRef is invalid and must be + dropped from the Gateway. The controller must ensure the "ResolvedRefs" + condition on the Route status is set to `status: False` and not configure + this backend in the underlying implementation. + + If there is a cross-namespace reference to an *existing* object + that is not allowed by a ReferenceGrant, the controller must ensure the + "ResolvedRefs" condition on the Route is set to `status: False`, + with the "RefNotPermitted" reason and not configure this backend in the + underlying implementation. + + In either error case, the Message of the `ResolvedRefs` Condition + should be used to provide more detail about the problem. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + Support: Core (Services with a type other than ExternalName) + + Support: Implementation-specific (Services with type ExternalName) + 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. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + fraction: + description: |- + Fraction represents the fraction of requests that should be + mirrored to BackendRef. + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + properties: + denominator: + default: 100 + format: int32 + minimum: 1 + type: integer + numerator: + format: int32 + minimum: 0 + type: integer + required: + - numerator + type: object + percent: + description: |- + Percent represents the percentage of requests that should be + mirrored to BackendRef. Its minimum value is 0 (indicating 0% of + requests) and its maximum value is 100 (indicating 100% of requests). + + Only one of Fraction or Percent may be specified. If neither field + is specified, 100% of requests will be mirrored. + format: int32 + maximum: 100 + minimum: 0 + type: integer + required: + - backendRef + type: object + requestRedirect: + description: |- + RequestRedirect defines a schema for a filter that responds to the + request with an HTTP redirection. + + Support: Core + properties: + hostname: + description: |- + Hostname is the hostname to be used in the value of the `Location` + header in the response. + When empty, the hostname in the `Host` header of the request is used. + + 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 + path: + description: |- + Path defines parameters used to modify the path of the incoming request. + The modified path is then used to construct the `Location` header. When + empty, the request path is used as-is. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: |- + Port is the port to be used in the value of the `Location` + header in the response. + + If no port is specified, the redirect port MUST be derived using the + following rules: + + * If redirect scheme is not-empty, the redirect port MUST be the well-known + port associated with the redirect scheme. Specifically "http" to port 80 + and "https" to port 443. If the redirect scheme does not have a + well-known port, the listener port of the Gateway SHOULD be used. + * If redirect scheme is empty, the redirect port MUST be the Gateway + Listener port. + + Implementations SHOULD NOT add the port number in the 'Location' + header in the following cases: + + * A Location header that will use HTTP (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 80. + * A Location header that will use HTTPS (whether that is determined via + the Listener protocol or the Scheme field) _and_ use port 443. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: |- + Scheme is the scheme to be used in the value of the `Location` header in + the response. When empty, the scheme of the request is used. + + Scheme redirects can affect the port of the redirect, for more information, + refer to the documentation for the port field of this filter. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Extended + enum: + - http + - https + type: string + statusCode: + default: 302 + description: |- + StatusCode is the HTTP status code to be used in response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + + Support: Core + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: |- + ResponseHeaderModifier defines a schema for a filter that modifies response + headers. + + Support: Extended + properties: + add: + description: |- + Add adds the given header(s) (name, value) to the request + before the action. It appends to any existing values associated + with the header name. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + add: + - name: "my-header" + value: "bar,baz" + + Output: + GET /foo HTTP/1.1 + my-header: foo,bar,baz + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: |- + Remove the given header(s) from the HTTP request before the action. The + value of Remove is a list of HTTP header names. Note that the header + names are case-insensitive (see + https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + + Input: + GET /foo HTTP/1.1 + my-header1: foo + my-header2: bar + my-header3: baz + + Config: + remove: ["my-header1", "my-header3"] + + Output: + GET /foo HTTP/1.1 + my-header2: bar + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + description: |- + Set overwrites the request with the given header (name, value) + before the action. + + Input: + GET /foo HTTP/1.1 + my-header: foo + + Config: + set: + - name: "my-header" + value: "bar" + + Output: + GET /foo HTTP/1.1 + my-header: bar + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, the first entry with + an equivalent name MUST be considered for a match. Subsequent entries + with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: + description: |- + Type identifies the type of filter to apply. As with other API fields, + types are classified into three conformance levels: + + - Core: Filter types and their corresponding configuration defined by + "Support: Core" in this package, e.g. "RequestHeaderModifier". All + implementations must support core filters. + + - Extended: Filter types and their corresponding configuration defined by + "Support: Extended" in this package, e.g. "RequestMirror". Implementers + are encouraged to support extended filters. + + - Implementation-specific: Filters that are defined and supported by + specific vendors. + In the future, filters showing convergence in behavior across multiple + implementations will be considered for inclusion in extended or core + conformance levels. Filter-specific configuration for such filters + is specified using the ExtensionRef field. `Type` should be set to + "ExtensionRef" for custom filters. + + Implementers are encouraged to define custom implementation types to + extend the core API with implementation-specific behavior. + + If a reference to a custom filter type cannot be resolved, the filter + MUST NOT be skipped. Instead, requests that would have been processed by + that filter MUST receive a HTTP error response. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: |- + URLRewrite defines a schema for a filter that modifies a request during forwarding. + + Support: Extended + properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + 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 + path: + description: |- + Path defines a path rewrite. + + Support: Extended + properties: + replaceFullPath: + description: |- + ReplaceFullPath specifies the value with which to replace the full path + of a request during a rewrite or redirect. + maxLength: 1024 + type: string + replacePrefixMatch: + description: |- + ReplacePrefixMatch specifies the value with which to replace the prefix + match of a request during a rewrite or redirect. For example, a request + to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch + of "/xyz" would be modified to "/xyz/bar". + + Note that this matches the behavior of the PathPrefix match type. This + matches full path elements. A path element refers to the list of labels + in the path split by the `/` separator. When specified, a trailing `/` is + ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all + match the prefix `/abc`, but the path `/abcd` would not. + + ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. + Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in + the implementation setting the Accepted Condition for the Route to `status: False`. + + Request Path | Prefix Match | Replace Prefix | Modified Path + maxLength: 1024 + type: string + type: + description: |- + Type defines the type of path modifier. Additional types may be + added in a future release of the API. + + Note that values may be added to this enum, implementations + must ensure that unknown values will not cause a crash. + + Unknown values here must result in the implementation setting the + Accepted Condition for the Route to `status: False`, with a + Reason of `UnsupportedValue`. + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + value: / + description: |- + Matches define conditions used for matching the rule against incoming + HTTP requests. Each match is independent, i.e. this rule will be matched + if **any** one of the matches is satisfied. + + For example, take the following matches configuration: + + ``` + matches: + - path: + value: "/foo" + headers: + - name: "version" + value: "v2" + - path: + value: "/v2/foo" + ``` + + For a request to match against this rule, a request must satisfy + EITHER of the two conditions: + + - path prefixed with `/foo` AND contains the header `version: v2` + - path prefix of `/v2/foo` + + See the documentation for HTTPRouteMatch on how to specify multiple + match conditions that should be ANDed together. + + If no matches are specified, the default is a prefix + path match on "/", which has the effect of matching every + HTTP request. + + Proxy or Load Balancer routing configuration generated from HTTPRoutes + MUST prioritize matches based on the following criteria, continuing on + ties. Across all rules specified on applicable Routes, precedence must be + given to the match having: + + * "Exact" path match. + * "Prefix" path match with largest number of characters. + * Method match. + * Largest number of header matches. + * Largest number of query param matches. + + Note: The precedence of RegularExpression path matches are implementation-specific. + + If ties still exist across multiple Routes, matching precedence MUST be + determined in order of the following criteria, continuing on ties: + + * The oldest Route based on creation timestamp. + * The Route appearing first in alphabetical order by + "{namespace}/{name}". + + If ties still exist within an HTTPRoute, matching precedence MUST be granted + to the FIRST matching rule (in list order) with a match meeting the above + criteria. + + When no rules matching a request have been successfully attached to the + parent a request is coming from, a HTTP 404 status code MUST be returned. + items: + description: "HTTPRouteMatch defines the predicate used to match requests to a given\naction. Multiple match types are ANDed together, i.e. the match will\nevaluate to true only if all conditions are satisfied.\n\nFor example, the match below will match a HTTP request only if its path\nstarts with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t value \"v1\"\n\n```" + properties: + headers: + description: |- + Headers specifies HTTP request header matchers. Multiple match values are + ANDed together, meaning, a request must match all the specified headers + to select the route. + items: + description: |- + HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request + headers. + properties: + name: + description: |- + Name is the name of the HTTP Header to be matched. Name matching MUST be + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + + If multiple entries specify equivalent header names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be ignored. Due to the + case-insensitivity of header names, "foo" and "Foo" are considered + equivalent. + + When a header is repeated in an HTTP request, it is + implementation-specific behavior as to how this is represented. + Generally, proxies should follow the guidance from the RFC: + https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding + processing a repeated header, with special handling for "Set-Cookie". + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the header. + + Support: Core (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression HeaderMatchType has implementation-specific + conformance, implementations can support POSIX, PCRE or any other dialects + of regular expressions. Please read the implementation's documentation to + determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: |- + Method specifies HTTP method matcher. + When specified, this route will be matched only if the request has the + specified method. + + Support: Extended + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + value: / + description: |- + Path specifies a HTTP request path matcher. If this field is not + specified, a default prefix match on the "/" path is provided. + properties: + type: + default: PathPrefix + description: |- + Type specifies how to match against the path Value. + + Support: Core (Exact, PathPrefix) + + Support: Implementation-specific (RegularExpression) + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: |- + QueryParams specifies HTTP query parameter matchers. Multiple match + values are ANDed together, meaning, a request must match all the + specified query parameters to select the route. + + Support: Extended + items: + description: |- + HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP + query parameters. + properties: + name: + description: |- + Name is the name of the HTTP query param to be matched. This must be an + exact string match. (See + https://tools.ietf.org/html/rfc7230#section-2.7.3). + + If multiple entries specify equivalent query param names, only the first + entry with an equivalent name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST be ignored. + + If a query param is repeated in an HTTP request, the behavior is + purposely left undefined, since different data planes have different + capabilities. However, it is *recommended* that implementations should + match against the first value of the param if the data plane supports it, + as this behavior is expected in other load balancing contexts outside of + the Gateway API. + + Users SHOULD NOT route traffic based on repeated query params to guard + themselves against potential differences in the implementations. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: |- + Type specifies how to match against the value of the query parameter. + + Support: Extended (Exact) + + Support: Implementation-specific (RegularExpression) + + Since RegularExpression QueryParamMatchType has Implementation-specific + conformance, implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the implementation's + documentation to determine the supported dialect. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 64 + type: array + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + Support: Extended + 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 + timeouts: + description: |- + Timeouts defines the timeouts that can be configured for an HTTP request. + + Support: Extended + properties: + backendRequest: + description: |- + BackendRequest specifies a timeout for an individual request from the gateway + to a backend. This covers the time from when the request first starts being + sent from the gateway to when the full response has been received from the backend. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + An entire client HTTP transaction with a gateway, covered by the Request timeout, + may result in more than one call from the gateway to the destination backend, + for example, if automatic retries are supported. + + The value of BackendRequest must be a Gateway API Duration string as defined by + GEP-2257. When this field is unspecified, its behavior is implementation-specific; + when specified, the value of BackendRequest must be no more than the value of the + Request timeout (since the Request timeout encompasses the BackendRequest timeout). + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + request: + description: |- + Request specifies the maximum duration for a gateway to respond to an HTTP request. + If the gateway has not been able to respond before this deadline is met, the gateway + MUST return a timeout error. + + For example, setting the `rules.timeouts.request` field to the value `10s` in an + `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds + to complete. + + Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + completely. Implementations that cannot completely disable the timeout MUST + instead interpret the zero duration as the longest possible value to which + the timeout can be set. + + This timeout is intended to cover as close to the whole request-response transaction + as possible although an implementation MAY choose to start the timeout after the entire + request stream has been received instead of immediately after the transaction is + initiated by the client. + + The value of Request is a Gateway API Duration string as defined by GEP-2257. When this + field is unspecified, request timeout behavior is implementation-specific. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + type: object + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + There are a number of cases where the "Accepted" condition may not be set + due to lack of controller visibility, that includes when: + + * The Route refers to a nonexistent parent. + * The Route is of a type that the controller does not support. + * The Route is in a namespace the controller does not have access to. + 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 + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus 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. + + 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. + + 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 + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/config/crd/bases/inference.networking.x-k8s.io_inferencepools.yaml b/config/crd/bases/inference.networking.x-k8s.io_inferencepools.yaml new file mode 100644 index 0000000..8b7b70e --- /dev/null +++ b/config/crd/bases/inference.networking.x-k8s.io_inferencepools.yaml @@ -0,0 +1,279 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: inferencepools.inference.networking.x-k8s.io +spec: + group: inference.networking.x-k8s.io + names: + kind: InferencePool + listKind: InferencePoolList + plural: inferencepools + singular: inferencepool + scope: Namespaced + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + description: InferencePool is the Schema for the InferencePools API. + 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: InferencePoolSpec defines the desired state of InferencePool + properties: + extensionRef: + description: Extension configures an endpoint picker as an extension + service. + properties: + failureMode: + default: FailClose + description: |- + Configures how the gateway handles the case when the extension is not responsive. + Defaults to failClose. + enum: + - FailOpen + - FailClose + type: string + group: + default: "" + description: |- + Group is the group of the referent. + The default value is "", representing the Core API group. + 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: Service + description: |- + Kind is the Kubernetes resource kind of the referent. For example + "Service". + + Defaults to "Service" when not specified. + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations MUST NOT + support ExternalName Services. + 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. + maxLength: 253 + minLength: 1 + type: string + portNumber: + description: |- + The port number on the service running the extension. When unspecified, + implementations SHOULD infer a default value of 9002 when the Kind is + Service. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + selector: + additionalProperties: + description: |- + LabelValue is the value of a label. This is used for validation + of maps. This matches the Kubernetes label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ + type: string + description: |- + Selector defines a map of labels to watch model server pods + that should be included in the InferencePool. + In some cases, implementations may translate this field to a Service selector, so this matches the simple + map used for Service selectors instead of the full Kubernetes LabelSelector type. + If sepecified, it will be applied to match the model server pods in the same namespace as the InferencePool. + Cross namesoace selector is not supported. + type: object + targetPortNumber: + description: |- + TargetPortNumber defines the port number to access the selected model servers. + The number must be in the range 1 to 65535. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - extensionRef + - selector + - targetPortNumber + type: object + status: + description: InferencePoolStatus defines the observed state of InferencePool + properties: + parent: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the InferencePool with respect to + each parent. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: PoolStatus defines the observed state of InferencePool + from a Gateway. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: |- + Conditions track the state of the InferencePool. + + Known condition types are: + + * "Accepted" + * "ResolvedRefs" + 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 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + parentRef: + description: GatewayRef indicates the gateway that observed + state of InferencePool. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + required: + - parentRef + type: object + maxItems: 32 + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} \ No newline at end of file diff --git a/config/kind/workload/bases/rollout.yaml b/config/kind/workload/bases/rollout.yaml index f16c268..e75a64a 100644 --- a/config/kind/workload/bases/rollout.yaml +++ b/config/kind/workload/bases/rollout.yaml @@ -16,6 +16,7 @@ spec: trafficTopologyRefs: - rollout-demo1 - rollout-demo2 + - rollout-demo1-httproute --- diff --git a/config/kind/workload/bases/traffic.yaml b/config/kind/workload/bases/traffic.yaml index 69c0f0e..18ed008 100644 --- a/config/kind/workload/bases/traffic.yaml +++ b/config/kind/workload/bases/traffic.yaml @@ -106,3 +106,64 @@ spec: ports: - port: 80 targetPort: 80 + +--- + +apiVersion: rollout.kusionstack.io/v1alpha1 +kind: TrafficTopology +metadata: + name: rollout-demo1-httproute +spec: + workloadRef: + apiVersion: apps/v1 + kind: StatefulSet + match: + selector: + matchLabels: + app: rollout-demo + cluster: cluster-a + trafficType: InCluster + backend: + apiVersion: inference.networking.x-k8s.io/v1alpha2 + kind: InferencePool + name: rollout-demo1 + routes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + name: rollout-demo1 + +--- + +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: rollout-demo1 + annotations: + # rollout.kusionstack.io/route-conditions: '{"conditions":[{"lastTransitionTime":"2025-07-20T08:48:12Z","observedGeneration":0,"reason":"Synced","status":"True","type":"Synced"}]}' +spec: + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: rollout-demo1 + group: inference.networking.x-k8s.io + kind: InferencePool + +--- + +apiVersion: inference.networking.x-k8s.io/v1alpha2 +kind: InferencePool +metadata: + name: rollout-demo1 +spec: + # spec.endpointPickerConfig 尽管暂时用不到,但inference pool api限定了需要设置,可以参照下面的设置 + extensionRef: + failureMode: FailClose + group: "" + kind: Service + name: rollout-demo1 + selector: + cluster: cluster-a + targetPortNumber: 80 diff --git a/go.mod b/go.mod index 1991575..829ce2a 100644 --- a/go.mod +++ b/go.mod @@ -6,38 +6,38 @@ toolchain go1.24.2 require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc - github.com/go-logr/logr v1.4.2 + github.com/go-logr/logr v1.4.3 github.com/google/uuid v1.6.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.30.0 + github.com/onsi/gomega v1.37.0 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 k8s.io/api v0.33.2 - k8s.io/apiextensions-apiserver v0.32.3 + k8s.io/apiextensions-apiserver v0.33.1 k8s.io/apimachinery v0.33.2 - k8s.io/apiserver v0.29.3 - k8s.io/client-go v0.32.3 - k8s.io/code-generator v0.32.3 - k8s.io/component-base v0.28.4 + k8s.io/apiserver v0.33.1 + k8s.io/client-go v0.33.1 + k8s.io/code-generator v0.33.1 + k8s.io/component-base v0.33.1 k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v1.22.2 - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 - kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6 + k8s.io/utils v0.0.0-20241210054802-24370beab758 + kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1 kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 kusionstack.io/resourceconsist v0.0.2 - sigs.k8s.io/controller-runtime v0.20.4 + sigs.k8s.io/controller-runtime v0.21.0 ) require ( github.com/cyphar/filepath-securejoin v0.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/runc v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/zoumo/golib v0.2.0 // indirect - golang.org/x/sync v0.13.0 // indirect + golang.org/x/sync v0.14.0 // indirect gotest.tools/v3 v3.4.0 // indirect k8s.io/component-helpers v0.22.2 // indirect ) @@ -50,7 +50,7 @@ require ( github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect @@ -69,22 +69,22 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.64.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/samber/lo v1.47.0 github.com/spf13/afero v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/mod v0.23.0 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.30.0 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.31.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -92,8 +92,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect sigs.k8s.io/gateway-api v1.3.0 + sigs.k8s.io/gateway-api-inference-extension v0.4.0 sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index a3337ce..2fbb921 100644 --- a/go.sum +++ b/go.sum @@ -344,6 +344,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -355,6 +357,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= @@ -372,8 +376,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= @@ -404,6 +406,7 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -452,21 +455,21 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -475,8 +478,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quobyte/api v0.1.8/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= @@ -637,8 +640,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -680,15 +683,15 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -702,8 +705,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -766,8 +769,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -776,8 +779,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -790,16 +793,16 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -844,8 +847,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1020,6 +1023,8 @@ k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCf k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6 h1:ZXP+K55y4j9SKmLr8TMTgza5w1Zeu5Fmevk4MrTvFSQ= kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= +kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1 h1:j/yoU/mjITbd5cQc7IaJGiEnVD9S36PsXbWkJcwYLKo= +kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 h1:HYE6Wa8EzSlA6UmaTLtNKUgkB2mmasp6Ul69d3/SpK0= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6/go.mod h1:5Uy3GCJ1JEGqZw/Sp/uVnHBJN1t9wjY6USPSZ9s4idk= kusionstack.io/resourceconsist v0.0.2 h1:gf+c/LOMsiKoVR+GLzOomw8qcUbZbPckQLczZllNdVM= @@ -1036,12 +1041,15 @@ sigs.k8s.io/controller-runtime v0.10.3 h1:s5Ttmw/B4AuIbwrXD3sfBkXwnPMMWrqpVj4WRt sigs.k8s.io/controller-runtime v0.10.3/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8= sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/gateway-api-inference-extension v0.4.0 h1:JoTYxBCkQStJGpV1rwdAR6oDrxquyLsNMECY1My7Ggk= +sigs.k8s.io/gateway-api-inference-extension v0.4.0/go.mod h1:44aUo5kUCGHJ1No/MwLofF2sTarkQ4wQYXr9gz92fhw= sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs= sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016 h1:kXv6kKdoEtedwuqMmkqhbkgvYKeycVbC8+iPCP9j5kQ= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= diff --git a/pkg/controllers/backendrouting/backendrouting_controller.go b/pkg/controllers/backendrouting/backendrouting_controller.go index b07c58e..2574b6b 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller.go +++ b/pkg/controllers/backendrouting/backendrouting_controller.go @@ -39,9 +39,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "kusionstack.io/rollout/pkg/backend" "kusionstack.io/rollout/pkg/controllers/registry" - "kusionstack.io/rollout/pkg/route" + "kusionstack.io/rollout/pkg/trafficrouting/backend" + "kusionstack.io/rollout/pkg/trafficrouting/route" ) const ( @@ -153,13 +153,12 @@ func (r *BackendRoutingReconciler) satisfiedExpectations(ctx context.Context, ob func (r *BackendRoutingReconciler) initSyncContext(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) (*syncContext, error) { syncCtx := &syncContext{ Object: obj, - Routes: make([]route.RouteControl, len(obj.Spec.Routes)), + Routes: make([]route.RouteController, len(obj.Spec.Routes)), } - syncCtx.Initialize() + syncCtx.SetDefaults() // find origin backend - - backend, backendObj, err := r.findBackend(ctx, obj, obj.Spec.Backend.Name) + _, backendObj, err := r.findBackend(ctx, obj, obj.Spec.Backend.Name) if err != nil { syncCtx.NewStatus.Backends.Origin.Conditions.Ready = ptr.To(false) return syncCtx, err @@ -167,21 +166,12 @@ func (r *BackendRoutingReconciler) initSyncContext(ctx context.Context, obj *rol syncCtx.NewStatus.Backends.Origin.Conditions.Ready = ptr.To(true) } - syncCtx.BackendInterface = backend syncCtx.BackendObject = backendObj - for i, routeSpec := range obj.Spec.Routes { - rctl, err := r.findRoute(ctx, obj.Namespace, routeSpec) - if err != nil { - syncCtx.setRouteCondition(i, metav1.ConditionUnknown, "Unknown", err.Error()) - return syncCtx, err - } - if syncCtx.NewStatus.Routes[i].Condition.Status == metav1.ConditionUnknown { - syncCtx.setRouteCondition(i, metav1.ConditionTrue, "RouteFound", "") - } - syncCtx.Routes[i] = rctl + err = r.findAllRouteControllers(ctx, syncCtx) + if err != nil { + return syncCtx, err } - return syncCtx, nil } @@ -287,19 +277,21 @@ func (b *BackendRoutingReconciler) syncInClusterBackends(ctx context.Context, sy return nil } -func (b *BackendRoutingReconciler) findBackend(ctx context.Context, obj *rolloutv1alpha1.BackendRouting, name string) (backend.InClusterBackend, client.Object, error) { - gvk := schema.FromAPIVersionAndKind(obj.Spec.Backend.APIVersion, obj.Spec.Backend.Kind) +func (b *BackendRoutingReconciler) findBackend(ctx context.Context, br *rolloutv1alpha1.BackendRouting, name string) (backend.InClusterBackend, client.Object, error) { + gvk := schema.FromAPIVersionAndKind(br.Spec.Backend.APIVersion, br.Spec.Backend.Kind) + cluster := br.Spec.Backend.Cluster + namespace := br.Namespace + backendStore, err := b.backendRegistry.Get(gvk) if err != nil { return nil, nil, err } - newObj := backendStore.NewObject() - ctx = clusterinfo.WithCluster(ctx, obj.Spec.Backend.Cluster) - err = b.Client.Get(ctx, client.ObjectKey{ - Namespace: obj.Namespace, + backendObj := backendStore.NewObject() + err = b.Client.Get(clusterinfo.WithCluster(ctx, cluster), client.ObjectKey{ + Namespace: namespace, Name: name, - }, newObj) - return backendStore, newObj, err + }, backendObj) + return backendStore, backendObj, err } func (b *BackendRoutingReconciler) deleteBackendResource(ctx context.Context, syncCtx *syncContext, name string) (bool, error) { @@ -330,7 +322,10 @@ func (b *BackendRoutingReconciler) deleteBackendResource(ctx context.Context, sy func (b *BackendRoutingReconciler) ensureBackendResource(ctx context.Context, syncCtx *syncContext, config rolloutv1alpha1.ForkedBackend) error { obj := syncCtx.Object + logger := logr.FromContextOrDiscard(ctx) + logger = logger.WithValues("apiVersion", obj.Spec.Backend.APIVersion, "kind", obj.Spec.Backend.Kind, "name", config.Name) + ctx = clusterinfo.WithCluster(ctx, obj.Spec.Backend.Cluster) backendStore, _, err := b.findBackend(ctx, obj, config.Name) if err == nil { @@ -338,34 +333,31 @@ func (b *BackendRoutingReconciler) ensureBackendResource(ctx context.Context, sy return nil } if !errors.IsNotFound(err) { - logger.Error(err, "failed to get backend resource", "backend", obj.Spec.Backend.Name) + logger.Error(err, "failed to get backend resource") return err } // need to create - newBackend := backendStore.Fork(syncCtx.BackendObject, config) - // set owner - newBackend.SetOwnerReferences([]metav1.OwnerReference{*metav1.NewControllerRef(obj, backendStore.GroupVersionKind())}) + newBackendObj := backendStore.Fork(syncCtx.BackendObject, config) // add label - labels := newBackend.GetLabels() + labels := newBackendObj.GetLabels() if labels == nil { labels = make(map[string]string) } labels[rolloutapi.LabelTemporaryResource] = "true" - newBackend.SetLabels(labels) + newBackendObj.SetLabels(labels) - err = b.Client.Create(ctx, newBackend) + err = b.Client.Create(ctx, newBackendObj) if err != nil { - logger.Error(err, "failed to create backend resource", "backend", newBackend.GetName()) + logger.Error(err, "failed to create backend resource") return err } + logger.Info("backend resource created") return nil } func (b *BackendRoutingReconciler) syncInClusterRoutes(ctx context.Context, syncCtx *syncContext) error { - obj := syncCtx.Object - - syncRoute := func(i int, fn func(routeCtl route.RouteControl) error) error { + syncRoute := func(i int, fn func(routeCtl route.RouteController) error) error { routeCtl := syncCtx.Routes[i] err := fn(routeCtl) if err != nil { @@ -374,16 +366,18 @@ func (b *BackendRoutingReconciler) syncInClusterRoutes(ctx context.Context, sync return err } // TODO: add synced logic, read condition from annotations - syncCtx.setRouteCondition(i, metav1.ConditionTrue, "Synced", "") + // syncCtx.setRouteCondition(i, metav1.ConditionTrue, "Synced", "") return nil } + logger := logr.FromContextOrDiscard(ctx) + for i, routeStatus := range syncCtx.NewStatus.Routes { needCreate, needDelete := syncCtx.checkOriginRoute(routeStatus.Forwarding) if needCreate { - err := syncRoute(i, func(routeCtl route.RouteControl) error { - return routeCtl.ChangeOrigin(ctx, obj.Spec.Backend, obj.Spec.Forwarding.HTTP.Origin.BackendName) + err := syncRoute(i, func(routeCtl route.RouteController) error { + return routeCtl.Initialize(ctx) }) if err != nil { return err @@ -395,8 +389,8 @@ func (b *BackendRoutingReconciler) syncInClusterRoutes(ctx context.Context, sync syncCtx.NewStatus.Routes[i].Forwarding.Origin.Conditions.Ready = nil syncCtx.NewStatus.Routes[i].Forwarding.Origin.Conditions.Terminating = ptr.To(true) - err := syncRoute(i, func(routeCtl route.RouteControl) error { - return routeCtl.ResetOrigin(ctx, obj.Spec.Backend, syncCtx.NewStatus.Routes[i].Forwarding.Origin.BackendName) + err := syncRoute(i, func(routeCtl route.RouteController) error { + return routeCtl.Reset(ctx) }) if err != nil { return err @@ -408,8 +402,8 @@ func (b *BackendRoutingReconciler) syncInClusterRoutes(ctx context.Context, sync needCreate, needDelete = syncCtx.checkCanaryRoute(routeStatus.Forwarding) if needCreate { - err := syncRoute(i, func(routeCtl route.RouteControl) error { - return routeCtl.AddCanary(ctx, obj) + err := syncRoute(i, func(routeCtl route.RouteController) error { + return routeCtl.AddCanary(ctx) }) if err != nil { return err @@ -421,29 +415,95 @@ func (b *BackendRoutingReconciler) syncInClusterRoutes(ctx context.Context, sync syncCtx.NewStatus.Routes[i].Forwarding.Canary.Conditions.Ready = nil syncCtx.NewStatus.Routes[i].Forwarding.Canary.Conditions.Terminating = ptr.To(true) - err := syncRoute(i, func(routeCtl route.RouteControl) error { - return routeCtl.DeleteCanary(ctx, obj) + err := syncRoute(i, func(routeCtl route.RouteController) error { + return routeCtl.DeleteCanary(ctx) }) if err != nil { return err } syncCtx.NewStatus.Routes[i].Forwarding.Canary = nil } + + ctrl := syncCtx.Routes[i] + conditions, err := ctrl.GetCondition(ctx) + if err != nil { + logger.Error(err, "failed to get route condition", "route", routeStatus.CrossClusterObjectReference) + continue + } + + // check ready according to condition extension + status, reason, message := b.checkRouteReady(ctx, ctrl.GetRoute().GetGeneration(), conditions) + syncCtx.setRouteCondition(i, status, reason, message) } return nil } -func (b *BackendRoutingReconciler) findRoute(ctx context.Context, namespace string, routeInfo rolloutv1alpha1.CrossClusterObjectReference) (route.RouteControl, error) { - routeAccessor, err := b.routeRegistry.Get(schema.FromAPIVersionAndKind(routeInfo.APIVersion, routeInfo.Kind)) - if err != nil { - return nil, err +func (b *BackendRoutingReconciler) checkRouteReady(_ context.Context, generation int64, conditions []metav1.Condition) (metav1.ConditionStatus, string, string) { + if len(conditions) == 0 { + return metav1.ConditionTrue, "NoConditionExtension", "" } + cond := meta.FindStatusCondition(conditions, "Ready") + if cond != nil { + if generation != cond.ObservedGeneration { + return metav1.ConditionFalse, "OutOfSync", fmt.Sprintf("Ready condition is out of synced, route.Generation(%d) != condition.ObservedGeneration(%d)", generation, cond.ObservedGeneration) + } + switch cond.Status { + case metav1.ConditionTrue: + return cond.Status, "ConditionExtensionReady", cond.Message + default: + return metav1.ConditionFalse, "ConditionExtensionNotReady", cond.Message + } + } + cond = meta.FindStatusCondition(conditions, "Synced") + if cond != nil { + if generation != cond.ObservedGeneration { + return metav1.ConditionFalse, "OutOfSync", fmt.Sprintf("Scyned condition is out of synced, route.Generation(%d) != condition.ObservedGeneration(%d)", generation, cond.ObservedGeneration) + } + switch cond.Status { + case metav1.ConditionTrue: + if !cond.LastTransitionTime.IsZero() && !metav1.Now().After(cond.LastTransitionTime.Time.Add(30*time.Second)) { + // if synced is true, we need to wait for 30 seconds + return metav1.ConditionFalse, "SufficientDelayTime", "waiting 30 seconds to ensure sufficient time for routing rules to take effect." + } + return cond.Status, "ConditionExtensionSynced", cond.Message + default: + return metav1.ConditionFalse, "ConditionExtensionNotSynced", cond.Message + } + } + return metav1.ConditionUnknown, "NoConditionExtension", "no Ready or Synced condition found in conditions extension" +} - routeObj := routeAccessor.NewObject() - err = b.Client.Get(clusterinfo.WithCluster(ctx, routeInfo.Cluster), client.ObjectKey{Namespace: namespace, Name: routeInfo.Name}, routeObj) - if err != nil { - return nil, err +func (b *BackendRoutingReconciler) findAllRouteControllers(ctx context.Context, syncCtx *syncContext) error { + fn := func(routeInfo rolloutv1alpha1.CrossClusterObjectReference) (route.Route, client.Object, error) { + routeAccessor, err := b.routeRegistry.Get(schema.FromAPIVersionAndKind(routeInfo.APIVersion, routeInfo.Kind)) + if err != nil { + return nil, nil, err + } + + routeObj := routeAccessor.NewObject() + err = b.Client.Get( + clusterinfo.WithCluster(ctx, routeInfo.Cluster), + client.ObjectKey{Namespace: syncCtx.Object.Namespace, Name: routeInfo.Name}, + routeObj, + ) + if err != nil { + return nil, nil, err + } + return routeAccessor, routeObj, nil } - return routeAccessor.Wrap(b.Client, routeInfo.Cluster, routeObj) + for i, routeInfo := range syncCtx.Object.Spec.Routes { + accessor, routeObj, err := fn(routeInfo) + if err != nil { + syncCtx.setRouteCondition(i, metav1.ConditionUnknown, "Unknown", err.Error()) + return err + } + if syncCtx.NewStatus.Routes[i].Condition.Status == metav1.ConditionUnknown { + syncCtx.setRouteCondition(i, metav1.ConditionTrue, "RouteExists", "") + } + + rctl, _ := accessor.GetController(b.Client, syncCtx.Object, routeObj, syncCtx.NewStatus.Routes[i]) + syncCtx.Routes[i] = rctl + } + return nil } diff --git a/pkg/controllers/backendrouting/backendrouting_controller_test.go b/pkg/controllers/backendrouting/backendrouting_controller_test.go index 5fd85d4..60a545d 100644 --- a/pkg/controllers/backendrouting/backendrouting_controller_test.go +++ b/pkg/controllers/backendrouting/backendrouting_controller_test.go @@ -72,9 +72,11 @@ func (s *backendRoutingTestSuite) SetupSuite() { err := rolloutv1alpha1.AddToScheme(testscheme) s.Require().NoError(err) - fedEnv := &envtest.Environment{ - Scheme: testscheme, - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + testClusterEnv := &envtest.Environment{ + Scheme: testscheme, + CRDInstallOptions: envtest.CRDInstallOptions{ + Paths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + }, } var ( @@ -83,13 +85,9 @@ func (s *backendRoutingTestSuite) SetupSuite() { cluster2Config *rest.Config ) - fedConfig, s.fedClient = s.setupCluster(fedEnv) - cluster1Config, s.cluster1Client = s.setupCluster(&envtest.Environment{ - Scheme: testscheme, - }) - cluster2Config, s.cluster2Client = s.setupCluster(&envtest.Environment{ - Scheme: testscheme, - }) + fedConfig, s.fedClient = s.setupCluster(testClusterEnv) + cluster1Config, s.cluster1Client = s.setupCluster(testClusterEnv) + cluster2Config, s.cluster2Client = s.setupCluster(testClusterEnv) // manager os.Setenv(clusterinfo.EnvClusterAllowList, "cluster1,cluster2") diff --git a/pkg/controllers/backendrouting/sync_context.go b/pkg/controllers/backendrouting/sync_context.go index 9a2868b..7e21824 100644 --- a/pkg/controllers/backendrouting/sync_context.go +++ b/pkg/controllers/backendrouting/sync_context.go @@ -9,20 +9,18 @@ import ( rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/pkg/backend" - "kusionstack.io/rollout/pkg/route" + "kusionstack.io/rollout/pkg/trafficrouting/route" ) type syncContext struct { - once sync.Once - Object *rolloutv1alpha1.BackendRouting - BackendInterface backend.InClusterBackend - BackendObject client.Object - NewStatus *rolloutv1alpha1.BackendRoutingStatus - Routes []route.RouteControl + once sync.Once + Object *rolloutv1alpha1.BackendRouting + BackendObject client.Object + NewStatus *rolloutv1alpha1.BackendRoutingStatus + Routes []route.RouteController } -func (c *syncContext) Initialize() { +func (c *syncContext) SetDefaults() { c.once.Do(func() { if c.NewStatus == nil { c.NewStatus = c.Object.Status.DeepCopy() @@ -160,7 +158,7 @@ func (c *syncContext) setRouteCondition(routeIndex int, status metav1.ConditionS } func (c *syncContext) Status() rolloutv1alpha1.BackendRoutingStatus { - c.Initialize() + c.SetDefaults() backendReady := true reason := "Ready" diff --git a/pkg/controllers/registry/backend.go b/pkg/controllers/registry/backend.go index 8f357d1..aba1cbb 100644 --- a/pkg/controllers/registry/backend.go +++ b/pkg/controllers/registry/backend.go @@ -20,9 +20,10 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/manager" - "kusionstack.io/rollout/pkg/backend" - "kusionstack.io/rollout/pkg/backend/service" "kusionstack.io/rollout/pkg/genericregistry" + "kusionstack.io/rollout/pkg/trafficrouting/backend" + "kusionstack.io/rollout/pkg/trafficrouting/backend/inferencepool" + "kusionstack.io/rollout/pkg/trafficrouting/backend/service" ) const ( @@ -41,5 +42,6 @@ func NewBackendRegistry() BackendRegistry { func InitBackendRegistry(mgr manager.Manager) (bool, error) { Backends.Register(service.GVK, service.New()) + Backends.Register(inferencepool.GVK, inferencepool.New()) return true, nil } diff --git a/pkg/controllers/registry/route.go b/pkg/controllers/registry/route.go index 29e4eba..4dfa63d 100644 --- a/pkg/controllers/registry/route.go +++ b/pkg/controllers/registry/route.go @@ -21,8 +21,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "kusionstack.io/rollout/pkg/genericregistry" - "kusionstack.io/rollout/pkg/route" - "kusionstack.io/rollout/pkg/route/ingress" + "kusionstack.io/rollout/pkg/trafficrouting/route" + "kusionstack.io/rollout/pkg/trafficrouting/route/httproute" + "kusionstack.io/rollout/pkg/trafficrouting/route/ingress" ) const ( @@ -40,6 +41,7 @@ func NewRouteRegistry() RouteRegistry { } func InitRouteRegistry(mgr manager.Manager) (bool, error) { - Routes.Register(ingress.GVK, ingress.NewStorage()) + Routes.Register(ingress.GVK, ingress.New()) + Routes.Register(httproute.GVK, httproute.New()) return true, nil } diff --git a/pkg/controllers/rolloutrun/executor/canary.go b/pkg/controllers/rolloutrun/executor/canary.go index 2c646b9..fd0a9af 100644 --- a/pkg/controllers/rolloutrun/executor/canary.go +++ b/pkg/controllers/rolloutrun/executor/canary.go @@ -132,40 +132,43 @@ func (e *canaryExecutor) modifyTraffic(ctx *ExecutorContext, op string) (bool, t rolloutRun := ctx.RolloutRun opResult := controllerutil.OperationResultNone + if rolloutRun.Spec.Canary.Traffic == nil { + logger.Info("traffic is nil, skip modify traffic") + return true, retryImmediately + } + // 1.a. do traffic initialization - if rolloutRun.Spec.Canary.Traffic != nil { - var err error - switch op { - case "forkBackends": - opResult, err = ctx.TrafficManager.ForkBackends() - case "initializeRoute": - opResult, err = ctx.TrafficManager.InitializeRoute() - case "addCanaryRoute": - opResult, err = ctx.TrafficManager.AddCanaryRoute() - case "deleteCanaryRoute": - opResult, err = ctx.TrafficManager.DeleteCanaryRoute() - case "resetRoute": - opResult, err = ctx.TrafficManager.ResetRoute() - case "deleteForkedBackends": - opResult, err = ctx.TrafficManager.DeleteForkedBackends() - } - if err != nil { - logger.Error(err, "failed to modify traffic", "operation", op) - return false, retryDefault - } - logger.Info("modify traffic routing", "operation", op, "result", opResult) + var err error + switch op { + case "forkBackends": + opResult, err = ctx.TrafficManager.ForkBackends() + case "initializeRoute": + opResult, err = ctx.TrafficManager.InitializeRoute() + case "addCanaryRoute": + opResult, err = ctx.TrafficManager.AddCanaryRoute() + case "deleteCanaryRoute": + opResult, err = ctx.TrafficManager.DeleteCanaryRoute() + case "resetRoute": + opResult, err = ctx.TrafficManager.ResetRoute() + case "deleteForkedBackends": + opResult, err = ctx.TrafficManager.DeleteForkedBackends() } + if err != nil { + logger.Error(err, "failed to modify traffic", "operation", op) + return false, retryDefault + } + if opResult != controllerutil.OperationResultNone { + logger.Info("modify traffic routing", "operation", op, "result", opResult) + // check next time return false, retryDefault } // 1.b. waiting for traffic - if rolloutRun.Spec.Canary.Traffic != nil { - ready := ctx.TrafficManager.CheckReady() - if !ready { - logger.Info("waiting for BackendRouting ready") - return false, retryDefault - } + ready := ctx.TrafficManager.CheckReady() + if !ready { + logger.Info("waiting for BackendRouting ready") + return false, retryDefault } return true, retryImmediately diff --git a/pkg/controllers/rolloutrun/executor/context.go b/pkg/controllers/rolloutrun/executor/context.go index 8817a70..6471523 100644 --- a/pkg/controllers/rolloutrun/executor/context.go +++ b/pkg/controllers/rolloutrun/executor/context.go @@ -18,6 +18,7 @@ package executor import ( "context" + "slices" "sync" "github.com/go-logr/logr" @@ -28,7 +29,7 @@ import ( rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/pkg/controllers/rolloutrun/traffic" + trafficcontrol "kusionstack.io/rollout/pkg/trafficrouting/control" "kusionstack.io/rollout/pkg/workload" ) @@ -45,7 +46,7 @@ type ExecutorContext struct { RolloutRun *rolloutv1alpha1.RolloutRun NewStatus *rolloutv1alpha1.RolloutRunStatus Workloads *workload.Set - TrafficManager *traffic.Manager + TrafficManager *trafficcontrol.Manager } func (c *ExecutorContext) Initialize() { @@ -91,12 +92,7 @@ func (c *ExecutorContext) Initialize() { // filterWebhooks return webhooks met hookType func filterWebhooks(hookType rolloutv1alpha1.HookType, rolloutRun *rolloutv1alpha1.RolloutRun) []rolloutv1alpha1.RolloutWebhook { return lo.Filter(rolloutRun.Spec.Webhooks, func(w rolloutv1alpha1.RolloutWebhook, _ int) bool { - for _, item := range w.HookTypes { - if item == hookType { - return true - } - } - return false + return slices.Contains(w.HookTypes, hookType) }) } diff --git a/pkg/controllers/rolloutrun/rolloutrun_controller.go b/pkg/controllers/rolloutrun/rolloutrun_controller.go index be8633a..e9873a9 100644 --- a/pkg/controllers/rolloutrun/rolloutrun_controller.go +++ b/pkg/controllers/rolloutrun/rolloutrun_controller.go @@ -40,9 +40,9 @@ import ( "kusionstack.io/rollout/pkg/controllers/registry" "kusionstack.io/rollout/pkg/controllers/rolloutrun/executor" - "kusionstack.io/rollout/pkg/controllers/rolloutrun/traffic" "kusionstack.io/rollout/pkg/features" "kusionstack.io/rollout/pkg/features/rolloutclasspredicate" + trafficcontrol "kusionstack.io/rollout/pkg/trafficrouting/control" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/utils/expectations" "kusionstack.io/rollout/pkg/workload" @@ -244,7 +244,7 @@ func (r *RolloutRunReconciler) syncRolloutRun( result ctrl.Result ) - trafficManager, err := traffic.NewManager(r.Client, r.Logger, topologies) + trafficManager, err := trafficcontrol.NewManager(r.Client, r.Logger, topologies) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/trafficrouting/backend/inferencepool/accessor.go b/pkg/trafficrouting/backend/inferencepool/accessor.go new file mode 100644 index 0000000..d5d7b3b --- /dev/null +++ b/pkg/trafficrouting/backend/inferencepool/accessor.go @@ -0,0 +1,48 @@ +package inferencepool + +import ( + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1alpha2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2" + + "kusionstack.io/rollout/pkg/trafficrouting/backend" + "kusionstack.io/rollout/pkg/utils/accessor" +) + +var GVK = gwapiv1alpha2.SchemeGroupVersion.WithKind("InferencePool") + +var _ backend.InClusterBackend = &accessorImpl{} + +type accessorImpl struct { + accessor.ObjectAccessor +} + +func New() backend.InClusterBackend { + return &accessorImpl{ + ObjectAccessor: accessor.NewObjectAccessor(GVK, &gwapiv1alpha2.InferencePool{}, &gwapiv1alpha2.InferencePoolList{}), + } +} + +func (b *accessorImpl) Fork(origin client.Object, config rolloutv1alpha1.ForkedBackend) client.Object { + obj := origin.(*gwapiv1alpha2.InferencePool) + + forkedObj := &gwapiv1alpha2.InferencePool{} + // fork metadata + forkedObj.ObjectMeta = backend.ForkObjectMeta(obj, config.Name) + if forkedObj.Labels == nil { + forkedObj.Labels = make(map[string]string) + } + forkedObj.Labels[rolloutapi.LabelTemporaryResource] = "true" + // fork spec + forkedObj.Spec = *obj.Spec.DeepCopy() + // change spec + if forkedObj.Spec.Selector == nil { + forkedObj.Spec.Selector = make(map[gwapiv1alpha2.LabelKey]gwapiv1alpha2.LabelValue) + } + for k, v := range config.ExtraLabelSelector { + forkedObj.Spec.Selector[gwapiv1alpha2.LabelKey(k)] = gwapiv1alpha2.LabelValue(v) + forkedObj.Labels[k] = v + } + return forkedObj +} diff --git a/pkg/backend/interface.go b/pkg/trafficrouting/backend/interface.go similarity index 100% rename from pkg/backend/interface.go rename to pkg/trafficrouting/backend/interface.go diff --git a/pkg/backend/service/accessor.go b/pkg/trafficrouting/backend/service/accessor.go similarity index 59% rename from pkg/backend/service/accessor.go rename to pkg/trafficrouting/backend/service/accessor.go index 8fd1136..216023e 100644 --- a/pkg/backend/service/accessor.go +++ b/pkg/trafficrouting/backend/service/accessor.go @@ -22,7 +22,7 @@ import ( rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/pkg/backend" + "kusionstack.io/rollout/pkg/trafficrouting/backend" "kusionstack.io/rollout/pkg/utils/accessor" ) @@ -40,22 +40,24 @@ func New() backend.InClusterBackend { } } -func (s *accessorImpl) Fork(original client.Object, config rolloutv1alpha1.ForkedBackend) client.Object { - obj := original.(*corev1.Service) - forkedbackend := &corev1.Service{} - forkedbackend.Name = config.Name - forkedbackend.Namespace = obj.Namespace - forkedbackend.Spec.Ports = obj.Spec.Ports - forkedbackend.Spec.Type = obj.Spec.Type - forkedbackend.Spec.Selector = obj.Spec.Selector - if forkedbackend.Spec.Selector == nil { - forkedbackend.Spec.Selector = make(map[string]string) +func (s *accessorImpl) Fork(origin client.Object, config rolloutv1alpha1.ForkedBackend) client.Object { + obj := origin.(*corev1.Service) + forkedObj := &corev1.Service{} + // fork metadata + forkedObj.ObjectMeta = backend.ForkObjectMeta(obj, config.Name) + if forkedObj.Labels == nil { + forkedObj.Labels = make(map[string]string) } - maps.Copy(forkedbackend.Spec.Selector, config.ExtraLabelSelector) - if forkedbackend.Labels == nil { - forkedbackend.Labels = make(map[string]string) + maps.Copy(forkedObj.Labels, config.ExtraLabelSelector) + forkedObj.Labels[rolloutapi.LabelTemporaryResource] = "true" + // fork spec + forkedObj.Spec.Ports = obj.Spec.Ports + forkedObj.Spec.Type = obj.Spec.Type + forkedObj.Spec.Selector = obj.Spec.Selector + if forkedObj.Spec.Selector == nil { + forkedObj.Spec.Selector = make(map[string]string) } - maps.Copy(forkedbackend.Labels, config.ExtraLabelSelector) - forkedbackend.Labels[rolloutapi.LabelTemporaryResource] = "true" - return forkedbackend + // change selector + maps.Copy(forkedObj.Spec.Selector, config.ExtraLabelSelector) + return forkedObj } diff --git a/pkg/trafficrouting/backend/util.go b/pkg/trafficrouting/backend/util.go new file mode 100644 index 0000000..33c6607 --- /dev/null +++ b/pkg/trafficrouting/backend/util.go @@ -0,0 +1,15 @@ +package backend + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func ForkObjectMeta(in client.Object, newName string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: newName, + Namespace: in.GetNamespace(), + Labels: in.GetLabels(), + Annotations: in.GetAnnotations(), + } +} diff --git a/pkg/controllers/rolloutrun/traffic/traffic_manager.go b/pkg/trafficrouting/control/traffic_manager.go similarity index 93% rename from pkg/controllers/rolloutrun/traffic/traffic_manager.go rename to pkg/trafficrouting/control/traffic_manager.go index edddd14..76e0289 100644 --- a/pkg/controllers/rolloutrun/traffic/traffic_manager.go +++ b/pkg/trafficrouting/control/traffic_manager.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package traffic +package control import ( "context" @@ -41,7 +41,7 @@ type Manager struct { func NewManager(c client.Client, logger logr.Logger, topologies []rolloutv1alpha1.TrafficTopology) (*Manager, error) { m := &Manager{ client: c, - logger: logger.WithName("traffic"), + logger: logger.WithName("trafficrouting"), topoligies: make(map[rolloutv1alpha1.CrossClusterObjectNameReference]*topology), } for _, obj := range topologies { @@ -70,7 +70,7 @@ func NewManager(c client.Client, logger logr.Logger, topologies []rolloutv1alpha } func (m *Manager) With(logger logr.Logger, workloads []rolloutv1alpha1.RolloutRunStepTarget, strategy *rolloutv1alpha1.TrafficStrategy) { - m.logger = logger.WithName("traffic") + m.logger = logger.WithName("trafficrouting") m.targets = workloads m.strategy = strategy } @@ -112,6 +112,7 @@ func (m *Manager) InitializeRoute() (controllerutil.OperationResult, error) { } } else { routing.Spec.Forwarding.HTTP.Stable = &rolloutv1alpha1.StableHTTPForwarding{ + BackendName: routing.Spec.ForkedBackends.Stable.Name, HTTPRouteRule: *m.strategy.HTTP.StableTraffic, } } @@ -134,6 +135,7 @@ func (m *Manager) AddCanaryRoute() (controllerutil.OperationResult, error) { } } routing.Spec.Forwarding.HTTP.Canary = &rolloutv1alpha1.CanaryHTTPForwarding{ + BackendName: routing.Spec.ForkedBackends.Canary.Name, CanaryHTTPRouteRule: m.strategy.HTTP.CanaryHTTPRouteRule, } return nil @@ -154,7 +156,7 @@ func (m *Manager) DeleteCanaryRoute() (controllerutil.OperationResult, error) { func (m *Manager) mutateRouting(mutateFn func(routing *rolloutv1alpha1.BackendRouting) error) (controllerutil.OperationResult, error) { operation := controllerutil.OperationResultNone if m.strategy == nil { - m.logger.Info("no traffic strategy found, skip it") + m.logger.Info("no trafficrouting strategy found, skip it") return operation, nil } ctx := clusterinfo.WithCluster(context.Background(), clusterinfo.Fed) @@ -162,7 +164,7 @@ func (m *Manager) mutateRouting(mutateFn func(routing *rolloutv1alpha1.BackendRo for _, workload := range m.targets { topo, ok := m.topoligies[workload.CrossClusterObjectNameReference] if !ok { - m.logger.Info("no traffic topology found for workload", "workload", workload.CrossClusterObjectNameReference) + m.logger.Info("no trafficrouting topology found for workload", "workload", workload.CrossClusterObjectNameReference) continue } for i := range topo.routings { diff --git a/pkg/trafficrouting/route/httproute/control.go b/pkg/trafficrouting/route/httproute/control.go new file mode 100644 index 0000000..e2462e9 --- /dev/null +++ b/pkg/trafficrouting/route/httproute/control.go @@ -0,0 +1,292 @@ +package httproute + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/samber/lo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/ptr" + rolloutapi "kusionstack.io/kube-api/rollout" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + clientutil "kusionstack.io/kube-utils/client" + "kusionstack.io/kube-utils/multicluster/clusterinfo" + "sigs.k8s.io/controller-runtime/pkg/client" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "kusionstack.io/rollout/pkg/trafficrouting/route" +) + +var _ route.RouteController = &httpRouteControl{} + +type httpRouteControl struct { + client client.Client + backendrouting *rolloutv1alpha1.BackendRouting + routeObj *gatewayapiv1.HTTPRoute + routeStatus rolloutv1alpha1.BackendRouteStatus +} + +func (r *httpRouteControl) GetRoute() client.Object { + return r.routeObj +} + +func (r *httpRouteControl) GetCondition(ctx context.Context) ([]metav1.Condition, error) { + routeObj := r.routeObj + + annotations := routeObj.GetAnnotations() + if len(annotations) == 0 { + return nil, nil + } + annoCond, ok := annotations[rolloutapi.AnnoRouteConditions] + if !ok { + return nil, nil + } + if len(annoCond) == 0 { + return nil, fmt.Errorf("annotaions[%s] value is empty", rolloutapi.AnnoRouteConditions) + } + + routeConds := struct { + Conditions []metav1.Condition `json:"conditions"` + }{} + + err := json.Unmarshal([]byte(annoCond), &routeConds) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal annotations[%s] value: %w", rolloutapi.AnnoRouteConditions, err) + } + + return routeConds.Conditions, nil +} + +// Initialize implements route.RouteControl. +func (c *httpRouteControl) Initialize(ctx context.Context) error { + from := c.backendrouting.Spec.Backend.Name + to := c.routeStatus.Forwarding.Origin.BackendName + typeRef := c.backendrouting.Spec.Backend.ObjectTypeRef + + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, c.client, c.routeObj, func(in *gatewayapiv1.HTTPRoute) error { + if in.Annotations == nil { + in.Annotations = make(map[string]string) + } + got := in.Annotations[rolloutapi.AnnoRouteSpecBackup] + if len(got) == 0 { + // backup origin spec + origin := encodeHTTPRouteSpec(&in.Spec) + in.Annotations[rolloutapi.AnnoRouteSpecBackup] = origin + } + + // change origin backend name + gvk := schema.FromAPIVersionAndKind(typeRef.APIVersion, typeRef.Kind) + for i := range in.Spec.Rules { + for j, backendRef := range in.Spec.Rules[i].BackendRefs { + if isBackendMatches(backendRef, gvk, from) { + // found, change the name + in.Spec.Rules[i].BackendRefs[j].Name = gatewayapiv1.ObjectName(to) + } + } + } + return nil + }) + return err +} + +// Reset implements route.RouteControl. +func (c *httpRouteControl) Reset(ctx context.Context) error { + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, c.client, c.routeObj, func(in *gatewayapiv1.HTTPRoute) error { + if in.Annotations == nil { + in.Annotations = make(map[string]string) + } + originSpec := in.Annotations[rolloutapi.AnnoRouteSpecBackup] + if len(originSpec) == 0 { + return fmt.Errorf("failed to find origin spec in HTTPRoute annotations") + } + origin, err := decodeHTTPRouteSpec(originSpec) + if err != nil { + return err + } + in.Spec = *origin + delete(in.Annotations, rolloutapi.AnnoRouteSpecBackup) + return nil + }) + return err +} + +// AddCanary implements route.RouteControl. +func (c *httpRouteControl) AddCanary(ctx context.Context) error { + gvk := schema.FromAPIVersionAndKind(c.backendrouting.Spec.Backend.ObjectTypeRef.APIVersion, c.backendrouting.Spec.Backend.ObjectTypeRef.Kind) + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, c.client, c.routeObj, func(in *gatewayapiv1.HTTPRoute) error { + stable := c.routeStatus.Forwarding.Origin.BackendName + in.Spec.Rules = addHTTPRouteRules( + in.Spec.Rules, + gvk, + stable, + c.backendrouting.Spec.ForkedBackends.Canary.Name, + &c.backendrouting.Spec.Forwarding.HTTP.Canary.CanaryHTTPRouteRule, + ) + return nil + }) + return err +} + +// DeleteCanary implements route.RouteControl. +func (c *httpRouteControl) DeleteCanary(ctx context.Context) error { + gvk := schema.FromAPIVersionAndKind(c.backendrouting.Spec.Backend.ObjectTypeRef.APIVersion, c.backendrouting.Spec.Backend.ObjectTypeRef.Kind) + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, c.client, c.routeObj, func(in *gatewayapiv1.HTTPRoute) error { + in.Spec.Rules = deleteBackendRefRules( + in.Spec.Rules, + gvk, + c.routeStatus.Forwarding.Origin.BackendName, + c.backendrouting.Spec.ForkedBackends.Canary.Name, + ) + return nil + }) + return err +} + +func (c *httpRouteControl) getCluster() string { + return c.routeStatus.Cluster +} + +func encodeHTTPRouteSpec(in *gatewayapiv1.HTTPRouteSpec) string { + data, _ := json.Marshal(in) + return string(data) +} + +func decodeHTTPRouteSpec(in string) (*gatewayapiv1.HTTPRouteSpec, error) { + spec := &gatewayapiv1.HTTPRouteSpec{} + err := json.Unmarshal([]byte(in), spec) + if err != nil { + return nil, err + } + return spec, nil +} + +func addHTTPRouteRules(in []gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, targetBackend, canaryBackend string, newRule *rolloutv1alpha1.CanaryHTTPRouteRule) []gatewayapiv1.HTTPRouteRule { + if newRule.Weight != nil { + return addWeightedBackendRefs(in, gvk, targetBackend, canaryBackend, newRule) + } + return addMatchesBackendRefs(in, gvk, targetBackend, canaryBackend, newRule) +} + +func addMatchesBackendRefs(oldRules []gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, targetBackend, canaryBackend string, newRule *rolloutv1alpha1.CanaryHTTPRouteRule) []gatewayapiv1.HTTPRouteRule { + if newRule.Matches == nil { + return oldRules + } + + outputRules := make([]gatewayapiv1.HTTPRouteRule, 0) + canaryRules := make([]gatewayapiv1.HTTPRouteRule, 0) + + newMatches := []gatewayapiv1.HTTPRouteMatch{} + for _, match := range newRule.Matches { + newMatches = append(newMatches, gatewayapiv1.HTTPRouteMatch{ + // Path: match.Path, + Headers: match.Headers, + QueryParams: match.QueryParams, + }) + } + + for i := range oldRules { + rule := &oldRules[i] + index, targetBackendRef := findBackendRef(rule, gvk, targetBackend) + if targetBackendRef == nil { + continue + } + + canaryRule := rule.DeepCopy() + canaryRule.BackendRefs[index].Name = gatewayapiv1.ObjectName(canaryBackend) + canaryRule.Matches = append(canaryRule.Matches, newMatches...) + canaryRule.Filters = append(canaryRule.Filters, newRule.Filters...) + canaryRules = append(canaryRules, *canaryRule) + } + + outputRules = append(outputRules, oldRules...) + for i := range canaryRules { + canaryRules[i].Name = ptr.To(gatewayapiv1.SectionName(fmt.Sprintf("%d.canary.rollout.kusionstack.io", i))) + outputRules = append(outputRules, canaryRules[i]) + } + return outputRules +} + +func addWeightedBackendRefs(oldRules []gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, targetBackend, canaryBackend string, newRule *rolloutv1alpha1.CanaryHTTPRouteRule) []gatewayapiv1.HTTPRouteRule { + if newRule.Weight == nil { + return oldRules + } + + outputRules := make([]gatewayapiv1.HTTPRouteRule, 0) + for i := range oldRules { + rule := &oldRules[i] + _, targetBackendRef := findBackendRef(rule, gvk, targetBackend) + if targetBackendRef == nil { + outputRules = append(outputRules, *rule) + continue + } + _, canaryBackendRef := findBackendRef(rule, gvk, canaryBackend) + if canaryBackendRef == nil { + canaryBackendRef = targetBackendRef.DeepCopy() + } + + canaryBackendRef.Name = gatewayapiv1.ObjectName(canaryBackend) + canaryBackendRef.Weight = newRule.Weight + canaryBackendRef.Filters = newRule.Filters + + targetBackendRef.Weight = ptr.To(100 - *canaryBackendRef.Weight) + + setBackendRef(rule, gvk, *targetBackendRef) + setBackendRef(rule, gvk, *canaryBackendRef) + outputRules = append(outputRules, *rule) + } + return outputRules +} + +func deleteBackendRefRules(oldRules []gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, targetBackend, _ string) []gatewayapiv1.HTTPRouteRule { + outputRules := make([]gatewayapiv1.HTTPRouteRule, 0) + for i := range oldRules { + rule := oldRules[i].DeepCopy() + // delete canary backendRef + filterOutBackendRef(rule, gvk, targetBackend) + // find target backendRef and reset weight to 1 + index, targetBackend := findBackendRef(rule, gvk, targetBackend) + if targetBackend != nil { + rule.BackendRefs[index].Weight = ptr.To[int32](1) + } + if len(rule.BackendRefs) == 0 { + // no backendRef, skip + continue + } + outputRules = append(outputRules, *rule) + } + return outputRules +} + +func filterOutBackendRef(rule *gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, name string) { + newBackends := lo.Reject(rule.BackendRefs, func(ref gatewayapiv1.HTTPBackendRef, _ int) bool { + return isBackendMatches(ref, gvk, name) + }) + + rule.BackendRefs = newBackends +} + +func findBackendRef(rule *gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, name string) (int, *gatewayapiv1.HTTPBackendRef) { + for i, backendRef := range rule.BackendRefs { + if isBackendMatches(backendRef, gvk, name) { + return i, &backendRef + } + } + return -1, nil +} + +func setBackendRef(rule *gatewayapiv1.HTTPRouteRule, gvk schema.GroupVersionKind, ref gatewayapiv1.HTTPBackendRef) { + index, backendRef := findBackendRef(rule, gvk, string(ref.Name)) + if backendRef == nil { + rule.BackendRefs = append(rule.BackendRefs, ref) + return + } + rule.BackendRefs[index] = ref +} + +func isBackendMatches(backendRef gatewayapiv1.HTTPBackendRef, gvk schema.GroupVersionKind, name string) bool { + return gvk.Group == string(ptr.Deref(backendRef.Group, "")) && + gvk.Kind == string(ptr.Deref(backendRef.Kind, "Service")) && + name == string(backendRef.Name) +} diff --git a/pkg/trafficrouting/route/httproute/route.go b/pkg/trafficrouting/route/httproute/route.go new file mode 100644 index 0000000..6320699 --- /dev/null +++ b/pkg/trafficrouting/route/httproute/route.go @@ -0,0 +1,58 @@ +package httproute + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "kusionstack.io/rollout/pkg/trafficrouting/route" + "kusionstack.io/rollout/pkg/utils/accessor" +) + +var GVK = gatewayapiv1.SchemeGroupVersion.WithKind("HTTPRoute") + +var _ route.Route = &routeImpl{} + +type routeImpl struct { + accessor.ObjectAccessor +} + +func New() route.Route { + return &routeImpl{ + ObjectAccessor: accessor.NewObjectAccessor( + GVK, + &gatewayapiv1.HTTPRoute{}, + &gatewayapiv1.HTTPRouteList{}, + ), + } +} + +func (r *routeImpl) GetReadyCondition(routeObj client.Object) *metav1.Condition { + cond := &metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + LastTransitionTime: routeObj.GetCreationTimestamp(), + ObservedGeneration: routeObj.GetGeneration(), + } + if routeObj == nil { + cond.Status = metav1.ConditionFalse + cond.Reason = "NotFound" + } + return cond +} + +func (r *routeImpl) GetController(client client.Client, br *rolloutv1alpha1.BackendRouting, route client.Object, routeStatus rolloutv1alpha1.BackendRouteStatus) (route.RouteController, error) { + routeObj, ok := route.(*gatewayapiv1.HTTPRoute) + if !ok { + return nil, fmt.Errorf("input route is not networkingv1.Ingress") + } + return &httpRouteControl{ + client: client, + backendrouting: br, + routeObj: routeObj, + routeStatus: routeStatus, + }, nil +} diff --git a/pkg/route/ingress/const.go b/pkg/trafficrouting/route/ingress/const.go similarity index 100% rename from pkg/route/ingress/const.go rename to pkg/trafficrouting/route/ingress/const.go diff --git a/pkg/route/ingress/route.go b/pkg/trafficrouting/route/ingress/control.go similarity index 74% rename from pkg/route/ingress/route.go rename to pkg/trafficrouting/route/ingress/control.go index 85a8cf6..493f373 100644 --- a/pkg/route/ingress/route.go +++ b/pkg/trafficrouting/route/ingress/control.go @@ -30,21 +30,35 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" v1 "sigs.k8s.io/gateway-api/apis/v1" - "kusionstack.io/rollout/pkg/route" + "kusionstack.io/rollout/pkg/trafficrouting/route" ) -var GVK = networkingv1.SchemeGroupVersion.WithKind("Ingress") +var _ route.RouteController = &ingressControl{} -type ingressRoute struct { - client client.Client - obj *networkingv1.Ingress - cluster string +type ingressControl struct { + client client.Client + backendrouting *rolloutv1alpha1.BackendRouting + routeObj *networkingv1.Ingress + routeStatus rolloutv1alpha1.BackendRouteStatus } -func (i *ingressRoute) AddCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error { - igs := i.obj +func (r *ingressControl) GetRoute() client.Object { + return r.routeObj +} + +func (r *ingressControl) GetCondition(_ context.Context) ([]metav1.Condition, error) { + // nil means ready and synced + return nil, nil +} - strategy := obj.Spec.Forwarding.HTTP.Canary +func (c *ingressControl) getCluster() string { + return c.routeStatus.Cluster +} + +func (c *ingressControl) AddCanary(ctx context.Context) error { + ingress := c.routeObj + + strategy := c.backendrouting.Spec.Forwarding.HTTP.Canary annosCanaryNeedCheck := map[string]string{ AnnoCanary: "true", @@ -58,7 +72,7 @@ func (i *ingressRoute) AddCanary(ctx context.Context, obj *rolloutv1alpha1.Backe AnnoMseReqHeaderCtrlRemove: "", } - isMseIngress := igs.Spec.IngressClassName != nil && *igs.Spec.IngressClassName == MseIngressClass + isMseIngress := ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName == MseIngressClass if strategy != nil { if len(strategy.Matches) > 0 { @@ -97,13 +111,13 @@ func (i *ingressRoute) AddCanary(ctx context.Context, obj *rolloutv1alpha1.Backe } canaryIgs := &networkingv1.Ingress{} - canaryIgs.Name = i.canaryIngressName() - canaryIgs.Namespace = igs.Namespace + canaryIgs.Name = c.canaryIngressName() + canaryIgs.Namespace = ingress.Namespace - forked := obj.Spec.ForkedBackends + forked := c.backendrouting.Spec.ForkedBackends - _, err := controllerutil.CreateOrUpdate(clusterinfo.WithCluster(ctx, i.cluster), i.client, canaryIgs, func() error { - canaryIgs.Spec = igs.Spec + _, err := controllerutil.CreateOrUpdate(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, canaryIgs, func() error { + canaryIgs.Spec = ingress.Spec if canaryIgs.Spec.DefaultBackend != nil { if canaryIgs.Spec.DefaultBackend.Service != nil && canaryIgs.Spec.DefaultBackend.Service.Name == forked.Stable.Name { @@ -149,23 +163,23 @@ func (i *ingressRoute) AddCanary(ctx context.Context, obj *rolloutv1alpha1.Backe return err } -func (i *ingressRoute) canaryIngressName() string { - return i.obj.Name + "-canary" +func (c *ingressControl) canaryIngressName() string { + return c.routeObj.Name + "-canary" } -func (i *ingressRoute) DeleteCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error { - canaryIgsName := i.canaryIngressName() +func (c *ingressControl) DeleteCanary(ctx context.Context) error { + canaryIgsName := c.canaryIngressName() canaryIgs := &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ - Namespace: i.obj.Namespace, + Namespace: c.routeObj.Namespace, Name: canaryIgsName, }, } - err := i.client.Delete(clusterinfo.WithCluster(ctx, i.cluster), canaryIgs) + err := c.client.Delete(clusterinfo.WithCluster(ctx, c.getCluster()), canaryIgs) return client.IgnoreNotFound(err) } -func (i *ingressRoute) changeCanaryBackend(kind, from, to string) func(ingress *networkingv1.Ingress) error { +func (c *ingressControl) changeCanaryBackend(kind, from, to string) func(ingress *networkingv1.Ingress) error { return func(ingress *networkingv1.Ingress) error { if ingress.Spec.DefaultBackend != nil { backend := ingress.Spec.DefaultBackend @@ -200,20 +214,20 @@ func (i *ingressRoute) changeCanaryBackend(kind, from, to string) func(ingress * } } -func (i *ingressRoute) ChangeOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, to string) error { - modify := i.changeCanaryBackend(originBackend.Kind, originBackend.Name, to) - _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, i.cluster), i.client, i.client, i.obj, modify) +func (c *ingressControl) Initialize(ctx context.Context) error { + to := c.routeStatus.Forwarding.Origin.BackendName + modify := c.changeCanaryBackend(c.backendrouting.Spec.Backend.Kind, c.backendrouting.Spec.Backend.Name, to) + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, c.client, c.routeObj, modify) return err } -func (i *ingressRoute) ResetOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, from string) error { - modify := i.changeCanaryBackend(originBackend.Kind, from, originBackend.Name) - _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, i.cluster), i.client, i.client, i.obj, modify) +func (c *ingressControl) Reset(ctx context.Context) error { + from := c.routeStatus.Forwarding.Origin.BackendName + modify := c.changeCanaryBackend(c.backendrouting.Spec.Backend.Kind, from, c.backendrouting.Spec.Backend.Name) + _, err := clientutil.UpdateOnConflict(clusterinfo.WithCluster(ctx, c.getCluster()), c.client, c.client, c.routeObj, modify) return err } -var _ route.RouteControl = &ingressRoute{} - func generateMultiHeadersAnno(headers []v1.HTTPHeader) string { if len(headers) == 0 { return "" diff --git a/pkg/route/ingress/store.go b/pkg/trafficrouting/route/ingress/route.go similarity index 57% rename from pkg/route/ingress/store.go rename to pkg/trafficrouting/route/ingress/route.go index 220f309..d2adb5b 100644 --- a/pkg/route/ingress/store.go +++ b/pkg/trafficrouting/route/ingress/route.go @@ -18,18 +18,23 @@ import ( "fmt" networkingv1 "k8s.io/api/networking/v1" + rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "kusionstack.io/rollout/pkg/route" + "kusionstack.io/rollout/pkg/trafficrouting/route" "kusionstack.io/rollout/pkg/utils/accessor" ) -type IgsStore struct { +var GVK = networkingv1.SchemeGroupVersion.WithKind("Ingress") + +var _ route.Route = &routeImpl{} + +type routeImpl struct { accessor.ObjectAccessor } -func NewStorage() route.Route { - return &IgsStore{ +func New() route.Route { + return &routeImpl{ ObjectAccessor: accessor.NewObjectAccessor( GVK, &networkingv1.Ingress{}, @@ -38,16 +43,15 @@ func NewStorage() route.Route { } } -func (i *IgsStore) Wrap(client client.Client, cluster string, route client.Object) (route.RouteControl, error) { - igs, ok := route.(*networkingv1.Ingress) +func (r *routeImpl) GetController(client client.Client, br *rolloutv1alpha1.BackendRouting, routeObj client.Object, routeStatus rolloutv1alpha1.BackendRouteStatus) (route.RouteController, error) { + igs, ok := routeObj.(*networkingv1.Ingress) if !ok { - return nil, fmt.Errorf("not Ingress") + return nil, fmt.Errorf("input route is not networkingv1.Ingress") } - return &ingressRoute{ - client: client, - obj: igs, - cluster: cluster, + return &ingressControl{ + client: client, + backendrouting: br, + routeObj: igs, + routeStatus: routeStatus, }, nil } - -var _ route.Route = &IgsStore{} diff --git a/pkg/route/interface.go b/pkg/trafficrouting/route/interface.go similarity index 51% rename from pkg/route/interface.go rename to pkg/trafficrouting/route/interface.go index ccc4fff..e0d6eb5 100644 --- a/pkg/route/interface.go +++ b/pkg/trafficrouting/route/interface.go @@ -18,32 +18,28 @@ package route import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" "kusionstack.io/rollout/pkg/utils/accessor" ) -type BackendChangeDetail struct { - Src string - Dst string - Kind string - ApiVersion string +type Route interface { + accessor.ObjectAccessor + + // GetController returns a RouteController to manage the route. + GetController(client client.Client, br *rolloutv1alpha1.BackendRouting, route client.Object, routeStatus rolloutv1alpha1.BackendRouteStatus) (RouteController, error) } -type RouteControl interface { - ChangeOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, to string) error - ResetOrigin(ctx context.Context, originBackend rolloutv1alpha1.CrossClusterObjectReference, from string) error +type RouteController interface { + GetRoute() client.Object - AddCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error - DeleteCanary(ctx context.Context, obj *rolloutv1alpha1.BackendRouting) error -} + GetCondition(ctx context.Context) ([]metav1.Condition, error) -type Route interface { - accessor.ObjectAccessor + Initialize(ctx context.Context) error + Reset(ctx context.Context) error - // Wrap get a client.Object and returns a route interface - Wrap(client client.Client, cluster string, route client.Object) (RouteControl, error) - // Get returns a wrapped route interface - // Get(ctx context.Context, cluster, namespace, name string) (RouteControl, error) + AddCanary(ctx context.Context) error + DeleteCanary(ctx context.Context) error } From 37b62d8b79b3d8bc9040c05fa983dc77ce9bc511 Mon Sep 17 00:00:00 2001 From: zoumo Date: Mon, 21 Jul 2025 00:34:45 +0800 Subject: [PATCH 05/10] feat: change interface PodControl to ReplicaObjectControl --- .../podcanarylabel/event_handler.go | 22 +++-------- .../podcanarylabel/podcanarylabel.go | 15 +++++-- pkg/workload/collaset/pod_control.go | 39 ++++++++++++++----- pkg/workload/interface.go | 16 ++++---- pkg/workload/statefulset/pod_control.go | 31 +++++++++++---- 5 files changed, 79 insertions(+), 44 deletions(-) diff --git a/pkg/controllers/podcanarylabel/event_handler.go b/pkg/controllers/podcanarylabel/event_handler.go index 361f64a..3e522a7 100644 --- a/pkg/controllers/podcanarylabel/event_handler.go +++ b/pkg/controllers/podcanarylabel/event_handler.go @@ -20,6 +20,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/workqueue" "sigs.k8s.io/controller-runtime/pkg/client" @@ -70,7 +71,7 @@ func (w workloadLabelOrStatusChangedPredict) Update(e event.UpdateEvent) bool { return oldInControl != newInControl || oldProgressing != newProgressing } -func enqueueWorkloadPods(accessor workload.Accessor, reader client.Reader, scheme *runtime.Scheme, logger logr.Logger) handler.EventHandler { +func enqueueWorkloadPods(desiredGVK schema.GroupVersionKind, pc workload.ReplicaObjectControl, reader client.Reader, scheme *runtime.Scheme, logger logr.Logger) handler.EventHandler { handlerWorkloadUpdate := func(evt event.UpdateEvent, q workqueue.RateLimitingInterface) { // check gvk of object obj := evt.ObjectNew @@ -86,30 +87,19 @@ func enqueueWorkloadPods(accessor workload.Accessor, reader client.Reader, schem } gvk := kinds[0] - if accessor.GroupVersionKind() != gvk { + if desiredGVK != gvk { // missmatch group version kind return } - pc, ok := accessor.(workload.PodControl) - if !ok { - return - } - selector, err := pc.GetPodSelector(obj) - if err != nil { - logger.Error(err, "failed to get PodSelector for workload", "key", key.String(), "gvk", gvk.String()) - return - } - - podList := &corev1.PodList{} - err = reader.List(context.Background(), podList, client.InNamespace(obj.GetNamespace()), client.MatchingLabelsSelector{Selector: selector}) + list, err := pc.GetReplicObjects(context.TODO(), reader, obj) if err != nil { logger.Error(err, "failed to list pods for workload", "key", key.String(), "gvk", gvk.String()) return } - for i := range podList.Items { - pod := &podList.Items[i] + for i := range list { + pod := list[i].(*corev1.Pod) q.Add(reconcile.Request{NamespacedName: client.ObjectKeyFromObject(pod)}) } } diff --git a/pkg/controllers/podcanarylabel/podcanarylabel.go b/pkg/controllers/podcanarylabel/podcanarylabel.go index c142b97..fdb9b57 100644 --- a/pkg/controllers/podcanarylabel/podcanarylabel.go +++ b/pkg/controllers/podcanarylabel/podcanarylabel.go @@ -63,11 +63,18 @@ func (r *PodCanaryReconciler) SetupWithManager(mgr manager.Manager) error { allworkloads := rolloutcontroller.GetWatchableWorkloads(r.workloadRegistry, r.Logger, r.Client, r.Config) for _, accessor := range allworkloads { + pc, ok := accessor.(workload.ReplicaObjectControl) + if !ok { + continue + } + if pc.ReplicaType().Kind != "Pod" { + continue + } gvk := accessor.GroupVersionKind() r.Logger.Info("add watcher for workload", "gvk", gvk.String()) b.Watches( multicluster.ClustersKind(&source.Kind{Type: accessor.NewObject()}), - enqueueWorkloadPods(accessor, r.Client, r.Scheme, r.Logger), + enqueueWorkloadPods(gvk, pc, r.Client, r.Scheme, r.Logger), builder.WithPredicates(workloadLabelOrStatusChangedPredict{accessor: accessor}), ) } @@ -114,7 +121,7 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque return reconcile.Result{}, err } - pc, ok := workloadObj.Accessor.(workload.PodControl) + pc, ok := workloadObj.Accessor.(workload.ReplicaObjectControl) if !ok { logger.V(2).Info("accessor does not support pod control, skip reconciling pod") return reconcile.Result{}, nil @@ -137,7 +144,7 @@ func (r *PodCanaryReconciler) Reconcile(ctx context.Context, req reconcile.Reque return reconcile.Result{}, err } -func recognizeTrafficLane(pc workload.PodControl, reader client.Reader, workloadObj client.Object, pod *corev1.Pod) string { +func recognizeTrafficLane(pc workload.ReplicaObjectControl, reader client.Reader, workloadObj client.Object, pod *corev1.Pod) string { if workload.IsCanary(workloadObj) { // canary workload, always set pod revision to canary return rolloutapi.LabelValueTrafficLaneCanary @@ -149,7 +156,7 @@ func recognizeTrafficLane(pc workload.PodControl, reader client.Reader, workload } // workload is progressing, set updated pod revision to canary - if updated, _ := pc.IsUpdatedPod(reader, workloadObj, pod); updated { + if updated, _ := pc.IsUpdateObject(context.TODO(), reader, workloadObj, pod); updated { return rolloutapi.LabelValueTrafficLaneCanary } return rolloutapi.LabelValueTrafficLaneStable diff --git a/pkg/workload/collaset/pod_control.go b/pkg/workload/collaset/pod_control.go index 96423a7..1e932b6 100644 --- a/pkg/workload/collaset/pod_control.go +++ b/pkg/workload/collaset/pod_control.go @@ -17,35 +17,47 @@ package collaset import ( + "context" + "fmt" + + "github.com/samber/lo" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload" ) -var _ workload.PodControl = &accessorImpl{} +var _ workload.ReplicaObjectControl = &accessorImpl{} + +func (c *accessorImpl) ReplicaType() schema.GroupVersionKind { + return corev1.SchemeGroupVersion.WithKind("Pod") +} -func (c *accessorImpl) IsUpdatedPod(_ client.Reader, object client.Object, pod *corev1.Pod) (bool, error) { - obj, err := checkObj(object) +func (c *accessorImpl) IsUpdateObject(_ context.Context, _ client.Reader, workload, obj client.Object) (bool, error) { + cls, err := checkObj(workload) if err != nil { return false, err } - revision := utils.GetMapValueByDefault(pod.Labels, appsv1.ControllerRevisionHashLabelKey, obj.Status.CurrentRevision) - if revision == obj.Status.CurrentRevision { + pod, ok := obj.(*corev1.Pod) + if !ok { + return false, fmt.Errorf("object must be Pod") + } + revision := utils.GetMapValueByDefault(pod.Labels, appsv1.ControllerRevisionHashLabelKey, cls.Status.CurrentRevision) + if revision == cls.Status.CurrentRevision { return false, nil } - if revision == obj.Status.UpdatedRevision { + if revision == cls.Status.UpdatedRevision { return true, nil } return false, nil } -func (c *accessorImpl) GetPodSelector(object client.Object) (labels.Selector, error) { - obj, err := checkObj(object) +func (c *accessorImpl) GetReplicObjects(ctx context.Context, reader client.Reader, workload client.Object) ([]client.Object, error) { + obj, err := checkObj(workload) if err != nil { return nil, err } @@ -53,5 +65,12 @@ func (c *accessorImpl) GetPodSelector(object client.Object) (labels.Selector, er if err != nil { return nil, err } - return selector, nil + + podList := &corev1.PodList{} + err = reader.List(ctx, podList, client.InNamespace(obj.Namespace), client.MatchingLabelsSelector{Selector: selector}) + if err != nil { + return nil, err + } + + return lo.Map(podList.Items, func(pod corev1.Pod, _ int) client.Object { return &pod }), nil } diff --git a/pkg/workload/interface.go b/pkg/workload/interface.go index bdd865f..f47fafd 100644 --- a/pkg/workload/interface.go +++ b/pkg/workload/interface.go @@ -15,8 +15,8 @@ package workload import ( - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" + "context" + "k8s.io/apimachinery/pkg/runtime/schema" "kusionstack.io/kube-api/rollout/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -57,9 +57,11 @@ type CanaryReleaseControl interface { ApplyCanaryPatch(canary client.Object, podTemplatePatch *v1alpha1.MetadataPatch) error } -type PodControl interface { - // IsUpdatedPod checks if the pod revision is updated of the workload - IsUpdatedPod(reader client.Reader, obj client.Object, pod *corev1.Pod) (bool, error) - // GetPodSelector gets the pod selector of the workload - GetPodSelector(obj client.Object) (labels.Selector, error) +type ReplicaObjectControl interface { + // RepliceType returns the type of replica object + ReplicaType() schema.GroupVersionKind + // IsUpdateObject checks if the replica object revision is updated of the workload + IsUpdateObject(ctx context.Context, reader client.Reader, workload, object client.Object) (bool, error) + // GetReplicObjects gets the pod selector of the workload + GetReplicObjects(ctx context.Context, reader client.Reader, workload client.Object) ([]client.Object, error) } diff --git a/pkg/workload/statefulset/pod_control.go b/pkg/workload/statefulset/pod_control.go index 461f148..b3bcd9d 100644 --- a/pkg/workload/statefulset/pod_control.go +++ b/pkg/workload/statefulset/pod_control.go @@ -17,23 +17,35 @@ package statefulset import ( + "context" + "fmt" + + "github.com/samber/lo" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" "kusionstack.io/rollout/pkg/utils" "kusionstack.io/rollout/pkg/workload" ) -var _ workload.PodControl = &accessorImpl{} +var _ workload.ReplicaObjectControl = &accessorImpl{} + +func (c *accessorImpl) ReplicaType() schema.GroupVersionKind { + return corev1.SchemeGroupVersion.WithKind("Pod") +} -func (c *accessorImpl) IsUpdatedPod(_ client.Reader, obj client.Object, pod *corev1.Pod) (bool, error) { - sts, ok := obj.(*appsv1.StatefulSet) +func (c *accessorImpl) IsUpdateObject(_ context.Context, _ client.Reader, workload, obj client.Object) (bool, error) { + sts, ok := workload.(*appsv1.StatefulSet) if !ok { return false, ObjectTypeError } + pod, ok := obj.(*corev1.Pod) + if !ok { + return false, fmt.Errorf("object must be Pod") + } revision := utils.GetMapValueByDefault(pod.Labels, appsv1.ControllerRevisionHashLabelKey, sts.Status.CurrentRevision) if revision == sts.Status.CurrentRevision { return false, nil @@ -44,8 +56,8 @@ func (c *accessorImpl) IsUpdatedPod(_ client.Reader, obj client.Object, pod *cor return false, nil } -func (c *accessorImpl) GetPodSelector(obj client.Object) (labels.Selector, error) { - sts, ok := obj.(*appsv1.StatefulSet) +func (c *accessorImpl) GetReplicObjects(ctx context.Context, reader client.Reader, workload client.Object) ([]client.Object, error) { + sts, ok := workload.(*appsv1.StatefulSet) if !ok { return nil, ObjectTypeError } @@ -53,5 +65,10 @@ func (c *accessorImpl) GetPodSelector(obj client.Object) (labels.Selector, error if err != nil { return nil, err } - return selector, nil + podList := &corev1.PodList{} + err = reader.List(ctx, podList, client.InNamespace(sts.Namespace), client.MatchingLabelsSelector{Selector: selector}) + if err != nil { + return nil, err + } + return lo.Map(podList.Items, func(pod corev1.Pod, _ int) client.Object { return &pod }), nil } From 9c6d1667b47994a7a217f8dad1638f25569633ff Mon Sep 17 00:00:00 2001 From: zoumo Date: Mon, 21 Jul 2025 00:59:55 +0800 Subject: [PATCH 06/10] feat: sync canary workload status --- .../v1alpha1/validation/rolloutrun_test.go | 5 +- ...ollout.kusionstack.io_backendroutings.yaml | 21 +----- .../rollout.kusionstack.io_rolloutruns.yaml | 47 +++++++++++++ pkg/controllers/rollout/rollout_controller.go | 2 +- .../rolloutrun/executor/context.go | 2 +- .../rolloutrun/executor/do_hook_test.go | 70 ++++++++++++------- .../rolloutrun/rolloutrun_controller.go | 48 ++++++++++--- pkg/controllers/traffictopology/adapter.go | 2 +- pkg/workload/info.go | 33 +++++---- pkg/workload/util.go | 4 +- 10 files changed, 152 insertions(+), 82 deletions(-) diff --git a/apis/rollout/v1alpha1/validation/rolloutrun_test.go b/apis/rollout/v1alpha1/validation/rolloutrun_test.go index 9414704..0b19ad3 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun_test.go @@ -319,9 +319,8 @@ func TestValidateRolloutRunUpdate(t *testing.T) { oldObj: validRolloutRun, newObj: func() *rolloutv1alpha1.RolloutRun { obj := validRolloutRun.DeepCopy() - obj.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: rolloutv1alpha1.RolloutStepRunning, - } + obj.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} + obj.Status.CanaryStatus.State = rolloutv1alpha1.RolloutStepRunning obj.Spec.Canary.Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Canary.TemplateMetadataPatch.Labels["canary"] = "false" return obj diff --git a/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml b/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml index 0a9001a..ba2bf57 100644 --- a/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml +++ b/config/crd/bases/rollout.kusionstack.io_backendroutings.yaml @@ -2501,22 +2501,7 @@ spec: description: Cluster indicates the name of cluster type: string condition: - description: |- - Condition contains details for one aspect of the current state of this API Resource. - --- - This struct is intended for direct use as an array at the field path .status.conditions. For example, - type FooStatus struct{ - // Represents the observations of a foo's current state. - // Known .status.conditions.type are: "Available", "Progressing", and "Degraded" - // +patchMergeKey=type - // +patchStrategy=merge - // +listType=map - // +listMapKey=type - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` - - - // other fields - } + description: Route condition properties: lastTransitionTime: description: |- @@ -2574,11 +2559,11 @@ spec: - type type: object forwarding: + description: Forwarding statuses properties: canary: properties: backendName: - description: Name is the name of the referent. type: string conditions: description: Backendonditions represents the current condition of an backend. @@ -2604,7 +2589,6 @@ spec: origin: properties: backendName: - description: Name is the name of the referent. type: string conditions: description: Backendonditions represents the current condition of an backend. @@ -2630,7 +2614,6 @@ spec: stable: properties: backendName: - description: Name is the name of the referent. type: string conditions: description: Backendonditions represents the current condition of an backend. diff --git a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml index f008832..9b519ba 100644 --- a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml @@ -4754,6 +4754,53 @@ spec: state: description: State is Rollout step state type: string + targetStatuses: + description: TargetStatuses describes the referenced workloads status + items: + properties: + cluster: + description: Cluster defines which cluster the workload is in. + type: string + generation: + description: Generation is the found in workload metadata. + format: int64 + type: integer + name: + description: Name is the workload name + type: string + observedGeneration: + description: ObservedGeneration is the most recent generation observed for this workload. + format: int64 + type: integer + replicas: + description: Replicas is the desired number of pods targeted by workload + format: int32 + type: integer + stableRevision: + description: StableRevision is the old stable revision used to generate pods. + type: string + updatedAvailableReplicas: + description: UpdatedAvailableReplicas is the number of service available pods targeted by workload that have the updated template spec. + format: int32 + type: integer + updatedReadyReplicas: + description: UpdatedReadyReplicas is the number of ready pods targeted by workload that have the updated template spec. + format: int32 + type: integer + updatedReplicas: + description: UpdatedReplicas is the number of pods targeted by workload that have the updated template spec. + format: int32 + type: integer + updatedRevision: + description: UpdatedRevision is the updated template revision used to generate pods. + type: string + required: + - replicas + - updatedAvailableReplicas + - updatedReadyReplicas + - updatedReplicas + type: object + type: array targets: description: WorkloadDetails contains release details for each workload items: diff --git a/pkg/controllers/rollout/rollout_controller.go b/pkg/controllers/rollout/rollout_controller.go index a5a065d..eb5f07a 100644 --- a/pkg/controllers/rollout/rollout_controller.go +++ b/pkg/controllers/rollout/rollout_controller.go @@ -514,7 +514,7 @@ func (r *RolloutReconciler) findWorkloadsCrossCluster(ctx context.Context, obj * if err != nil { return nil, nil, err } - workloads, err := workload.List(ctx, r.Client, rest, obj.GetNamespace(), obj.Spec.WorkloadRef.Match) + workloads, _, err := workload.List(ctx, r.Client, rest, obj.GetNamespace(), obj.Spec.WorkloadRef.Match) if err != nil { return nil, nil, err } diff --git a/pkg/controllers/rolloutrun/executor/context.go b/pkg/controllers/rolloutrun/executor/context.go index 6471523..e6ae932 100644 --- a/pkg/controllers/rolloutrun/executor/context.go +++ b/pkg/controllers/rolloutrun/executor/context.go @@ -63,7 +63,7 @@ func (c *ExecutorContext) Initialize() { // init canary status if c.RolloutRun.Spec.Canary != nil && newStatus.CanaryStatus == nil { - newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{} + newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} } // init BatchStatus if c.RolloutRun.Spec.Batch != nil { diff --git a/pkg/controllers/rolloutrun/executor/do_hook_test.go b/pkg/controllers/rolloutrun/executor/do_hook_test.go index 02a0ae9..17568ee 100644 --- a/pkg/controllers/rolloutrun/executor/do_hook_test.go +++ b/pkg/controllers/rolloutrun/executor/do_hook_test.go @@ -131,8 +131,10 @@ func (s *webhookExecutorTestSuite) Test_Webhook_Retry() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + }, } ctx := createTestExecutorContext(rollout, rolloutRun) @@ -203,8 +205,10 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + }, } return rollout, rolloutRun }, @@ -238,19 +242,21 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - Webhooks: []rolloutv1alpha1.RolloutWebhookStatus{ - { - State: rolloutv1alpha1.WebhookCompleted, - HookType: rolloutv1alpha1.PreCanaryStepHook, - Name: s.webhook1.Name, - CodeReasonMessage: s.webhook1Error, - FailureCount: 1, - }, - { - HookType: rolloutv1alpha1.PreCanaryStepHook, - Name: s.webhook2.Name, // current webhook + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + Webhooks: []rolloutv1alpha1.RolloutWebhookStatus{ + { + State: rolloutv1alpha1.WebhookCompleted, + HookType: rolloutv1alpha1.PreCanaryStepHook, + Name: s.webhook1.Name, + CodeReasonMessage: s.webhook1Error, + FailureCount: 1, + }, + { + HookType: rolloutv1alpha1.PreCanaryStepHook, + Name: s.webhook2.Name, // current webhook + }, }, }, } @@ -294,8 +300,10 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + }, } return rollout, rolloutRun }, @@ -329,8 +337,10 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + }, } return rollout, rolloutRun }, @@ -371,8 +381,10 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { HookTypes: []rolloutv1alpha1.HookType{}, // the hookType matches no one }, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + }, } return rollout, rolloutRun }, @@ -405,8 +417,10 @@ func (s *webhookExecutorTestSuite) Test_webhook_PostCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPostCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPostCanaryStepHook, + }, } return rollout, rolloutRun }, @@ -439,8 +453,10 @@ func (s *webhookExecutorTestSuite) Test_webhook_PostCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPostCanaryStepHook, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ + RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPostCanaryStepHook, + }, } return rollout, rolloutRun }, diff --git a/pkg/controllers/rolloutrun/rolloutrun_controller.go b/pkg/controllers/rolloutrun/rolloutrun_controller.go index e9873a9..dd97518 100644 --- a/pkg/controllers/rolloutrun/rolloutrun_controller.go +++ b/pkg/controllers/rolloutrun/rolloutrun_controller.go @@ -131,7 +131,7 @@ func (r *RolloutRunReconciler) Reconcile(ctx context.Context, req ctrl.Request) newStatus := obj.Status.DeepCopy() - accessor, workloads, err := r.findWorkloadsCrossCluster(ctx, obj) + accessor, workloads, canaryWorkloads, err := r.findWorkloadsCrossCluster(ctx, obj) if err != nil { return reconcile.Result{}, err } @@ -143,7 +143,7 @@ func (r *RolloutRunReconciler) Reconcile(ctx context.Context, req ctrl.Request) logger.Error(tempErr, "failed to clean up annotation") } - updateStatus := r.updateStatusOnly(ctx, obj, newStatus, workloads) + updateStatus := r.updateStatusOnly(ctx, obj, newStatus, workloads, canaryWorkloads) if updateStatus != nil { logger.Error(updateStatus, "failed to update status") return reconcile.Result{}, updateStatus @@ -304,7 +304,7 @@ func (r *RolloutRunReconciler) findOwnerKindName(rolloutRun *rolloutv1alpha1.Rol return "", "" } -func (r *RolloutRunReconciler) findWorkloadsCrossCluster(ctx context.Context, obj *rolloutv1alpha1.RolloutRun) (workload.Accessor, *workload.Set, error) { +func (r *RolloutRunReconciler) findWorkloadsCrossCluster(ctx context.Context, obj *rolloutv1alpha1.RolloutRun) (workload.Accessor, *workload.Set, *workload.Set, error) { all := make([]rolloutv1alpha1.CrossClusterObjectNameReference, 0) for _, b := range obj.Spec.Batch.Batches { @@ -317,19 +317,20 @@ func (r *RolloutRunReconciler) findWorkloadsCrossCluster(ctx context.Context, ob } gvk := schema.FromAPIVersionAndKind(obj.Spec.TargetType.APIVersion, obj.Spec.TargetType.Kind) - accesor, err := r.workloadRegistry.Get(gvk) + accessor, err := r.workloadRegistry.Get(gvk) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - list, err := workload.List(ctx, r.Client, accesor, obj.Namespace, match) + list, canaryList, err := workload.List(ctx, r.Client, accessor, obj.Namespace, match) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - return accesor, workload.NewSet(list...), nil + return accessor, workload.NewSet(list...), workload.NewSet(canaryList...), nil } -func (r *RolloutRunReconciler) syncWorkloadStatus(newStatus *rolloutv1alpha1.RolloutRunStatus, workloads *workload.Set) { +func (r *RolloutRunReconciler) syncWorkloadStatus(newStatus *rolloutv1alpha1.RolloutRunStatus, workloads, canaryWorkloads *workload.Set) { + // generate workload status allWorkloads := workloads.ToSlice() sort.Slice(allWorkloads, func(i, j int) bool { iInfo := allWorkloads[i] @@ -347,11 +348,36 @@ func (r *RolloutRunReconciler) syncWorkloadStatus(newStatus *rolloutv1alpha1.Rol workloadStatuses[i] = info.APIStatus() } newStatus.TargetStatuses = workloadStatuses + + // generate canary workload status + allCanaryWorkloads := canaryWorkloads.ToSlice() + if len(allCanaryWorkloads) > 0 { + sort.Slice(allCanaryWorkloads, func(i, j int) bool { + iInfo := allCanaryWorkloads[i] + jInfo := allCanaryWorkloads[j] + + if iInfo.ClusterName == jInfo.ClusterName { + return iInfo.Name < jInfo.Name + } + + return iInfo.ClusterName < jInfo.ClusterName + }) + canaryWorkloadStatuses := make([]rolloutv1alpha1.RolloutWorkloadStatus, len(allCanaryWorkloads)) + for i := range allCanaryWorkloads { + info := allCanaryWorkloads[i] + canaryWorkloadStatuses[i] = info.APIStatus() + } + + if newStatus.CanaryStatus == nil { + newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} + newStatus.CanaryStatus.TargetStatuses = canaryWorkloadStatuses + } + } } -func (r *RolloutRunReconciler) updateStatusOnly(ctx context.Context, obj *rolloutv1alpha1.RolloutRun, newStatus *rolloutv1alpha1.RolloutRunStatus, workloads *workload.Set) error { +func (r *RolloutRunReconciler) updateStatusOnly(ctx context.Context, obj *rolloutv1alpha1.RolloutRun, newStatus *rolloutv1alpha1.RolloutRunStatus, workloads, canaryWorkloads *workload.Set) error { // generate workload status - r.syncWorkloadStatus(newStatus, workloads) + r.syncWorkloadStatus(newStatus, workloads, canaryWorkloads) if equality.Semantic.DeepEqual(obj.Status, *newStatus) { // no change diff --git a/pkg/controllers/traffictopology/adapter.go b/pkg/controllers/traffictopology/adapter.go index 08d58af..11a08d4 100644 --- a/pkg/controllers/traffictopology/adapter.go +++ b/pkg/controllers/traffictopology/adapter.go @@ -102,7 +102,7 @@ func (t *TPControllerAdapter) GetExpectedEmployer(ctx context.Context, employer return expected, err } - workloads, err := workload.List(ctx, t.Client, inter, trafficTopology.Namespace, trafficTopology.Spec.WorkloadRef.Match) + workloads, _, err := workload.List(ctx, t.Client, inter, trafficTopology.Namespace, trafficTopology.Spec.WorkloadRef.Match) if err != nil { return expected, err } diff --git a/pkg/workload/info.go b/pkg/workload/info.go index a72ae18..9bfdbdc 100644 --- a/pkg/workload/info.go +++ b/pkg/workload/info.go @@ -26,7 +26,6 @@ import ( "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - rolloutapi "kusionstack.io/kube-api/rollout" rolloutv1alpha1 "kusionstack.io/kube-api/rollout/v1alpha1" "kusionstack.io/kube-utils/multicluster/clusterinfo" "sigs.k8s.io/controller-runtime/pkg/client" @@ -149,38 +148,33 @@ func Get(ctx context.Context, c client.Client, inter Accessor, cluster, namespac // List return a list of workloads that match the given namespace and match. // It will ignore canary workloads or deleted workloads by default. -func List(ctx context.Context, c client.Client, inter Accessor, namespace string, match rolloutv1alpha1.ResourceMatch) ([]*Info, error) { +func List(ctx context.Context, c client.Client, inter Accessor, namespace string, match rolloutv1alpha1.ResourceMatch) (workloads []*Info, canaryWorkloads []*Info, err error) { listObj := inter.NewObjectList() if err := c.List(clusterinfo.WithCluster(ctx, clusterinfo.Clusters), listObj, &client.ListOptions{Namespace: namespace}); err != nil { - return nil, err + return nil, nil, err } matcher := MatchAsMatcher(match) listPtr, err := meta.GetItemsPtr(listObj) if err != nil { - return nil, err + return nil, nil, err } v, err := conversion.EnforcePtr(listPtr) if err != nil || v.Kind() != reflect.Slice { - return nil, fmt.Errorf("neet ptr to slice: %w", err) + return nil, nil, fmt.Errorf("neet ptr to slice: %w", err) } length := v.Len() - workloads := make([]*Info, 0) + workloads = make([]*Info, 0) + canaryWorkloads = make([]*Info, 0) - for i := 0; i < length; i++ { + for i := range length { elemPtr := v.Index(i).Addr().Interface() obj, ok := elemPtr.(client.Object) if !ok { - return nil, fmt.Errorf("can not convert element to client.Object") - } - - canary := utils.GetMapValueByDefault(obj.GetLabels(), rolloutapi.LabelCanary, "false") - if canary == "true" { - // ignore canary workload here, you should get canary worload from release control interface - continue + return nil, nil, fmt.Errorf("can not convert element to client.Object") } if obj.GetDeletionTimestamp() != nil { @@ -188,15 +182,20 @@ func List(ctx context.Context, c client.Client, inter Accessor, namespace string continue } + isCanary := IsCanary(obj) cluster := GetClusterFromLabel(obj.GetLabels()) if !matcher.Matches(cluster, obj.GetName(), obj.GetLabels()) { continue } info, err := inter.GetInfo(cluster, obj) if err != nil { - return nil, err + return nil, nil, err + } + if isCanary { + canaryWorkloads = append(canaryWorkloads, info) + } else { + workloads = append(workloads, info) } - workloads = append(workloads, info) } - return workloads, nil + return workloads, canaryWorkloads, nil } diff --git a/pkg/workload/util.go b/pkg/workload/util.go index f2a5ffb..b302da8 100644 --- a/pkg/workload/util.go +++ b/pkg/workload/util.go @@ -105,8 +105,8 @@ func IsProgressing(workload client.Object) bool { } func IsCanary(workload client.Object) bool { - _, ok := utils.GetMapValue(workload.GetLabels(), rolloutapi.LabelCanary) - return ok + canary, ok := utils.GetMapValue(workload.GetLabels(), rolloutapi.LabelCanary) + return ok && canary == "true" } type Owner struct { From 8b645114ec4c66a2f89cc8c4e5e535bd17cd8cce Mon Sep 17 00:00:00 2001 From: zoumo Date: Mon, 21 Jul 2025 01:00:43 +0800 Subject: [PATCH 07/10] feat: update api --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 829ce2a..3aa72e4 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v1.22.2 k8s.io/utils v0.0.0-20241210054802-24370beab758 - kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1 + kusionstack.io/kube-api v0.6.7-0.20250720165902-a400661d33a0 kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 kusionstack.io/resourceconsist v0.0.2 sigs.k8s.io/controller-runtime v0.21.0 diff --git a/go.sum b/go.sum index 2fbb921..58b13f4 100644 --- a/go.sum +++ b/go.sum @@ -1025,6 +1025,8 @@ kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6 h1:ZXP+K55y4j9SKmLr kusionstack.io/kube-api v0.6.7-0.20250719054959-1cbe2be851f6/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1 h1:j/yoU/mjITbd5cQc7IaJGiEnVD9S36PsXbWkJcwYLKo= kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= +kusionstack.io/kube-api v0.6.7-0.20250720165902-a400661d33a0 h1:bgna3I0Xi0XGvdU+hwLc6ZFuNKPeTgZOAgUZaVW2ICQ= +kusionstack.io/kube-api v0.6.7-0.20250720165902-a400661d33a0/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 h1:HYE6Wa8EzSlA6UmaTLtNKUgkB2mmasp6Ul69d3/SpK0= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6/go.mod h1:5Uy3GCJ1JEGqZw/Sp/uVnHBJN1t9wjY6USPSZ9s4idk= kusionstack.io/resourceconsist v0.0.2 h1:gf+c/LOMsiKoVR+GLzOomw8qcUbZbPckQLczZllNdVM= From dd639d21e73869a71f7792fd5319b9f43807fcea Mon Sep 17 00:00:00 2001 From: zoumo Date: Mon, 21 Jul 2025 14:52:02 +0800 Subject: [PATCH 08/10] fix: filter canary workloads --- pkg/controllers/rolloutrun/control/control.go | 14 +++----- .../rolloutrun/rolloutrun_controller.go | 2 +- pkg/workload/info.go | 35 +++++++++++++++---- pkg/workload/util.go | 4 +-- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/pkg/controllers/rolloutrun/control/control.go b/pkg/controllers/rolloutrun/control/control.go index 36eef36..bc48d55 100644 --- a/pkg/controllers/rolloutrun/control/control.go +++ b/pkg/controllers/rolloutrun/control/control.go @@ -144,7 +144,7 @@ func (c *CanaryReleaseControl) Initialize(ctx context.Context, stable *workload. } func (c *CanaryReleaseControl) Finalize(ctx context.Context, stable *workload.Info) error { - canaryObj, err := c.getCanaryObject(stable.ClusterName, stable.Namespace, stable.Name) + canaryObj, err := c.GetCanaryObject(stable.ClusterName, stable.Namespace, stable.Name) if client.IgnoreNotFound(err) != nil { return err } @@ -221,15 +221,11 @@ func (c *CanaryReleaseControl) CreateOrUpdate(ctx context.Context, stable *workl return controllerutil.OperationResultUpdated, canaryInfo, nil } -func (c *CanaryReleaseControl) getCanaryName(stableName string) string { - return stableName + "-canary" -} - -func (c *CanaryReleaseControl) getCanaryObject(cluster, namespace, name string) (client.Object, error) { +func (c *CanaryReleaseControl) GetCanaryObject(cluster, namespace, name string) (client.Object, error) { if strings.HasSuffix(name, "-canary") { return nil, fmt.Errorf("input name should not end with -canary, got=%s", name) } - canaryName := c.getCanaryName(name) + canaryName := workload.GetCanaryName(name) canaryObj := c.workload.NewObject() err := c.client.Get( clusterinfo.WithCluster(context.TODO(), cluster), @@ -241,7 +237,7 @@ func (c *CanaryReleaseControl) getCanaryObject(cluster, namespace, name string) func (c *CanaryReleaseControl) canaryObject(stable *workload.Info) (client.Object, bool, error) { // retrieve canary object - canaryObj, err := c.getCanaryObject(stable.ClusterName, stable.Namespace, stable.Name) + canaryObj, err := c.GetCanaryObject(stable.ClusterName, stable.Namespace, stable.Name) if client.IgnoreNotFound(err) != nil { return nil, false, err } @@ -267,7 +263,7 @@ func (c *CanaryReleaseControl) canaryObject(stable *workload.Info) (client.Objec canaryObj.SetFinalizers(nil) canaryObj.SetManagedFields(nil) // set canary metadata - canaryObj.SetName(c.getCanaryName(stable.Name)) + canaryObj.SetName(workload.GetCanaryName(stable.Name)) } return canaryObj, found, nil diff --git a/pkg/controllers/rolloutrun/rolloutrun_controller.go b/pkg/controllers/rolloutrun/rolloutrun_controller.go index dd97518..757f4a5 100644 --- a/pkg/controllers/rolloutrun/rolloutrun_controller.go +++ b/pkg/controllers/rolloutrun/rolloutrun_controller.go @@ -370,8 +370,8 @@ func (r *RolloutRunReconciler) syncWorkloadStatus(newStatus *rolloutv1alpha1.Rol if newStatus.CanaryStatus == nil { newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} - newStatus.CanaryStatus.TargetStatuses = canaryWorkloadStatuses } + newStatus.CanaryStatus.TargetStatuses = canaryWorkloadStatuses } } diff --git a/pkg/workload/info.go b/pkg/workload/info.go index 9bfdbdc..df5f939 100644 --- a/pkg/workload/info.go +++ b/pkg/workload/info.go @@ -147,8 +147,7 @@ func Get(ctx context.Context, c client.Client, inter Accessor, cluster, namespac } // List return a list of workloads that match the given namespace and match. -// It will ignore canary workloads or deleted workloads by default. -func List(ctx context.Context, c client.Client, inter Accessor, namespace string, match rolloutv1alpha1.ResourceMatch) (workloads []*Info, canaryWorkloads []*Info, err error) { +func List(ctx context.Context, c client.Client, inter Accessor, namespace string, match rolloutv1alpha1.ResourceMatch) (workloads, canaryWorkloads []*Info, err error) { listObj := inter.NewObjectList() if err := c.List(clusterinfo.WithCluster(ctx, clusterinfo.Clusters), listObj, &client.ListOptions{Namespace: namespace}); err != nil { return nil, nil, err @@ -168,7 +167,7 @@ func List(ctx context.Context, c client.Client, inter Accessor, namespace string length := v.Len() workloads = make([]*Info, 0) - canaryWorkloads = make([]*Info, 0) + canaryObjects := map[string]client.Object{} for i := range length { elemPtr := v.Index(i).Addr().Interface() @@ -182,7 +181,11 @@ func List(ctx context.Context, c client.Client, inter Accessor, namespace string continue } - isCanary := IsCanary(obj) + if IsCanary(obj) { + // ignore canary workload here + canaryObjects[obj.GetName()] = obj + continue + } cluster := GetClusterFromLabel(obj.GetLabels()) if !matcher.Matches(cluster, obj.GetName(), obj.GetLabels()) { continue @@ -191,11 +194,29 @@ func List(ctx context.Context, c client.Client, inter Accessor, namespace string if err != nil { return nil, nil, err } - if isCanary { + workloads = append(workloads, info) + } + + _, canCanary := inter.(CanaryReleaseControl) + if canCanary { + // find canary workload + for _, w := range workloads { + name := GetCanaryName(w.Name) + canaryObject, ok := canaryObjects[name] + if !ok { + continue + } + cluster := GetClusterFromLabel(canaryObject.GetLabels()) + info, err := inter.GetInfo(cluster, canaryObject) + if err != nil { + return nil, nil, err + } canaryWorkloads = append(canaryWorkloads, info) - } else { - workloads = append(workloads, info) } } return workloads, canaryWorkloads, nil } + +func GetCanaryName(workloadName string) string { + return workloadName + "-canary" +} diff --git a/pkg/workload/util.go b/pkg/workload/util.go index b302da8..b3e7244 100644 --- a/pkg/workload/util.go +++ b/pkg/workload/util.go @@ -105,8 +105,8 @@ func IsProgressing(workload client.Object) bool { } func IsCanary(workload client.Object) bool { - canary, ok := utils.GetMapValue(workload.GetLabels(), rolloutapi.LabelCanary) - return ok && canary == "true" + canaryValue := workload.GetLabels()[rolloutapi.LabelCanary] + return canaryValue == "true" } type Owner struct { From 7257b560a565669c43983762edf77610b0d1da5e Mon Sep 17 00:00:00 2001 From: zoumo Date: Mon, 21 Jul 2025 15:30:21 +0800 Subject: [PATCH 09/10] feat: bump up kusionstack.io/kube-api --- go.mod | 2 +- go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3aa72e4..01b8565 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( k8s.io/klog/v2 v2.130.1 k8s.io/kubernetes v1.22.2 k8s.io/utils v0.0.0-20241210054802-24370beab758 - kusionstack.io/kube-api v0.6.7-0.20250720165902-a400661d33a0 + kusionstack.io/kube-api v0.7.0 kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 kusionstack.io/resourceconsist v0.0.2 sigs.k8s.io/controller-runtime v0.21.0 diff --git a/go.sum b/go.sum index 58b13f4..c7efeee 100644 --- a/go.sum +++ b/go.sum @@ -1027,6 +1027,10 @@ kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1 h1:j/yoU/mjITbd5cQc kusionstack.io/kube-api v0.6.7-0.20250720104212-3e44585627a1/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= kusionstack.io/kube-api v0.6.7-0.20250720165902-a400661d33a0 h1:bgna3I0Xi0XGvdU+hwLc6ZFuNKPeTgZOAgUZaVW2ICQ= kusionstack.io/kube-api v0.6.7-0.20250720165902-a400661d33a0/go.mod h1:ZrLpR6T7HzZp5UGSTXxzNCRizCC66mn2oGJWfL3VONc= +kusionstack.io/kube-api v0.6.7-0.20250721072644-0af27496d542 h1:JM7CsrsbwBK//eF8WkPlvw0IIj4sHT5SN3xHMryZSmM= +kusionstack.io/kube-api v0.6.7-0.20250721072644-0af27496d542/go.mod h1:e1jtrQH2LK5fD2nTyfIXG6nYrYbU8VXShRxTRwVPaLk= +kusionstack.io/kube-api v0.7.0 h1:jYjNq9LbpqVCNPU4cQZJI17G5gwZGJbhgAYi0BaTe7g= +kusionstack.io/kube-api v0.7.0/go.mod h1:e1jtrQH2LK5fD2nTyfIXG6nYrYbU8VXShRxTRwVPaLk= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6 h1:HYE6Wa8EzSlA6UmaTLtNKUgkB2mmasp6Ul69d3/SpK0= kusionstack.io/kube-utils v0.2.1-0.20250613035327-11e9cdaec9d6/go.mod h1:5Uy3GCJ1JEGqZw/Sp/uVnHBJN1t9wjY6USPSZ9s4idk= kusionstack.io/resourceconsist v0.0.2 h1:gf+c/LOMsiKoVR+GLzOomw8qcUbZbPckQLczZllNdVM= From f66f10ebb4b6b4d152ab0b2622a371428a96edaa Mon Sep 17 00:00:00 2001 From: zoumo Date: Mon, 21 Jul 2025 15:53:27 +0800 Subject: [PATCH 10/10] refactor: simplify canary status --- .../v1alpha1/validation/rolloutrun_test.go | 2 +- .../rollout.kusionstack.io_rolloutruns.yaml | 47 ------------- .../rolloutrun/executor/context.go | 2 +- .../rolloutrun/executor/do_hook_test.go | 70 +++++++------------ .../rolloutrun/rolloutrun_controller.go | 4 +- 5 files changed, 31 insertions(+), 94 deletions(-) diff --git a/apis/rollout/v1alpha1/validation/rolloutrun_test.go b/apis/rollout/v1alpha1/validation/rolloutrun_test.go index 0b19ad3..b85570c 100644 --- a/apis/rollout/v1alpha1/validation/rolloutrun_test.go +++ b/apis/rollout/v1alpha1/validation/rolloutrun_test.go @@ -319,7 +319,7 @@ func TestValidateRolloutRunUpdate(t *testing.T) { oldObj: validRolloutRun, newObj: func() *rolloutv1alpha1.RolloutRun { obj := validRolloutRun.DeepCopy() - obj.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} + obj.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{} obj.Status.CanaryStatus.State = rolloutv1alpha1.RolloutStepRunning obj.Spec.Canary.Targets[0].Replicas = intstr.FromInt(2) obj.Spec.Canary.TemplateMetadataPatch.Labels["canary"] = "false" diff --git a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml index 9b519ba..f008832 100644 --- a/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml +++ b/config/crd/bases/rollout.kusionstack.io_rolloutruns.yaml @@ -4754,53 +4754,6 @@ spec: state: description: State is Rollout step state type: string - targetStatuses: - description: TargetStatuses describes the referenced workloads status - items: - properties: - cluster: - description: Cluster defines which cluster the workload is in. - type: string - generation: - description: Generation is the found in workload metadata. - format: int64 - type: integer - name: - description: Name is the workload name - type: string - observedGeneration: - description: ObservedGeneration is the most recent generation observed for this workload. - format: int64 - type: integer - replicas: - description: Replicas is the desired number of pods targeted by workload - format: int32 - type: integer - stableRevision: - description: StableRevision is the old stable revision used to generate pods. - type: string - updatedAvailableReplicas: - description: UpdatedAvailableReplicas is the number of service available pods targeted by workload that have the updated template spec. - format: int32 - type: integer - updatedReadyReplicas: - description: UpdatedReadyReplicas is the number of ready pods targeted by workload that have the updated template spec. - format: int32 - type: integer - updatedReplicas: - description: UpdatedReplicas is the number of pods targeted by workload that have the updated template spec. - format: int32 - type: integer - updatedRevision: - description: UpdatedRevision is the updated template revision used to generate pods. - type: string - required: - - replicas - - updatedAvailableReplicas - - updatedReadyReplicas - - updatedReplicas - type: object - type: array targets: description: WorkloadDetails contains release details for each workload items: diff --git a/pkg/controllers/rolloutrun/executor/context.go b/pkg/controllers/rolloutrun/executor/context.go index e6ae932..6471523 100644 --- a/pkg/controllers/rolloutrun/executor/context.go +++ b/pkg/controllers/rolloutrun/executor/context.go @@ -63,7 +63,7 @@ func (c *ExecutorContext) Initialize() { // init canary status if c.RolloutRun.Spec.Canary != nil && newStatus.CanaryStatus == nil { - newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} + newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{} } // init BatchStatus if c.RolloutRun.Spec.Batch != nil { diff --git a/pkg/controllers/rolloutrun/executor/do_hook_test.go b/pkg/controllers/rolloutrun/executor/do_hook_test.go index 17568ee..02a0ae9 100644 --- a/pkg/controllers/rolloutrun/executor/do_hook_test.go +++ b/pkg/controllers/rolloutrun/executor/do_hook_test.go @@ -131,10 +131,8 @@ func (s *webhookExecutorTestSuite) Test_Webhook_Retry() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, } ctx := createTestExecutorContext(rollout, rolloutRun) @@ -205,10 +203,8 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, } return rollout, rolloutRun }, @@ -242,21 +238,19 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - Webhooks: []rolloutv1alpha1.RolloutWebhookStatus{ - { - State: rolloutv1alpha1.WebhookCompleted, - HookType: rolloutv1alpha1.PreCanaryStepHook, - Name: s.webhook1.Name, - CodeReasonMessage: s.webhook1Error, - FailureCount: 1, - }, - { - HookType: rolloutv1alpha1.PreCanaryStepHook, - Name: s.webhook2.Name, // current webhook - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, + Webhooks: []rolloutv1alpha1.RolloutWebhookStatus{ + { + State: rolloutv1alpha1.WebhookCompleted, + HookType: rolloutv1alpha1.PreCanaryStepHook, + Name: s.webhook1.Name, + CodeReasonMessage: s.webhook1Error, + FailureCount: 1, + }, + { + HookType: rolloutv1alpha1.PreCanaryStepHook, + Name: s.webhook2.Name, // current webhook }, }, } @@ -300,10 +294,8 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, } return rollout, rolloutRun }, @@ -337,10 +329,8 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, } return rollout, rolloutRun }, @@ -381,10 +371,8 @@ func (s *webhookExecutorTestSuite) Test_Webhook_PreCanaryHookStep() { HookTypes: []rolloutv1alpha1.HookType{}, // the hookType matches no one }, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPreCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPreCanaryStepHook, } return rollout, rolloutRun }, @@ -417,10 +405,8 @@ func (s *webhookExecutorTestSuite) Test_webhook_PostCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPostCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPostCanaryStepHook, } return rollout, rolloutRun }, @@ -453,10 +439,8 @@ func (s *webhookExecutorTestSuite) Test_webhook_PostCanaryHookStep() { rolloutRun.Spec.Canary = &rolloutv1alpha1.RolloutRunCanaryStrategy{ Targets: unimportantTargets, } - rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{ - RolloutRunStepStatus: rolloutv1alpha1.RolloutRunStepStatus{ - State: StepPostCanaryStepHook, - }, + rolloutRun.Status.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{ + State: StepPostCanaryStepHook, } return rollout, rolloutRun }, diff --git a/pkg/controllers/rolloutrun/rolloutrun_controller.go b/pkg/controllers/rolloutrun/rolloutrun_controller.go index 757f4a5..fe7c54b 100644 --- a/pkg/controllers/rolloutrun/rolloutrun_controller.go +++ b/pkg/controllers/rolloutrun/rolloutrun_controller.go @@ -369,9 +369,9 @@ func (r *RolloutRunReconciler) syncWorkloadStatus(newStatus *rolloutv1alpha1.Rol } if newStatus.CanaryStatus == nil { - newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunCanaryStatus{} + newStatus.CanaryStatus = &rolloutv1alpha1.RolloutRunStepStatus{} } - newStatus.CanaryStatus.TargetStatuses = canaryWorkloadStatuses + newStatus.CanaryStatus.Targets = canaryWorkloadStatuses } }