From f4c53f4899b543a6461e7bf666a9bfa3b476e6b4 Mon Sep 17 00:00:00 2001 From: Alex Volchok Date: Thu, 1 Aug 2024 19:02:52 +0200 Subject: [PATCH] feat: Support DNS resolution settings (#3974) * Support DNS resolution settings Signed-off-by: Alexander Volchok * updating Signed-off-by: Alexander Volchok * updating Signed-off-by: Alexander Volchok * regenerate Signed-off-by: Alexander Volchok * add a nil cluster settings check Signed-off-by: Alexander Volchok * updating Signed-off-by: Alexander Volchok * updating as per code review feedback Signed-off-by: Alexander Volchok * adding tcp / udp route translations Signed-off-by: Alexander Volchok * use processXdsCluster with udp,tcp and http route types Signed-off-by: Alexander Volchok * fixing lint Signed-off-by: Alexander Volchok * fix respect dnsTTL setting Signed-off-by: Alexander Volchok --------- Signed-off-by: Alexander Volchok --- api/v1alpha1/backendtrafficpolicy_types.go | 4 + api/v1alpha1/dns_types.go | 18 + api/v1alpha1/zz_generated.deepcopy.go | 30 ++ ....envoyproxy.io_backendtrafficpolicies.yaml | 15 + internal/gatewayapi/backendtrafficpolicy.go | 36 ++ ...endtrafficpolicy-with-dns-settings.in.yaml | 127 ++++++ ...ndtrafficpolicy-with-dns-settings.out.yaml | 427 ++++++++++++++++++ internal/ir/xds.go | 14 + internal/ir/zz_generated.deepcopy.go | 40 ++ internal/xds/translator/cluster.go | 93 ++++ internal/xds/translator/translator.go | 63 +-- site/content/en/latest/api/extension_types.md | 16 + site/content/zh/latest/api/extension_types.md | 16 + 13 files changed, 846 insertions(+), 53 deletions(-) create mode 100644 api/v1alpha1/dns_types.go create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.in.yaml create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index f484f44b409..a8c39e9dfcc 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -110,6 +110,10 @@ type BackendTrafficPolicySpec struct { // // +optional Connection *BackendConnection `json:"connection,omitempty"` + // DNS includes dns resolution settings. + // + // +optional + DNS *DNS `json:"dns,omitempty"` } // +kubebuilder:object:root=true diff --git a/api/v1alpha1/dns_types.go b/api/v1alpha1/dns_types.go new file mode 100644 index 00000000000..62108a934a9 --- /dev/null +++ b/api/v1alpha1/dns_types.go @@ -0,0 +1,18 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type DNS struct { + // DNSRefreshRate specifies the rate at which DNS records should be refreshed. + // Defaults to 30 seconds. + DNSRefreshRate *metav1.Duration `json:"dnsRefreshRate,omitempty"` + // RespectDNSTTL indicates whether the DNS Time-To-Live (TTL) should be respected. + // If the value is set to true, the DNS refresh rate will be set to the resource record’s TTL. + // Defaults to true. + RespectDNSTTL *bool `json:"respectDnsTtl,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 3ea39b669c6..e6c4da29aee 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -569,6 +569,11 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec) *out = new(BackendConnection) (*in).DeepCopyInto(*out) } + if in.DNS != nil { + in, out := &in.DNS, &out.DNS + *out = new(DNS) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicySpec. @@ -1097,6 +1102,31 @@ func (in *CustomTag) DeepCopy() *CustomTag { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DNS) DeepCopyInto(out *DNS) { + *out = *in + if in.DNSRefreshRate != nil { + in, out := &in.DNSRefreshRate, &out.DNSRefreshRate + *out = new(v1.Duration) + **out = **in + } + if in.RespectDNSTTL != nil { + in, out := &in.RespectDNSTTL, &out.RespectDNSTTL + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNS. +func (in *DNS) DeepCopy() *DNS { + if in == nil { + return nil + } + out := new(DNS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvironmentCustomTag) DeepCopyInto(out *EnvironmentCustomTag) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 20ffe833923..2ddf8c841cc 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -138,6 +138,21 @@ spec: rule: 'type(self) == string ? self.matches(r"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$") : type(self) == int' type: object + dns: + description: DNS includes dns resolution settings. + properties: + dnsRefreshRate: + description: |- + DNSRefreshRate specifies the rate at which DNS records should be refreshed. + Defaults to 30 seconds. + type: string + respectDnsTtl: + description: |- + RespectDNSTTL indicates whether the DNS Time-To-Live (TTL) should be respected. + If the value is set to true, the DNS refresh rate will be set to the resource record’s TTL. + Defaults to true. + type: boolean + type: object faultInjection: description: |- FaultInjection defines the fault injection policy to be applied. This configuration can be used to diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 26d4a2a9b7c..65d4404dfb9 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -295,6 +295,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen ka *ir.TCPKeepalive rt *ir.Retry bc *ir.BackendConnection + ds *ir.DNS err, errs error ) @@ -349,6 +350,10 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen } } + if policy.Spec.DNS != nil { + ds = t.translateDNS(policy) + } + // Early return if got any errors if errs != nil { return errs @@ -368,6 +373,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen r.TCPKeepalive = ka r.Timeout = to r.BackendConnection = bc + r.DNS = ds } } } @@ -380,6 +386,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen r.LoadBalancer = lb r.Timeout = to r.BackendConnection = bc + r.DNS = ds } } } @@ -400,6 +407,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen BackendConnection: bc, } + r.DNS = ds // Update the Host field in HealthCheck, now that we have access to the Route Hostname. r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname) @@ -432,6 +440,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back ct *ir.Timeout ka *ir.TCPKeepalive rt *ir.Retry + ds *ir.DNS err, errs error ) @@ -479,6 +488,10 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back } } + if policy.Spec.DNS != nil { + ds = t.translateDNS(policy) + } + // Early return if got any errors if errs != nil { return errs @@ -516,6 +529,10 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back if r.Timeout == nil { r.Timeout = ct } + + if r.DNS == nil { + r.DNS = ds + } } } @@ -540,6 +557,10 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back if route.Timeout == nil { route.Timeout = ct } + + if route.DNS == nil { + route.DNS = ds + } } for _, http := range x.HTTP { @@ -568,6 +589,10 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back Retry: rt, } + if r.DNS == nil { + r.DNS = ds + } + // Update the Host field in HealthCheck, now that we have access to the Route Hostname. r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname) @@ -847,6 +872,17 @@ func (t *Translator) buildConsistentHashLoadBalancer(policy *egv1a1.BackendTraff return consistentHash, nil } +func (t *Translator) translateDNS(policy *egv1a1.BackendTrafficPolicy) *ir.DNS { + ds := &ir.DNS{} + if policy.Spec.DNS.RespectDNSTTL != nil { + ds.RespectDNSTTL = policy.Spec.DNS.RespectDNSTTL + } + if policy.Spec.DNS.DNSRefreshRate != nil { + ds.DNSRefreshRate = policy.Spec.DNS.DNSRefreshRate + } + return ds +} + func (t *Translator) buildProxyProtocol(policy *egv1a1.BackendTrafficPolicy) *ir.ProxyProtocol { var pp *ir.ProxyProtocol switch policy.Spec.ProxyProtocol.Version { diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.in.yaml new file mode 100644 index 00000000000..f1d48c4d31d --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.in.yaml @@ -0,0 +1,127 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +- 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-2 + sectionName: http + rules: + - matches: + - path: + value: "/v2" + backendRefs: + - name: service-2 + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-3 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/v3" + backendRefs: + - name: service-3 + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + dns: + dnsRefreshRate: "1s" + respectDnsTtl: true +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + dns: + dnsRefreshRate: "5s" + respectDnsTtl: false +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-all-routes-in-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + dns: + dnsRefreshRate: "10s" + respectDnsTtl: true diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml new file mode 100644 index 00000000000..89d9902328f --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-dns-settings.out.yaml @@ -0,0 +1,427 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-1 + namespace: default + spec: + dns: + dnsRefreshRate: 1s + respectDnsTtl: true + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-2 + namespace: default + spec: + dns: + dnsRefreshRate: 5s + respectDnsTtl: false + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-2 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-all-routes-in-gateway-1 + namespace: envoy-gateway + spec: + dns: + dnsRefreshRate: 10s + respectDnsTtl: true + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + 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 + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + 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-2 + 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-2 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /v2 + 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-2 + namespace: envoy-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-3 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-3 + port: 8080 + matches: + - path: + value: /v3 + 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: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 + envoy-gateway/gateway-2: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-2/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-2 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-3/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + dns: + dnsRefreshRate: 10s + respectDnsTtl: true + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-3 + namespace: default + name: httproute/default/httproute-3/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /v3 + traffic: {} + envoy-gateway/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-2 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + dns: + dnsRefreshRate: 5s + respectDnsTtl: false + 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: /v2 + traffic: {} + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + dns: + dnsRefreshRate: 1s + respectDnsTtl: true + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: {} diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 3d300f1a539..f4f059a2af6 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -563,6 +563,17 @@ 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"` + // DNS is used to configure how DNS resolution is handled for the route + DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"` +} + +// DNS contains configuration options for DNS resolution. +// +k8s:deepcopy-gen=true +type DNS struct { + // DNSRefreshRate specifies the rate at which DNS records should be refreshed. + DNSRefreshRate *metav1.Duration `json:"dnsRefreshRate,omitempty"` + // RespectDNSTTL indicates whether the DNS Time-To-Live (TTL) should be respected. + RespectDNSTTL *bool `json:"respectDnsTtl,omitempty"` } // TrafficFeatures holds the information associated with the Backend Traffic Policy. @@ -1386,6 +1397,8 @@ type TCPRoute struct { ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty"` // settings of upstream connection BackendConnection *BackendConnection `json:"backendConnection,omitempty" yaml:"backendConnection,omitempty"` + // DNS is used to configure how DNS resolution is handled for the route + DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"` } // TLS holds information for configuring TLS on a listener @@ -1496,6 +1509,7 @@ type UDPRoute struct { Timeout *Timeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` // settings of upstream connection BackendConnection *BackendConnection `json:"backendConnection,omitempty" yaml:"backendConnection,omitempty"` + DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"` } // Validate the fields within the UDPListener structure diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 273eeb1c3ca..e0a7b45571a 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -562,6 +562,31 @@ func (in *CoreListenerDetails) DeepCopy() *CoreListenerDetails { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DNS) DeepCopyInto(out *DNS) { + *out = *in + if in.DNSRefreshRate != nil { + in, out := &in.DNSRefreshRate, &out.DNSRefreshRate + *out = new(v1.Duration) + **out = **in + } + if in.RespectDNSTTL != nil { + in, out := &in.RespectDNSTTL, &out.RespectDNSTTL + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNS. +func (in *DNS) DeepCopy() *DNS { + if in == nil { + return nil + } + out := new(DNS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DestinationEndpoint) DeepCopyInto(out *DestinationEndpoint) { *out = *in @@ -1318,6 +1343,11 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { *out = new(ResourceMetadata) (*in).DeepCopyInto(*out) } + if in.DNS != nil { + in, out := &in.DNS, &out.DNS + *out = new(DNS) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. @@ -2524,6 +2554,11 @@ func (in *TCPRoute) DeepCopyInto(out *TCPRoute) { *out = new(BackendConnection) (*in).DeepCopyInto(*out) } + if in.DNS != nil { + in, out := &in.DNS, &out.DNS + *out = new(DNS) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPRoute. @@ -2914,6 +2949,11 @@ func (in *UDPRoute) DeepCopyInto(out *UDPRoute) { *out = new(BackendConnection) (*in).DeepCopyInto(*out) } + if in.DNS != nil { + in, out := &in.DNS, &out.DNS + *out = new(DNS) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UDPRoute. diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go index e646f410944..9a7819a597f 100644 --- a/internal/xds/translator/cluster.go +++ b/internal/xds/translator/cluster.go @@ -52,6 +52,7 @@ type xdsClusterArgs struct { tcpkeepalive *ir.TCPKeepalive metrics *ir.Metrics backendConnection *ir.BackendConnection + dns *ir.DNS useClientProtocol bool } @@ -144,6 +145,16 @@ func buildXdsCluster(args *xdsClusterArgs) *clusterv3.Cluster { cluster.ClusterDiscoveryType = &clusterv3.Cluster_Type{Type: clusterv3.Cluster_STRICT_DNS} cluster.DnsRefreshRate = durationpb.New(30 * time.Second) cluster.RespectDnsTtl = true + if args.dns != nil { + if args.dns.DNSRefreshRate != nil { + if args.dns.DNSRefreshRate.Duration > 0 { + cluster.DnsRefreshRate = durationpb.New(args.dns.DNSRefreshRate.Duration) + } + } + if args.dns.RespectDNSTTL != nil { + cluster.RespectDnsTtl = ptr.Deref(args.dns.RespectDNSTTL, true) + } + } } // build common, HTTP/1 and HTTP/2 protocol options for cluster @@ -649,3 +660,85 @@ func buildBackandConnectionBufferLimitBytes(bc *ir.BackendConnection) *wrappers. return wrapperspb.UInt32(tcpClusterPerConnectionBufferLimitBytes) } + +type ExtraArgs struct { + metrics *ir.Metrics + http1Settings *ir.HTTP1Settings +} + +type clusterArgs interface { + asClusterArgs(extras *ExtraArgs) *xdsClusterArgs +} + +type UDPRouteTranslator struct { + *ir.UDPRoute +} + +func (route *UDPRouteTranslator) asClusterArgs(extra *ExtraArgs) *xdsClusterArgs { + return &xdsClusterArgs{ + name: route.Destination.Name, + settings: route.Destination.Settings, + loadBalancer: route.LoadBalancer, + timeout: route.Timeout, + tSocket: nil, + endpointType: buildEndpointType(route.Destination.Settings), + metrics: extra.metrics, + backendConnection: route.BackendConnection, + dns: route.DNS, + } +} + +type TCPRouteTranslator struct { + *ir.TCPRoute +} + +func (route *TCPRouteTranslator) asClusterArgs(extra *ExtraArgs) *xdsClusterArgs { + return &xdsClusterArgs{ + name: route.Destination.Name, + settings: route.Destination.Settings, + loadBalancer: route.LoadBalancer, + proxyProtocol: route.ProxyProtocol, + circuitBreaker: route.CircuitBreaker, + tcpkeepalive: route.TCPKeepalive, + healthCheck: route.HealthCheck, + timeout: route.Timeout, + endpointType: buildEndpointType(route.Destination.Settings), + metrics: extra.metrics, + backendConnection: route.BackendConnection, + dns: route.DNS, + } +} + +type HTTPRouteTranslator struct { + *ir.HTTPRoute +} + +func (httpRoute *HTTPRouteTranslator) asClusterArgs(extra *ExtraArgs) *xdsClusterArgs { + clusterArgs := &xdsClusterArgs{ + name: httpRoute.Destination.Name, + settings: httpRoute.Destination.Settings, + tSocket: nil, + endpointType: buildEndpointType(httpRoute.Destination.Settings), + metrics: extra.metrics, + http1Settings: extra.http1Settings, + useClientProtocol: ptr.Deref(httpRoute.UseClientProtocol, false), + } + + // Populate traffic features. + bt := httpRoute.Traffic + if bt != nil { + clusterArgs.loadBalancer = bt.LoadBalancer + clusterArgs.proxyProtocol = bt.ProxyProtocol + clusterArgs.circuitBreaker = bt.CircuitBreaker + clusterArgs.healthCheck = bt.HealthCheck + clusterArgs.timeout = bt.Timeout + clusterArgs.tcpkeepalive = bt.TCPKeepalive + clusterArgs.backendConnection = bt.BackendConnection + } + + if httpRoute.DNS != nil { + clusterArgs.dns = httpRoute.DNS + } + + return clusterArgs +} diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index a58903e88ad..861e36ca8f1 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/wrapperspb" - "k8s.io/utils/ptr" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" extensionTypes "github.com/envoyproxy/gateway/internal/extension/types" @@ -453,9 +452,11 @@ func (t *Translator) addRouteToRouteConfig( if httpRoute.Destination != nil { if err = processXdsCluster( tCtx, - httpRoute, - httpListener.HTTP1, - metrics, + &HTTPRouteTranslator{httpRoute}, + &ExtraArgs{ + metrics: metrics, + http1Settings: httpListener.HTTP1, + }, ); err != nil { errs = errors.Join(errs, err) } @@ -573,19 +574,7 @@ func (t *Translator) processTCPListenerXdsTranslation( patchProxyProtocolFilter(xdsListener, tcpListener.EnableProxyProtocol) for _, route := range tcpListener.Routes { - if err := addXdsCluster(tCtx, &xdsClusterArgs{ - name: route.Destination.Name, - settings: route.Destination.Settings, - loadBalancer: route.LoadBalancer, - proxyProtocol: route.ProxyProtocol, - circuitBreaker: route.CircuitBreaker, - tcpkeepalive: route.TCPKeepalive, - healthCheck: route.HealthCheck, - timeout: route.Timeout, - endpointType: buildEndpointType(route.Destination.Settings), - metrics: metrics, - backendConnection: route.BackendConnection, - }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + if err := processXdsCluster(tCtx, &TCPRouteTranslator{route}, &ExtraArgs{metrics: metrics}); err != nil && !errors.Is(err, ErrXdsClusterExists) { errs = errors.Join(errs, err) } if route.TLS != nil && route.TLS.Terminate != nil { @@ -647,16 +636,7 @@ func processUDPListenerXdsTranslation( } // 1:1 between IR UDPRoute and xDS Cluster - if err := addXdsCluster(tCtx, &xdsClusterArgs{ - name: route.Destination.Name, - settings: route.Destination.Settings, - loadBalancer: route.LoadBalancer, - timeout: route.Timeout, - tSocket: nil, - endpointType: buildEndpointType(route.Destination.Settings), - metrics: metrics, - backendConnection: route.BackendConnection, - }); err != nil && !errors.Is(err, ErrXdsClusterExists) { + if err := processXdsCluster(tCtx, &UDPRouteTranslator{route}, &ExtraArgs{metrics: metrics}); err != nil && !errors.Is(err, ErrXdsClusterExists) { errs = errors.Join(errs, err) } } @@ -748,34 +728,11 @@ func findXdsEndpoint(tCtx *types.ResourceVersionTable, name string) *endpointv3. return nil } -// processXdsCluster processes a xds cluster by its endpoint address type. -func processXdsCluster(tCtx *types.ResourceVersionTable, httpRoute *ir.HTTPRoute, http1Settings *ir.HTTP1Settings, metrics *ir.Metrics) error { - clusterArgs := &xdsClusterArgs{ - name: httpRoute.Destination.Name, - settings: httpRoute.Destination.Settings, - tSocket: nil, - endpointType: buildEndpointType(httpRoute.Destination.Settings), - http1Settings: http1Settings, - metrics: metrics, - useClientProtocol: ptr.Deref(httpRoute.UseClientProtocol, false), - } - - // Populate traffic features. - bt := httpRoute.Traffic - if bt != nil { - clusterArgs.loadBalancer = bt.LoadBalancer - clusterArgs.proxyProtocol = bt.ProxyProtocol - clusterArgs.circuitBreaker = bt.CircuitBreaker - clusterArgs.healthCheck = bt.HealthCheck - clusterArgs.timeout = bt.Timeout - clusterArgs.tcpkeepalive = bt.TCPKeepalive - clusterArgs.backendConnection = bt.BackendConnection - } - - if err := addXdsCluster(tCtx, clusterArgs); err != nil && !errors.Is(err, ErrXdsClusterExists) { +// processXdsCluster processes xds cluster with args per route. +func processXdsCluster(tCtx *types.ResourceVersionTable, route clusterArgs, extras *ExtraArgs) error { + if err := addXdsCluster(tCtx, route.asClusterArgs(extras)); err != nil && !errors.Is(err, ErrXdsClusterExists) { return err } - return nil } diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 9a2cd961a63..b0e85833110 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -460,6 +460,7 @@ _Appears in:_ | `useClientProtocol` | _boolean_ | false | UseClientProtocol configures Envoy to prefer sending requests to backends using
the same HTTP protocol that the incoming request used. Defaults to false, which means
that Envoy will use the protocol indicated by the attached BackendRef. | | `timeout` | _[Timeout](#timeout)_ | false | Timeout settings for the backend connections. | | `connection` | _[BackendConnection](#backendconnection)_ | false | Connection includes backend connection settings. | +| `dns` | _[DNS](#dns)_ | false | DNS includes dns resolution settings. | #### BasicAuth @@ -845,6 +846,21 @@ _Appears in:_ | `RequestHeader` | CustomTagTypeRequestHeader adds value from request header to each span.
| +#### DNS + + + + + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `dnsRefreshRate` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | true | DNSRefreshRate specifies the rate at which DNS records should be refreshed.
Defaults to 30 seconds. | +| `respectDnsTtl` | _boolean_ | true | RespectDNSTTL indicates whether the DNS Time-To-Live (TTL) should be respected.
If the value is set to true, the DNS refresh rate will be set to the resource record’s TTL.
Defaults to true. | + + #### EnvironmentCustomTag diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 9a2cd961a63..b0e85833110 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -460,6 +460,7 @@ _Appears in:_ | `useClientProtocol` | _boolean_ | false | UseClientProtocol configures Envoy to prefer sending requests to backends using
the same HTTP protocol that the incoming request used. Defaults to false, which means
that Envoy will use the protocol indicated by the attached BackendRef. | | `timeout` | _[Timeout](#timeout)_ | false | Timeout settings for the backend connections. | | `connection` | _[BackendConnection](#backendconnection)_ | false | Connection includes backend connection settings. | +| `dns` | _[DNS](#dns)_ | false | DNS includes dns resolution settings. | #### BasicAuth @@ -845,6 +846,21 @@ _Appears in:_ | `RequestHeader` | CustomTagTypeRequestHeader adds value from request header to each span.
| +#### DNS + + + + + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `dnsRefreshRate` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | true | DNSRefreshRate specifies the rate at which DNS records should be refreshed.
Defaults to 30 seconds. | +| `respectDnsTtl` | _boolean_ | true | RespectDNSTTL indicates whether the DNS Time-To-Live (TTL) should be respected.
If the value is set to true, the DNS refresh rate will be set to the resource record’s TTL.
Defaults to true. | + + #### EnvironmentCustomTag