diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod index 91f5323ca58..53b8de792c2 100644 --- a/examples/extension-server/go.mod +++ b/examples/extension-server/go.mod @@ -30,8 +30,8 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum index 78924f55902..d3d7871a5d2 100644 --- a/examples/extension-server/go.sum +++ b/examples/extension-server/go.sum @@ -84,12 +84,12 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/go.mod b/go.mod index f2973a8d6f8..0eb33cb35cd 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 - golang.org/x/sys v0.22.0 + golang.org/x/sys v0.28.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 @@ -202,7 +202,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - golang.org/x/crypto v0.25.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/crypto/x509roots/fallback v0.0.0-20240626151235-a6a393ffd658 // indirect google.golang.org/api v0.172.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect @@ -275,9 +275,9 @@ require ( golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.27.0 golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/term v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.22.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 3be30fc7f7d..b2c026e4b39 100644 --- a/go.sum +++ b/go.sum @@ -1202,8 +1202,8 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto/x509roots/fallback v0.0.0-20240626151235-a6a393ffd658 h1:i7K6wQLN/0oxF7FT3tKkfMCstxoT4VGG36YIB9ZKLzI= golang.org/x/crypto/x509roots/fallback v0.0.0-20240626151235-a6a393ffd658/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1350,8 +1350,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/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= @@ -1445,14 +1445,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1464,8 +1464,8 @@ 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 9d4088ef3eb..09dc0888ec7 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -361,7 +361,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen rt = t.buildRetry(policy) } if policy.Spec.Timeout != nil { - if to, err = t.buildTimeout(*policy, nil); err != nil { + if to, err = t.buildTimeout(*policy); err != nil { err = perr.WithMessage(err, "Timeout") errs = errors.Join(errs, err) } @@ -411,8 +411,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen for _, http := range x.HTTP { for _, r := range http.Routes { - // Some timeout setting originate from the route. - if localTo, err := t.buildTimeout(*policy, r.Traffic); err == nil { + if localTo, err := t.buildTimeout(*policy); err == nil { to = localTo } @@ -497,7 +496,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back rt = t.buildRetry(policy) } if policy.Spec.Timeout != nil { - if ct, err = t.buildTimeout(*policy, nil); err != nil { + if ct, err = t.buildTimeout(*policy); err != nil { err = perr.WithMessage(err, "Timeout") errs = errors.Join(errs, err) } @@ -595,7 +594,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back // Update the Host field in HealthCheck, now that we have access to the Route Hostname. r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname) - if ct, err = t.buildTimeout(*policy, r.Traffic); err == nil { + if ct, err = t.buildTimeout(*policy); err == nil { r.Traffic.Timeout = ct } @@ -1066,12 +1065,8 @@ func (t *Translator) buildCircuitBreaker(policy *egv1a1.BackendTrafficPolicy) (* return cb, nil } -func (t *Translator) buildTimeout(policy egv1a1.BackendTrafficPolicy, traffic *ir.TrafficFeatures) (*ir.Timeout, error) { +func (t *Translator) buildTimeout(policy egv1a1.BackendTrafficPolicy) (*ir.Timeout, error) { if policy.Spec.Timeout == nil { - if traffic != nil { - // Don't lose any existing timeout definitions. - return mergeTimeoutSettings(nil, traffic.Timeout), nil - } return nil, nil } var ( @@ -1119,41 +1114,9 @@ func (t *Translator) buildTimeout(policy egv1a1.BackendTrafficPolicy, traffic *i } } - // http request timeout is translated during the gateway-api route resource translation - // merge route timeout setting with backendtrafficpolicy timeout settings. - // Merging is done after the clustersettings definitions are translated so that - // clustersettings will override previous settings. - if traffic != nil { - to = mergeTimeoutSettings(to, traffic.Timeout) - } return to, errs } -// merge secondary into main if both are not nil, otherwise return the -// one that is not nil. If both are nil, returns nil -func mergeTimeoutSettings(main, secondary *ir.Timeout) *ir.Timeout { - switch { - case main == nil && secondary == nil: - return nil - case main == nil: - return secondary.DeepCopy() - case secondary == nil: - return main - default: // Neither main nor secondary are nil here - if secondary.HTTP != nil { - setIfNil(&main.HTTP, &ir.HTTPTimeout{}) - setIfNil(&main.HTTP.RequestTimeout, secondary.HTTP.RequestTimeout) - setIfNil(&main.HTTP.ConnectionIdleTimeout, secondary.HTTP.ConnectionIdleTimeout) - setIfNil(&main.HTTP.MaxConnectionDuration, secondary.HTTP.MaxConnectionDuration) - } - if secondary.TCP != nil { - setIfNil(&main.TCP, &ir.TCPTimeout{}) - setIfNil(&main.TCP.ConnectTimeout, secondary.TCP.ConnectTimeout) - } - return main - } -} - func int64ToUint32(in int64) (uint32, bool) { if in >= 0 && in <= math.MaxUint32 { return uint32(in), true diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index 52df40f4736..a6e13720e44 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -580,10 +580,3 @@ func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences, return ret } - -// Sets *target to value if and only if *target is nil -func setIfNil[T any](target **T, value *T) { - if *target == nil { - *target = value - } -} diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index ce7fff56ae1..62de02fdf2f 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -257,14 +257,13 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe func processRouteTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { if rule.Timeouts != nil { - rto := &ir.Timeout{} if rule.Timeouts.Request != nil { d, err := time.ParseDuration(string(*rule.Timeouts.Request)) if err != nil { d, _ = time.ParseDuration(HTTPRequestTimeout) } - setRequestTimeout(rto, metav1.Duration{Duration: d}) + irRoute.Timeout = ptr.To(metav1.Duration{Duration: d}) } // Also set the IR Route Timeout to the backend request timeout @@ -274,23 +273,8 @@ func processRouteTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { if err != nil { d, _ = time.ParseDuration(HTTPRequestTimeout) } - setRequestTimeout(rto, metav1.Duration{Duration: d}) + irRoute.Timeout = ptr.To(metav1.Duration{Duration: d}) } - - irRoute.Traffic = &ir.TrafficFeatures{ - Timeout: rto, - } - } -} - -func setRequestTimeout(irTimeout *ir.Timeout, d metav1.Duration) { - switch { - case irTimeout.HTTP == nil: - irTimeout.HTTP = &ir.HTTPTimeout{ - RequestTimeout: ptr.To(d), - } - default: - irTimeout.HTTP.RequestTimeout = ptr.To(d) } } @@ -692,11 +676,11 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route Mirrors: routeRoute.Mirrors, ExtensionRefs: routeRoute.ExtensionRefs, IsHTTP2: routeRoute.IsHTTP2, + Timeout: routeRoute.Timeout, } if routeRoute.Traffic != nil { hostRoute.Traffic = &ir.TrafficFeatures{ - Timeout: routeRoute.Traffic.Timeout, - Retry: routeRoute.Traffic.Retry, + Retry: routeRoute.Traffic.Retry, } } perHostRoutes = append(perHostRoutes, hostRoute) diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml index e26f10c353f..33ae5c94859 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml @@ -35,6 +35,27 @@ httpRoutes: port: 8080 timeouts: request: 130s +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + timeouts: + request: 130s backendTrafficPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy @@ -47,4 +68,20 @@ backendTrafficPolicies: kind: HTTPRoute name: httproute-1 useClientProtocol: true - +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + useClientProtocol: true + loadBalancer: + type: ConsistentHash + consistentHash: + type: Cookie + cookie: + name: "test" diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml index 245739ca233..c60ae5b2347 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml @@ -26,6 +26,44 @@ backendTrafficPolicies: status: "True" type: Accepted controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + loadBalancer: + consistentHash: + cookie: + name: test + type: Cookie + type: ConsistentHash + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + useClientProtocol: true + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other backendTrafficPolicies + for these routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway @@ -44,7 +82,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 1 + - attachedRoutes: 2 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -108,6 +146,46 @@ httpRoutes: name: gateway-1 namespace: envoy-gateway sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + timeouts: + request: 130s + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http infraIR: envoy-gateway/gateway-1: proxy: @@ -165,8 +243,33 @@ xdsIR: distinct: false name: "" prefix: / + timeout: 2m10s + traffic: {} + useClientProtocol: true + - destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + timeout: 2m10s traffic: - timeout: - http: - requestTimeout: 2m10s + loadBalancer: + consistentHash: + cookie: + name: test useClientProtocol: true diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml index 8a7e9838848..d26f4a33f3c 100644 --- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml +++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml @@ -169,7 +169,4 @@ xdsIR: distinct: false name: "" prefix: / - traffic: - timeout: - http: - requestTimeout: 1s + timeout: 1s diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml index 04843eba9aa..60c001200b9 100644 --- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml +++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml @@ -332,10 +332,10 @@ xdsIR: distinct: false name: "" prefix: / + timeout: 1s traffic: timeout: http: maxConnectionDuration: 22s - requestTimeout: 1s tcp: connectTimeout: 20s diff --git a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml index c49d551e867..189c968899b 100644 --- a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml +++ b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml @@ -135,7 +135,4 @@ xdsIR: distinct: false name: "" prefix: / - traffic: - timeout: - http: - requestTimeout: 1s + timeout: 1s diff --git a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml index dc1c9cb950d..88b1dc893f3 100644 --- a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml +++ b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml @@ -135,7 +135,4 @@ xdsIR: distinct: false name: "" prefix: / - traffic: - timeout: - http: - requestTimeout: 5s + timeout: 5s diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 68f16439400..f2f51e43b88 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -563,6 +563,8 @@ type HTTPRoute struct { UseClientProtocol *bool `json:"useClientProtocol,omitempty" yaml:"useClientProtocol,omitempty"` // Metadata is used to enrich envoy route metadata with user and provider-specific information Metadata *ResourceMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` + // Timeout is the time until which entire response is received from the upstream. + Timeout *metav1.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` } // TrafficFeatures holds the information associated with the Backend Traffic Policy. diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 3262cf8d721..1616ad5a7c3 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -1331,6 +1331,11 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { *out = new(ResourceMetadata) (*in).DeepCopyInto(*out) } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(v1.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 0f18f11713d..35e19158f7e 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -1873,7 +1873,7 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicyObjectRefs( if backendNamespace != policy.Namespace { from := ObjectKindNamespacedName{ - kind: gatewayapi.KindHTTPRoute, + kind: gatewayapi.KindEnvoyExtensionPolicy, namespace: policy.Namespace, name: policy.Name, } diff --git a/internal/provider/kubernetes/controller_test.go b/internal/provider/kubernetes/controller_test.go index e3b63360163..d6ce239b473 100644 --- a/internal/provider/kubernetes/controller_test.go +++ b/internal/provider/kubernetes/controller_test.go @@ -12,8 +12,10 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" @@ -289,3 +291,158 @@ func TestProcessGatewayClassParamsRef(t *testing.T) { }) } } + +func TestProcessEnvoyExtensionPolicyObjectRefs(t *testing.T) { + testCases := []struct { + name string + envoyExtensionPolicy *egv1a1.EnvoyExtensionPolicy + backend *egv1a1.Backend + referenceGrant *gwapiv1b1.ReferenceGrant + shouldBeAdded bool + }{ + { + name: "valid envoy extension policy with proper ref grant to backend", + envoyExtensionPolicy: &egv1a1.EnvoyExtensionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.EnvoyExtensionPolicySpec{ + ExtProc: []egv1a1.ExtProc{ + { + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(egv1a1.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(gatewayapi.KindEnvoyExtensionPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(egv1a1.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: true, + }, + { + name: "valid envoy extension policy with wrong from kind in ref grant to backend", + envoyExtensionPolicy: &egv1a1.EnvoyExtensionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.EnvoyExtensionPolicySpec{ + ExtProc: []egv1a1.ExtProc{ + { + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(egv1a1.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(gatewayapi.KindHTTPRoute), + Group: gwapiv1.Group(gwapiv1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(egv1a1.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: false, + }, + } + + for i := range testCases { + tc := testCases[i] + // Run the test cases. + t.Run(tc.name, func(t *testing.T) { + // Add objects referenced by test cases. + objs := []client.Object{tc.envoyExtensionPolicy, tc.backend, tc.referenceGrant} + + // Create the reconciler. + logger := logging.DefaultLogger(egv1a1.LogLevelInfo) + + ctx := context.Background() + + r := &gatewayAPIReconciler{ + log: logger, + classController: "some-gateway-class", + } + + r.client = fakeclient.NewClientBuilder(). + WithScheme(envoygateway.GetScheme()). + WithObjects(objs...). + WithIndex(&gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, getReferenceGrantIndexerFunc()). + Build() + + resourceTree := gatewayapi.NewResources() + resourceMap := newResourceMapping() + + err := r.processEnvoyExtensionPolicies(ctx, resourceTree, resourceMap) + require.NoError(t, err) + if tc.shouldBeAdded { + require.Contains(t, resourceTree.ReferenceGrants, tc.referenceGrant) + } else { + require.NotContains(t, resourceTree.ReferenceGrants, tc.referenceGrant) + } + }) + } +} diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go index 443c667e349..8d8a601a680 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -47,17 +47,21 @@ const ( ) func addReferenceGrantIndexers(ctx context.Context, mgr manager.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, func(rawObj client.Object) []string { + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, getReferenceGrantIndexerFunc()); err != nil { + return err + } + return nil +} + +func getReferenceGrantIndexerFunc() func(rawObj client.Object) []string { + return func(rawObj client.Object) []string { refGrant := rawObj.(*gwapiv1b1.ReferenceGrant) var referredServices []string for _, target := range refGrant.Spec.To { referredServices = append(referredServices, string(target.Kind)) } return referredServices - }); err != nil { - return err } - return nil } // addHTTPRouteIndexers adds indexing on HTTPRoute. diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go index 6a9e72f498c..81f9717ed33 100644 --- a/internal/xds/translator/route.go +++ b/internal/xds/translator/route.go @@ -16,6 +16,7 @@ import ( matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils/protocov" @@ -96,12 +97,11 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) { } // Timeouts - if router.GetRoute() != nil && - httpRoute.Traffic != nil && - httpRoute.Traffic.Timeout != nil && - httpRoute.Traffic.Timeout.HTTP != nil && - httpRoute.Traffic.Timeout.HTTP.RequestTimeout != nil { - router.GetRoute().Timeout = durationpb.New(httpRoute.Traffic.Timeout.HTTP.RequestTimeout.Duration) + if router.GetRoute() != nil { + rt := getEffectiveRequestTimeout(httpRoute) + if rt != nil { + router.GetRoute().Timeout = durationpb.New(rt.Duration) + } } // Retries @@ -292,14 +292,26 @@ func buildXdsWeightedRouteAction(backendWeights *ir.BackendWeights, settings []* } } -func idleTimeout(httpRoute *ir.HTTPRoute) *durationpb.Duration { +func getEffectiveRequestTimeout(httpRoute *ir.HTTPRoute) *metav1.Duration { + // gateway-api timeout takes precedence + if httpRoute.Timeout != nil { + return httpRoute.Timeout + } + if httpRoute.Traffic != nil && httpRoute.Traffic.Timeout != nil && httpRoute.Traffic.Timeout.HTTP != nil && httpRoute.Traffic.Timeout.HTTP.RequestTimeout != nil { - rt := httpRoute.Traffic.Timeout.HTTP.RequestTimeout - timeout := time.Hour // Default to 1 hour + return httpRoute.Traffic.Timeout.HTTP.RequestTimeout + } + return nil +} + +func idleTimeout(httpRoute *ir.HTTPRoute) *durationpb.Duration { + rt := getEffectiveRequestTimeout(httpRoute) + timeout := time.Hour // Default to 1 hour + if rt != nil { // Ensure is not less than the request timeout if timeout < rt.Duration { timeout = rt.Duration diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml index 746d4922542..8c4130e8e89 100644 --- a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml @@ -48,3 +48,47 @@ http: - endpoints: - host: "1.2.3.4" port: 50002 + - name: "forth-route" + hostname: "*" + timeout: 10s + traffic: + timeout: + http: + requestTimeout: 5s + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50002 + - name: "fifth-route" + hostname: "*" + timeout: 10s + destination: + name: "fifth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50002 + - name: "sixth-route" + hostname: "*" + timeout: 0s + destination: + name: "sixth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50002 + - name: "seventh-route" + hostname: "*" + timeout: 0s + traffic: + timeout: + http: + requestTimeout: 5s + destination: + name: "seventh-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50002 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml index a89644e62d9..0322cbb616d 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml @@ -49,3 +49,71 @@ outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fifth-route-dest + lbPolicy: LEAST_REQUEST + name: fifth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: sixth-route-dest + lbPolicy: LEAST_REQUEST + name: sixth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: seventh-route-dest + lbPolicy: LEAST_REQUEST + name: seventh-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml index 42a346c4041..59669a901a0 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml @@ -34,3 +34,51 @@ loadBalancingWeight: 1 locality: region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 +- clusterName: fifth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fifth-route-dest/backend/0 +- clusterName: sixth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: sixth-route-dest/backend/0 +- clusterName: seventh-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50002 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: seventh-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml index 23d2bf4b701..1c335ad621e 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml @@ -36,3 +36,39 @@ timeout: 0s upgradeConfigs: - upgradeType: websocket + - match: + prefix: / + name: forth-route + route: + cluster: fourth-route-dest + idleTimeout: 3600s + timeout: 10s + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: fifth-route + route: + cluster: fifth-route-dest + idleTimeout: 3600s + timeout: 10s + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: sixth-route + route: + cluster: sixth-route-dest + idleTimeout: 0s + timeout: 0s + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: seventh-route + route: + cluster: seventh-route-dest + idleTimeout: 0s + timeout: 0s + upgradeConfigs: + - upgradeType: websocket