diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go index 1ba5e7e4473..4ab2048876f 100644 --- a/internal/gatewayapi/contexts.go +++ b/internal/gatewayapi/contexts.go @@ -187,6 +187,7 @@ func GetRouteType(route RouteContext) gwapiv1.Kind { // TODO: [v1alpha2-gwapiv1] This should not be required once all Route // objects being implemented are of type gwapiv1. + // GetHostnames returns the hosts targeted by the Route object. func GetHostnames(route RouteContext) []string { rv := reflect.ValueOf(route).Elem() diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 0eb383c90cd..3ffdeb2b9c9 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -6,9 +6,12 @@ package gatewayapi import ( + "errors" "fmt" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" @@ -16,7 +19,6 @@ import ( "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils" "github.com/envoyproxy/gateway/internal/utils/naming" - "github.com/envoyproxy/gateway/internal/utils/net" ) var _ ListenersTranslator = (*Translator)(nil) @@ -44,7 +46,7 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap if resources.EnvoyProxy != nil { infraIR[irKey].Proxy.Config = resources.EnvoyProxy } - t.processProxyObservability(gateway.Gateway, xdsIR[irKey], infraIR[irKey].Proxy.Config) + t.processProxyObservability(gateway, xdsIR[irKey], infraIR[irKey].Proxy.Config, resources) for _, listener := range gateway.listeners { // Process protocol & supported kinds @@ -143,10 +145,29 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR XdsIRMap } } -func (t *Translator) processProxyObservability(gw *gwapiv1.Gateway, xdsIR *ir.Xds, envoyProxy *egv1a1.EnvoyProxy) { - xdsIR.AccessLog = processAccessLog(envoyProxy) - xdsIR.Tracing = processTracing(gw, envoyProxy, t.MergeGateways) - xdsIR.Metrics = processMetrics(envoyProxy) +func (t *Translator) processProxyObservability(gwCtx *GatewayContext, xdsIR *ir.Xds, envoyProxy *egv1a1.EnvoyProxy, resources *Resources) { + var err error + + xdsIR.AccessLog, err = t.processAccessLog(envoyProxy, resources) + if err != nil { + status.UpdateGatewayListenersNotValidCondition(gwCtx.Gateway, gwapiv1.GatewayReasonInvalid, metav1.ConditionFalse, + fmt.Sprintf("Invalid access log backendRefs: %v", err)) + return + } + + xdsIR.Tracing, err = t.processTracing(gwCtx.Gateway, envoyProxy, t.MergeGateways, resources) + if err != nil { + status.UpdateGatewayListenersNotValidCondition(gwCtx.Gateway, gwapiv1.GatewayReasonInvalid, metav1.ConditionFalse, + fmt.Sprintf("Invalid tracing backendRefs: %v", err)) + return + } + + xdsIR.Metrics, err = t.processMetrics(envoyProxy, resources) + if err != nil { + status.UpdateGatewayListenersNotValidCondition(gwCtx.Gateway, gwapiv1.GatewayReasonInvalid, metav1.ConditionFalse, + fmt.Sprintf("Invalid metrics backendRefs: %v", err)) + return + } } func (t *Translator) processInfraIRListener(listener *ListenerContext, infraIR InfraIRMap, irKey string, servicePort *protocolPort, containerPort int32) { @@ -179,7 +200,7 @@ func (t *Translator) processInfraIRListener(listener *ListenerContext, infraIR I infraIR[irKey].Proxy.Listeners = append(infraIR[irKey].Proxy.Listeners, proxyListener) } -func processAccessLog(envoyproxy *egv1a1.EnvoyProxy) *ir.AccessLog { +func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources *Resources) (*ir.AccessLog, error) { if envoyproxy == nil || envoyproxy.Spec.Telemetry == nil || envoyproxy.Spec.Telemetry.AccessLog == nil || @@ -191,16 +212,16 @@ func processAccessLog(envoyproxy *egv1a1.EnvoyProxy) *ir.AccessLog { Path: "/dev/stdout", }, }, - } + }, nil } if envoyproxy.Spec.Telemetry.AccessLog.Disable { - return nil + return nil, nil } irAccessLog := &ir.AccessLog{} // translate the access log configuration to the IR - for _, accessLog := range envoyproxy.Spec.Telemetry.AccessLog.Settings { + for idx, accessLog := range envoyproxy.Spec.Telemetry.AccessLog.Settings { for _, sink := range accessLog.Sinks { switch sink.Type { case egv1a1.ProxyAccessLogSinkTypeFile: @@ -234,16 +255,28 @@ func processAccessLog(envoyproxy *egv1a1.EnvoyProxy) *ir.AccessLog { // TODO: remove support for Host/Port in v1.2 al := &ir.OpenTelemetryAccessLog{ - Port: uint32(sink.OpenTelemetry.Port), Resources: sink.OpenTelemetry.Resources, } - if sink.OpenTelemetry.Host != nil { - al.Host = *sink.OpenTelemetry.Host + // TODO: how to get authority from the backendRefs? + ds, err := t.processBackendRefs(sink.OpenTelemetry.BackendRefs, envoyproxy.Namespace, resources) + if err != nil { + return nil, err + } + al.Destination = ir.RouteDestination{ + Name: fmt.Sprintf("accesslog-%d", idx), // TODO: rename this, so that we can share backend with tracing? + Settings: ds, } - if len(sink.OpenTelemetry.BackendRefs) > 0 { - al.Host, al.Port = net.BackendHostAndPort(sink.OpenTelemetry.BackendRefs[0].BackendObjectReference, envoyproxy.Namespace) + if len(ds) == 0 { + // fallback to host and port + var host string + var port uint32 + if sink.OpenTelemetry.Host != nil { + host, port = *sink.OpenTelemetry.Host, uint32(sink.OpenTelemetry.Port) + } + al.Destination.Settings = destinationSettingFromHostAndPort(host, port) + al.Authority = host } switch accessLog.Format.Type { @@ -258,25 +291,35 @@ func processAccessLog(envoyproxy *egv1a1.EnvoyProxy) *ir.AccessLog { } } - return irAccessLog + return irAccessLog, nil } -func processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.EnvoyProxy, mergeGateways bool) *ir.Tracing { +func (t *Translator) processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.EnvoyProxy, mergeGateways bool, resources *Resources) (*ir.Tracing, error) { if envoyproxy == nil || envoyproxy.Spec.Telemetry == nil || envoyproxy.Spec.Telemetry.Tracing == nil { - return nil + return nil, nil } tracing := envoyproxy.Spec.Telemetry.Tracing - // TODO: remove support for Host/Port in v1.2 - var host string - var port uint32 - if tracing.Provider.Host != nil { - host, port = *tracing.Provider.Host, uint32(tracing.Provider.Port) + // TODO: how to get authority from the backendRefs? + ds, err := t.processBackendRefs(tracing.Provider.BackendRefs, envoyproxy.Namespace, resources) + if err != nil { + return nil, err } - if len(tracing.Provider.BackendRefs) > 0 { - host, port = net.BackendHostAndPort(tracing.Provider.BackendRefs[0].BackendObjectReference, gw.Namespace) + + var authority string + + // fallback to host and port + // TODO: remove support for Host/Port in v1.2 + if len(ds) == 0 { + var host string + var port uint32 + if tracing.Provider.Host != nil { + host, port = *tracing.Provider.Host, uint32(tracing.Provider.Port) + } + ds = destinationSettingFromHostAndPort(host, port) + authority = host } samplingRate := 100.0 @@ -290,22 +333,68 @@ func processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.EnvoyProxy, mergeGat } return &ir.Tracing{ + Authority: authority, ServiceName: serviceName, - Host: host, - Port: port, SamplingRate: samplingRate, CustomTags: tracing.CustomTags, - } + Destination: ir.RouteDestination{ + Name: "tracing", // TODO: rename this, so that we can share backend with accesslog? + Settings: ds, + }, + }, nil } -func processMetrics(envoyproxy *egv1a1.EnvoyProxy) *ir.Metrics { +func (t *Translator) processMetrics(envoyproxy *egv1a1.EnvoyProxy, resources *Resources) (*ir.Metrics, error) { if envoyproxy == nil || envoyproxy.Spec.Telemetry == nil || envoyproxy.Spec.Telemetry.Metrics == nil { - return nil + return nil, nil } + + for _, sink := range envoyproxy.Spec.Telemetry.Metrics.Sinks { + if sink.OpenTelemetry == nil { + continue + } + + _, err := t.processBackendRefs(sink.OpenTelemetry.BackendRefs, envoyproxy.Namespace, resources) + if err != nil { + return nil, err + } + } + return &ir.Metrics{ EnableVirtualHostStats: envoyproxy.Spec.Telemetry.Metrics.EnableVirtualHostStats, EnablePerEndpointStats: envoyproxy.Spec.Telemetry.Metrics.EnablePerEndpointStats, + }, nil +} + +func (t *Translator) processBackendRefs(backendRefs []egv1a1.BackendRef, namespace string, resources *Resources) ([]*ir.DestinationSetting, error) { + result := make([]*ir.DestinationSetting, 0, len(backendRefs)) + for _, ref := range backendRefs { + ns := NamespaceDerefOr(ref.Namespace, namespace) + kind := KindDerefOr(ref.Kind, KindService) + if kind != KindService { + return nil, errors.New("only service kind is supported for backendRefs") + } + if err := validateBackendService(ref.BackendObjectReference, resources, ns, corev1.ProtocolTCP); err != nil { + return nil, err + } + + ds := t.processServiceDestinationSetting(ref.BackendObjectReference, ns, ir.GRPC, resources) + result = append(result, ds) + } + if len(result) == 0 { + return nil, nil + } + return result, nil +} + +func destinationSettingFromHostAndPort(host string, port uint32) []*ir.DestinationSetting { + return []*ir.DestinationSetting{ + { + Weight: ptr.To[uint32](1), + Protocol: ir.GRPC, + Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(host, port)}, + }, } } diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go deleted file mode 100644 index 1da6638f9a8..00000000000 --- a/internal/gatewayapi/listener_test.go +++ /dev/null @@ -1,229 +0,0 @@ -// 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 gatewayapi - -import ( - "testing" - - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" - gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - egcfgv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" - "github.com/envoyproxy/gateway/internal/ir" -) - -func TestProcessTracing(t *testing.T) { - cases := []struct { - gw gwapiv1.Gateway - proxy *egcfgv1a1.EnvoyProxy - mergedgw bool - - expected *ir.Tracing - }{ - {}, - { - gw: gwapiv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-gw", - Namespace: "fake-ns", - }, - }, - proxy: &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Tracing: &egcfgv1a1.ProxyTracing{}, - }, - }, - }, - expected: &ir.Tracing{ - ServiceName: "fake-gw.fake-ns", - SamplingRate: 100.0, - }, - }, - { - gw: gwapiv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-gw", - Namespace: "fake-ns", - }, - Spec: gwapiv1.GatewaySpec{ - GatewayClassName: "fake-gateway-class", - }, - }, - proxy: &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Tracing: &egcfgv1a1.ProxyTracing{}, - }, - }, - }, - mergedgw: true, - expected: &ir.Tracing{ - ServiceName: "fake-gateway-class", - SamplingRate: 100.0, - }, - }, - { - gw: gwapiv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-gw", - Namespace: "fake-ns", - }, - }, - proxy: &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Tracing: &egcfgv1a1.ProxyTracing{ - Provider: egcfgv1a1.TracingProvider{ - Host: ptr.To("fake-host"), - Port: 4317, - }, - }, - }, - }, - }, - expected: &ir.Tracing{ - ServiceName: "fake-gw.fake-ns", - SamplingRate: 100.0, - Host: "fake-host", - Port: 4317, - }, - }, - { - gw: gwapiv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-gw", - Namespace: "fake-ns", - }, - }, - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-eproxy", - Namespace: "fake-ns", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Tracing: &egcfgv1a1.ProxyTracing{ - Provider: egcfgv1a1.TracingProvider{ - Host: ptr.To("fake-host"), - Port: 4317, - BackendRefs: []egcfgv1a1.BackendRef{ - { - BackendObjectReference: gwapiv1.BackendObjectReference{ - Name: "fake-name", - Port: PortNumPtr(4317), - }, - }, - }, - }, - }, - }, - }, - }, - expected: &ir.Tracing{ - ServiceName: "fake-gw.fake-ns", - SamplingRate: 100.0, - Host: "fake-name.fake-ns.svc", - Port: 4317, - }, - }, - { - gw: gwapiv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-gw", - Namespace: "fake-ns", - }, - }, - proxy: &egcfgv1a1.EnvoyProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fake-eproxy", - Namespace: "fake-ns", - }, - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Tracing: &egcfgv1a1.ProxyTracing{ - Provider: egcfgv1a1.TracingProvider{ - BackendRefs: []egcfgv1a1.BackendRef{ - { - BackendObjectReference: gwapiv1.BackendObjectReference{ - Name: "fake-name", - Port: PortNumPtr(4317), - }, - }, - }, - }, - }, - }, - }, - }, - expected: &ir.Tracing{ - ServiceName: "fake-gw.fake-ns", - SamplingRate: 100.0, - Host: "fake-name.fake-ns.svc", - Port: 4317, - }, - }, - } - - for _, c := range cases { - c := c - t.Run("", func(t *testing.T) { - got := processTracing(&c.gw, c.proxy, c.mergedgw) - assert.Equal(t, c.expected, got) - }) - } -} - -func TestProcessMetrics(t *testing.T) { - cases := []struct { - name string - proxy *egcfgv1a1.EnvoyProxy - - expected *ir.Metrics - }{ - { - name: "nil proxy config", - }, - { - name: "virtual host stats enabled", - proxy: &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Metrics: &egcfgv1a1.ProxyMetrics{ - EnableVirtualHostStats: true, - }, - }, - }, - }, - expected: &ir.Metrics{ - EnableVirtualHostStats: true, - }, - }, - { - name: "peer endpoint stats enabled", - proxy: &egcfgv1a1.EnvoyProxy{ - Spec: egcfgv1a1.EnvoyProxySpec{ - Telemetry: &egcfgv1a1.ProxyTelemetry{ - Metrics: &egcfgv1a1.ProxyMetrics{ - EnablePerEndpointStats: true, - }, - }, - }, - }, - expected: &ir.Metrics{ - EnablePerEndpointStats: true, - }, - }, - } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - got := processMetrics(c.proxy) - assert.Equal(t, c.expected, got) - }) - } -} diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 18056c29412..b8562e4e804 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -1148,7 +1148,6 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext, addrType *ir.DestinationAddressType ) protocol := inspectAppProtocolByRouteKind(routeType) - var backendTLS *ir.TLSUpstreamConfig switch KindDerefOr(backendRef.Kind, KindService) { case KindServiceImport: serviceImport := resources.GetServiceImport(backendNamespace, string(backendRef.Name)) @@ -1172,35 +1171,16 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext, endpoints = append(endpoints, ep) } } - case KindService: - service := resources.GetService(backendNamespace, string(backendRef.Name)) - var servicePort corev1.ServicePort - for _, port := range service.Spec.Ports { - if port.Port == int32(*backendRef.Port) { - servicePort = port - break - } - } - // support HTTPRouteBackendProtocolH2C - if servicePort.AppProtocol != nil && - *servicePort.AppProtocol == "kubernetes.io/h2c" { - protocol = ir.HTTP2 + ds = &ir.DestinationSetting{ + Weight: &weight, + Protocol: protocol, + Endpoints: endpoints, + AddressType: addrType, } - - // Route to endpoints by default - if !t.EndpointRoutingDisabled { - endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, KindService)) - endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, servicePort.Protocol) - } else { - // Fall back to Service ClusterIP routing - ep := ir.NewDestEndpoint( - service.Spec.ClusterIP, - uint32(*backendRef.Port)) - endpoints = append(endpoints, ep) - } - - backendTLS = t.processBackendTLSPolicy( + case KindService: + ds = t.processServiceDestinationSetting(backendRef.BackendObjectReference, backendNamespace, protocol, resources) + ds.TLS = t.processBackendTLSPolicy( backendRef.BackendObjectReference, backendNamespace, gwapiv1a2.ParentReference{ @@ -1212,6 +1192,7 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext, Port: parentRef.Port, }, resources) + ds.Filters = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) } // TODO: support mixed endpointslice address type for the same backendRef @@ -1225,17 +1206,51 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext, gwapiv1a2.RouteReasonResolvedRefs, "Mixed endpointslice address type for the same backendRef is not supported") } - destinationFilters := t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) - ds = &ir.DestinationSetting{ - Weight: &weight, + + ds.Weight = &weight + return ds +} + +func (t *Translator) processServiceDestinationSetting(backendRef gwapiv1.BackendObjectReference, backendNamespace string, + protocol ir.AppProtocol, resources *Resources, +) *ir.DestinationSetting { + var ( + endpoints []*ir.DestinationEndpoint + addrType *ir.DestinationAddressType + ) + + service := resources.GetService(backendNamespace, string(backendRef.Name)) + var servicePort corev1.ServicePort + for _, port := range service.Spec.Ports { + if port.Port == int32(*backendRef.Port) { + servicePort = port + break + } + } + + // support HTTPRouteBackendProtocolH2C + if servicePort.AppProtocol != nil && + *servicePort.AppProtocol == "kubernetes.io/h2c" { + protocol = ir.HTTP2 + } + + // Route to endpoints by default + if !t.EndpointRoutingDisabled { + endpointSlices := resources.GetEndpointSlicesForBackend(backendNamespace, string(backendRef.Name), KindDerefOr(backendRef.Kind, KindService)) + endpoints, addrType = getIREndpointsFromEndpointSlices(endpointSlices, servicePort.Name, servicePort.Protocol) + } else { + // Fall back to Service ClusterIP routing + ep := ir.NewDestEndpoint( + service.Spec.ClusterIP, + uint32(*backendRef.Port)) + endpoints = append(endpoints, ep) + } + + return &ir.DestinationSetting{ Protocol: protocol, Endpoints: endpoints, AddressType: addrType, - TLS: backendTLS, - Filters: destinationFilters, } - - return ds } func getBackendFilters(routeType gwapiv1.Kind, backendRefContext BackendRefContext) (backendFilters any) { diff --git a/internal/gatewayapi/status/gateway.go b/internal/gatewayapi/status/gateway.go index 5744824bbe4..aeeabf88d3b 100644 --- a/internal/gatewayapi/status/gateway.go +++ b/internal/gatewayapi/status/gateway.go @@ -16,6 +16,12 @@ import ( gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) +func UpdateGatewayListenersNotValidCondition(gw *gwapiv1.Gateway, reason gwapiv1.GatewayConditionReason, status metav1.ConditionStatus, msg string) *gwapiv1.Gateway { + cond := newCondition(string(gwapiv1.GatewayReasonListenersNotValid), status, string(reason), msg, time.Now(), gw.Generation) + gw.Status.Conditions = MergeConditions(gw.Status.Conditions, cond) + return gw +} + // UpdateGatewayStatusAcceptedCondition updates the status condition for the provided Gateway based on the accepted state. func UpdateGatewayStatusAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) *gwapiv1.Gateway { gw.Status.Conditions = MergeConditions(gw.Status.Conditions, computeGatewayAcceptedCondition(gw, accepted)) diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-backend-invalid.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-backend-invalid.in.yaml new file mode 100644 index 00000000000..0c045e3165f --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-backend-invalid.in.yaml @@ -0,0 +1,89 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - format: + type: Text + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + sinks: + - type: File + file: + path: /dev/stdout + - type: OpenTelemetry + openTelemetry: + backendRefs: + - name: service-not-found + namespace: monitoring + port: 4317 + resources: + k8s.cluster.name: "cluster-1" + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-dev" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +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: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-backend-invalid.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-backend-invalid.out.yaml new file mode 100755 index 00000000000..4c81757777b --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-backend-invalid.out.yaml @@ -0,0 +1,153 @@ +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: Same + name: http + port: 80 + protocol: HTTP + status: + conditions: + - lastTransitionTime: null + message: 'Invalid access log backendRefs: Service monitoring/service-not-found + not found' + reason: Invalid + status: "False" + type: ListenersNotValid + listeners: + - attachedRoutes: 0 + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-dev + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - format: + text: | + [%START_TIME%] "%REQ(:METHOD)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n + type: Text + sinks: + - file: + path: /dev/stdout + type: File + - openTelemetry: + backendRefs: + - name: service-not-found + namespace: monitoring + port: 4317 + resources: + k8s.cluster.name: cluster-1 + type: OpenTelemetry + status: {} + 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 +xdsIR: + envoy-gateway/gateway-1: + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-backend.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-backend.out.yaml index 265b1a19887..d744be89bb6 100755 --- a/internal/gatewayapi/testdata/envoyproxy-accesslog-backend.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-backend.out.yaml @@ -136,8 +136,14 @@ xdsIR: envoy-gateway/gateway-1: accessLog: openTelemetry: - - host: otel-collector.monitoring.svc - port: 4317 + - destination: + name: accesslog-0 + settings: + - addressType: IP + endpoints: + - host: 8.7.6.5 + port: 4317 + protocol: GRPC resources: k8s.cluster.name: cluster-1 text: | diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml index b219623c2ed..1c4c5b68347 100644 --- a/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog.out.yaml @@ -134,8 +134,15 @@ xdsIR: envoy-gateway/gateway-1: accessLog: openTelemetry: - - host: otel-collector.monitoring.svc.cluster.local - port: 4317 + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog-0 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 resources: k8s.cluster.name: cluster-1 text: | diff --git a/internal/gatewayapi/testdata/envoyproxy-metric-backend-invalid.in.yaml b/internal/gatewayapi/testdata/envoyproxy-metric-backend-invalid.in.yaml new file mode 100644 index 00000000000..0c60778b2aa --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-metric-backend-invalid.in.yaml @@ -0,0 +1,79 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + backendRefs: + - name: service-not-found + namespace: monitoring + port: 4317 + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-dev" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +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: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-metric-backend-invalid.out.yaml b/internal/gatewayapi/testdata/envoyproxy-metric-backend-invalid.out.yaml new file mode 100755 index 00000000000..b46517a221b --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-metric-backend-invalid.out.yaml @@ -0,0 +1,146 @@ +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: Same + name: http + port: 80 + protocol: HTTP + status: + conditions: + - lastTransitionTime: null + message: 'Invalid metrics backendRefs: Service monitoring/service-not-found + not found' + reason: Invalid + status: "False" + type: ListenersNotValid + listeners: + - attachedRoutes: 0 + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-dev + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + metrics: + sinks: + - openTelemetry: + backendRefs: + - name: service-not-found + namespace: monitoring + port: 4317 + type: OpenTelemetry + status: {} + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/internal/gatewayapi/testdata/envoyproxy-metric-backend.in.yaml b/internal/gatewayapi/testdata/envoyproxy-metric-backend.in.yaml new file mode 100644 index 00000000000..3a5228cc271 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-metric-backend.in.yaml @@ -0,0 +1,79 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + metrics: + sinks: + - type: OpenTelemetry + openTelemetry: + backendRefs: + - name: otel-collector + namespace: monitoring + port: 4317 + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-dev" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +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: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-metric-backend.out.yaml b/internal/gatewayapi/testdata/envoyproxy-metric-backend.out.yaml new file mode 100755 index 00000000000..5a42626a54c --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-metric-backend.out.yaml @@ -0,0 +1,142 @@ +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: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-dev + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + metrics: + sinks: + - openTelemetry: + backendRefs: + - name: otel-collector + namespace: monitoring + port: 4317 + type: OpenTelemetry + status: {} + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + metrics: + enablePerEndpointStats: false + enableVirtualHostStats: false diff --git a/internal/gatewayapi/testdata/envoyproxy-tracing-backend-invalid.in.yaml b/internal/gatewayapi/testdata/envoyproxy-tracing-backend-invalid.in.yaml new file mode 100644 index 00000000000..2feceaf788e --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-tracing-backend-invalid.in.yaml @@ -0,0 +1,79 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + tracing: + samplingRate: 100 + provider: + backendRefs: + - name: service-not-found + namespace: monitoring + port: 4317 + type: OpenTelemetry + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-dev" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +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: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-tracing-backend-invalid.out.yaml b/internal/gatewayapi/testdata/envoyproxy-tracing-backend-invalid.out.yaml new file mode 100755 index 00000000000..32bf193bf5a --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-tracing-backend-invalid.out.yaml @@ -0,0 +1,146 @@ +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: Same + name: http + port: 80 + protocol: HTTP + status: + conditions: + - lastTransitionTime: null + message: 'Invalid tracing backendRefs: Service monitoring/service-not-found + not found' + reason: Invalid + status: "False" + type: ListenersNotValid + listeners: + - attachedRoutes: 0 + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-dev + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + tracing: + provider: + backendRefs: + - name: service-not-found + namespace: monitoring + port: 4317 + type: OpenTelemetry + samplingRate: 100 + status: {} + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 diff --git a/internal/gatewayapi/testdata/envoyproxy-tracing-backend.in.yaml b/internal/gatewayapi/testdata/envoyproxy-tracing-backend.in.yaml new file mode 100644 index 00000000000..42e91b06cf7 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-tracing-backend.in.yaml @@ -0,0 +1,79 @@ +envoyproxy: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + tracing: + samplingRate: 100 + provider: + backendRefs: + - name: otel-collector + namespace: monitoring + port: 4317 + type: OpenTelemetry + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-dev" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +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: Same diff --git a/internal/gatewayapi/testdata/envoyproxy-tracing-backend.out.yaml b/internal/gatewayapi/testdata/envoyproxy-tracing-backend.out.yaml new file mode 100755 index 00000000000..8393d9c4ff8 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-tracing-backend.out.yaml @@ -0,0 +1,150 @@ +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: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + 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 +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-dev + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + tracing: + provider: + backendRefs: + - name: otel-collector + namespace: monitoring + port: 4317 + type: OpenTelemetry + samplingRate: 100 + status: {} + 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 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + tracing: + destination: + name: tracing + settings: + - addressType: IP + endpoints: + - host: 8.7.6.5 + port: 4317 + protocol: GRPC + samplingRate: 100 + serviceName: gateway-1.envoy-gateway diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml index 736c23a35b7..866ca1e861a 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml @@ -78,7 +78,7 @@ tcpRoutes: type: Accepted - lastTransitionTime: null message: TCP Port 8081 not found on service default/service-1 - reason: PortNotFound + reason: BackendNotFound status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml index 2603bbfad7d..03cf19502d7 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml @@ -78,7 +78,7 @@ udpRoutes: type: Accepted - lastTransitionTime: null message: UDP Port 8080 not found on service default/service-1 - reason: PortNotFound + reason: BackendNotFound status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml index 54e0ea88d40..c1f4d5feb57 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml @@ -68,7 +68,7 @@ httpRoutes: type: Accepted - lastTransitionTime: null message: TCP Port 8888 not found on service default/service-1 - reason: PortNotFound + reason: BackendNotFound status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/tracing-merged-multiple-routes.out.yaml b/internal/gatewayapi/testdata/tracing-merged-multiple-routes.out.yaml index 8ff7d8ec5b2..9fd0cc12b2d 100755 --- a/internal/gatewayapi/testdata/tracing-merged-multiple-routes.out.yaml +++ b/internal/gatewayapi/testdata/tracing-merged-multiple-routes.out.yaml @@ -292,7 +292,14 @@ xdsIR: name: "" prefix: / tracing: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 + authority: otel-collector.monitoring.svc.cluster.local + destination: + name: tracing + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 samplingRate: 100 serviceName: envoy-gateway-class diff --git a/internal/gatewayapi/testdata/tracing-multiple-routes.out.yaml b/internal/gatewayapi/testdata/tracing-multiple-routes.out.yaml index d8d945ac796..90f8dc6a27d 100755 --- a/internal/gatewayapi/testdata/tracing-multiple-routes.out.yaml +++ b/internal/gatewayapi/testdata/tracing-multiple-routes.out.yaml @@ -282,8 +282,15 @@ xdsIR: name: "" prefix: / tracing: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 + authority: otel-collector.monitoring.svc.cluster.local + destination: + name: tracing + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 samplingRate: 100 serviceName: gateway-1.envoy-gateway envoy-gateway/gateway-2: @@ -327,7 +334,14 @@ xdsIR: name: "" prefix: / tracing: - host: otel-collector.monitoring.svc.cluster.local - port: 4317 + authority: otel-collector.monitoring.svc.cluster.local + destination: + name: tracing + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 samplingRate: 100 serviceName: gateway-2.envoy-gateway diff --git a/internal/gatewayapi/translator_test.go b/internal/gatewayapi/translator_test.go index 1a200555ba2..69b46828e26 100644 --- a/internal/gatewayapi/translator_test.go +++ b/internal/gatewayapi/translator_test.go @@ -215,6 +215,56 @@ func TestTranslate(t *testing.T) { }, ) + // add otel-collector service + resources.Services = append(resources.Services, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "monitoring", + Name: "otel-collector", + }, + Spec: corev1.ServiceSpec{ + ClusterIP: "3.3.3.3", + Ports: []corev1.ServicePort{ + { + Name: "grpc", + Port: 4317, + TargetPort: intstr.IntOrString{IntVal: 4317}, + Protocol: corev1.ProtocolTCP, + }, + }, + }, + }, + ) + resources.EndpointSlices = append(resources.EndpointSlices, + &discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: "otel-collector-endpointslice", + Namespace: "monitoring", + Labels: map[string]string{ + discoveryv1.LabelServiceName: "otel-collector", + }, + }, + AddressType: discoveryv1.AddressTypeIPv4, + Ports: []discoveryv1.EndpointPort{ + { + Name: ptr.To("grpc"), + Port: ptr.To[int32](4317), + Protocol: ptr.To(corev1.ProtocolTCP), + }, + }, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{ + "8.7.6.5", + }, + Conditions: discoveryv1.EndpointConditions{ + Ready: ptr.To(true), + }, + }, + }, + }, + ) + resources.Namespaces = append(resources.Namespaces, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "envoy-gateway", diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 344b886a6f9..208fa300d45 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -50,7 +50,16 @@ func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, par backendRefKind := KindDerefOr(backendRef.Kind, KindService) switch backendRefKind { case KindService: - if !t.validateBackendService(backendRef, parentRef, resources, backendNamespace, route, protocol) { + if err := validateBackendService(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { + routeStatus := GetRouteStatus(route) + status.SetRouteStatusCondition(routeStatus, + parentRef.routeParentStatusIdx, + route.GetGeneration(), + gwapiv1.RouteConditionResolvedRefs, + metav1.ConditionFalse, + gwapiv1.RouteReasonBackendNotFound, + err.Error(), + ) return false } case KindServiceImport: @@ -179,22 +188,14 @@ func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, paren return true } -func (t *Translator) validateBackendService(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, resources *Resources, - serviceNamespace string, route RouteContext, protocol v1.Protocol, -) bool { +func validateBackendService(backendRef gwapiv1a2.BackendObjectReference, resources *Resources, + serviceNamespace string, protocol v1.Protocol, +) error { service := resources.GetService(serviceNamespace, string(backendRef.Name)) if service == nil { - routeStatus := GetRouteStatus(route) - status.SetRouteStatusCondition(routeStatus, - parentRef.routeParentStatusIdx, - route.GetGeneration(), - gwapiv1.RouteConditionResolvedRefs, - metav1.ConditionFalse, - gwapiv1.RouteReasonBackendNotFound, - fmt.Sprintf("Service %s/%s not found", NamespaceDerefOr(backendRef.Namespace, route.GetNamespace()), - string(backendRef.Name)), - ) - return false + // nolint: stylecheck + // The error message is used in the status.conditions, so it should be capitalized. + return fmt.Errorf("Service %s/%s not found", serviceNamespace, string(backendRef.Name)) } var portFound bool for _, port := range service.Spec.Ports { @@ -209,19 +210,9 @@ func (t *Translator) validateBackendService(backendRef *gwapiv1a2.BackendRef, pa } if !portFound { - routeStatus := GetRouteStatus(route) - status.SetRouteStatusCondition(routeStatus, - parentRef.routeParentStatusIdx, - route.GetGeneration(), - gwapiv1.RouteConditionResolvedRefs, - metav1.ConditionFalse, - "PortNotFound", - fmt.Sprintf(string(protocol)+" Port %d not found on service %s/%s", *backendRef.Port, serviceNamespace, - string(backendRef.Name)), - ) - return false + return fmt.Errorf("%s Port %d not found on service %s/%s", string(protocol), *backendRef.Port, serviceNamespace, string(backendRef.Name)) } - return true + return nil } func (t *Translator) validateBackendServiceImport(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, resources *Resources, diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 3eb789f19d8..025f5702648 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -1521,11 +1521,11 @@ type JSONAccessLog struct { // OpenTelemetryAccessLog holds the configuration for OpenTelemetry access logging. // +k8s:deepcopy-gen=true type OpenTelemetryAccessLog struct { - Text *string `json:"text,omitempty" yaml:"text,omitempty"` - Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` - Host string `json:"host" yaml:"host"` - Port uint32 `json:"port" yaml:"port"` - Resources map[string]string `json:"resources,omitempty" yaml:"resources,omitempty"` + Authority string `json:"authority,omitempty" yaml:"authority,omitempty"` + Text *string `json:"text,omitempty" yaml:"text,omitempty"` + Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` + Resources map[string]string `json:"resources,omitempty" yaml:"resources,omitempty"` + Destination RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` } // EnvoyPatchPolicy defines the intermediate representation of the EnvoyPatchPolicy resource. @@ -1580,10 +1580,10 @@ type JSONPatchOperation struct { // +k8s:deepcopy-gen=true type Tracing struct { ServiceName string `json:"serviceName"` - Host string `json:"host"` - Port uint32 `json:"port"` + Authority string `json:"authority,omitempty"` SamplingRate float64 `json:"samplingRate,omitempty"` CustomTags map[string]egv1a1.CustomTag `json:"customTags,omitempty"` + Destination RouteDestination `json:"destination,omitempty"` } // Metrics defines the configuration for metrics generated by Envoy diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 0965e401376..9fcbf302d29 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -1516,6 +1516,7 @@ func (in *OpenTelemetryAccessLog) DeepCopyInto(out *OpenTelemetryAccessLog) { (*out)[key] = val } } + in.Destination.DeepCopyInto(&out.Destination) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryAccessLog. @@ -2416,6 +2417,7 @@ func (in *Tracing) DeepCopyInto(out *Tracing) { (*out)[key] = *val.DeepCopy() } } + in.Destination.DeepCopyInto(&out.Destination) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tracing. diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 0c33873c1df..a11a54391ae 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -221,6 +221,19 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques return reconcile.Result{}, err } + // Process the parametersRef of the accepted GatewayClass. + // This should run before processBackendRefs + if managedGC.Spec.ParametersRef != nil && managedGC.DeletionTimestamp == nil { + if err := r.processParamsRef(ctx, managedGC, resourceMappings, gwcResource); err != nil { + msg := fmt.Sprintf("%s: %v", status.MsgGatewayClassInvalidParams, err) + if err := r.updateStatusForGatewayClass(ctx, managedGC, false, string(gwapiv1.GatewayClassReasonInvalidParameters), msg); err != nil { + r.log.Error(err, "unable to update GatewayClass status") + } + r.log.Error(err, "failed to process parametersRef for gatewayclass", "name", managedGC.Name) + return reconcile.Result{}, err + } + } + // Add the referenced services, ServiceImports, and EndpointSlices in // the collected BackendRefs to the resourceTree. // BackendRefs are referred by various Route objects and the ExtAuth in SecurityPolicies. @@ -241,18 +254,6 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques gwcResource.Namespaces = append(gwcResource.Namespaces, namespace) } - // Process the parametersRef of the accepted GatewayClass. - if managedGC.Spec.ParametersRef != nil && managedGC.DeletionTimestamp == nil { - if err := r.processParamsRef(ctx, managedGC, gwcResource); err != nil { - msg := fmt.Sprintf("%s: %v", status.MsgGatewayClassInvalidParams, err) - if err := r.updateStatusForGatewayClass(ctx, managedGC, false, string(gwapiv1.GatewayClassReasonInvalidParameters), msg); err != nil { - r.log.Error(err, "unable to update GatewayClass status") - } - r.log.Error(err, "failed to process parametersRef for gatewayclass", "name", managedGC.Name) - return reconcile.Result{}, err - } - } - if gwcResource.EnvoyProxy != nil && gwcResource.EnvoyProxy.Spec.MergeGateways != nil { if *gwcResource.EnvoyProxy.Spec.MergeGateways { r.mergeGateways.Insert(managedGC.Name) @@ -1451,7 +1452,7 @@ func (r *gatewayAPIReconciler) hasManagedClass(ep *egv1a1.EnvoyProxy) bool { } // processParamsRef processes the parametersRef of the provided GatewayClass. -func (r *gatewayAPIReconciler) processParamsRef(ctx context.Context, gc *gwapiv1.GatewayClass, resourceTree *gatewayapi.Resources) error { +func (r *gatewayAPIReconciler) processParamsRef(ctx context.Context, gc *gwapiv1.GatewayClass, resourceMap *resourceMappings, resourceTree *gatewayapi.Resources) error { if !refsEnvoyProxy(gc) { return fmt.Errorf("unsupported parametersRef for gatewayclass %s", gc.Name) } @@ -1480,6 +1481,60 @@ func (r *gatewayAPIReconciler) processParamsRef(ctx context.Context, gc *gwapiv1 validationErr = fmt.Errorf("invalid envoyproxy: %w", err) continue } + + if ep.Spec.Telemetry != nil { + telemetry := ep.Spec.Telemetry + + if telemetry.AccessLog != nil { + for _, setting := range telemetry.AccessLog.Settings { + for _, sink := range setting.Sinks { + if sink.OpenTelemetry == nil { + continue + } + for _, backendRef := range sink.OpenTelemetry.BackendRefs { + backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, ep.Namespace) + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, + }] = struct{}{} + } + } + } + } + + if telemetry.Metrics != nil { + for _, sink := range telemetry.Metrics.Sinks { + if sink.OpenTelemetry == nil { + continue + } + for _, backendRef := range sink.OpenTelemetry.BackendRefs { + backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, ep.Namespace) + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, + }] = struct{}{} + } + } + } + + if telemetry.Tracing != nil { + for _, backendRef := range telemetry.Tracing.Provider.BackendRefs { + backendNamespace := gatewayapi.NamespaceDerefOrAlpha(backendRef.Namespace, ep.Namespace) + resourceMap.allAssociatedBackendRefs[gwapiv1.BackendObjectReference{ + Group: backendRef.BackendObjectReference.Group, + Kind: backendRef.BackendObjectReference.Kind, + Namespace: gatewayapi.NamespacePtrV1Alpha2(backendNamespace), + Name: backendRef.Name, + }] = struct{}{} + + } + } + } + valid = true resourceTree.EnvoyProxy = &ep break diff --git a/internal/provider/kubernetes/controller_test.go b/internal/provider/kubernetes/controller_test.go index 860ba09e4b1..edd8b53c703 100644 --- a/internal/provider/kubernetes/controller_test.go +++ b/internal/provider/kubernetes/controller_test.go @@ -467,7 +467,8 @@ func TestProcessParamsRef(t *testing.T) { // Process the test case gatewayclasses. resourceTree := gatewayapi.NewResources() - err := r.processParamsRef(context.Background(), tc.gc, resourceTree) + resourceMap := newResourceMapping() + err := r.processParamsRef(context.Background(), tc.gc, resourceMap, resourceTree) if tc.expected { require.NoError(t, err) // Ensure the resource tree and map are as expected. diff --git a/internal/xds/translator/accesslog.go b/internal/xds/translator/accesslog.go index 446dc7d3fba..a3a2c819285 100644 --- a/internal/xds/translator/accesslog.go +++ b/internal/xds/translator/accesslog.go @@ -23,7 +23,6 @@ import ( "golang.org/x/exp/maps" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/structpb" - "k8s.io/utils/ptr" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils/protocov" @@ -179,8 +178,8 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo GrpcService: &cfgcore.GrpcService{ TargetSpecifier: &cfgcore.GrpcService_EnvoyGrpc_{ EnvoyGrpc: &cfgcore.GrpcService_EnvoyGrpc{ - ClusterName: buildClusterName("accesslog", otel.Host, otel.Port), - Authority: otel.Host, + ClusterName: otel.Destination.Name, + Authority: otel.Authority, }, }, }, @@ -334,23 +333,15 @@ func processClusterForAccessLog(tCtx *types.ResourceVersionTable, al *ir.AccessL } for _, otel := range al.OpenTelemetry { - clusterName := buildClusterName("accesslog", otel.Host, otel.Port) - - ds := &ir.DestinationSetting{ - Weight: ptr.To[uint32](1), - Protocol: ir.GRPC, - Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(otel.Host, otel.Port)}, - } if err := addXdsCluster(tCtx, &xdsClusterArgs{ - name: clusterName, - settings: []*ir.DestinationSetting{ds}, + name: otel.Destination.Name, + settings: otel.Destination.Settings, tSocket: nil, endpointType: EndpointTypeDNS, metrics: metrics, }); err != nil && !errors.Is(err, ErrXdsClusterExists) { return err } - } return nil diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go index bb18a7e0286..a37f2a91170 100644 --- a/internal/xds/translator/cluster.go +++ b/internal/xds/translator/cluster.go @@ -539,13 +539,6 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs) map[string]*anypb. return extensionOptions } -// buildClusterName returns a cluster name for the given `host` and `port`. -// The format is: ||, where type is "accesslog" for access logs. -// It's easy to distinguish when debugging. -func buildClusterName(prefix string, host string, port uint32) string { - return fmt.Sprintf("%s|%s|%d", prefix, host, port) -} - // buildProxyProtocolSocket builds the ProxyProtocol transport socket. func buildProxyProtocolSocket(proxyProtocol *ir.ProxyProtocol, tSocket *corev3.TransportSocket) *corev3.TransportSocket { if proxyProtocol == nil { diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-endpoint-stats.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-endpoint-stats.yaml index ca71bbad491..b5244667de9 100644 --- a/internal/xds/translator/testdata/in/xds-ir/accesslog-endpoint-stats.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-endpoint-stats.yaml @@ -3,44 +3,50 @@ metrics: enablePerEndpointStats: true accesslog: text: - - path: "/dev/stdout" - format: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + - path: "/dev/stdout" + format: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" json: - - path: "/dev/stdout" - json: - start_time: "%START_TIME%" - method: "%REQ(:METHOD)%" - path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" - protocol: "%PROTOCOL%" - response_code: "%RESPONSE_CODE%" + - path: "/dev/stdout" + json: + start_time: "%START_TIME%" + method: "%REQ(:METHOD)%" + path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" + protocol: "%PROTOCOL%" + response_code: "%RESPONSE_CODE%" openTelemetry: - - text: | - [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" - attributes: - "response_code": "%RESPONSE_CODE%" - resources: - "cluster_name": "cluster1" - host: otel-collector.default.svc.cluster.local - port: 4317 + - text: | + [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" + attributes: + "response_code": "%RESPONSE_CODE%" + resources: + "cluster_name": "cluster1" + authority: "otel-collector.default.svc.cluster.local" + destination: + name: "accesslog-0" + settings: + - endpoints: + - host: "otel-collector.default.svc.cluster.local" + port: 4317 + protocol: "GRPC" http: - - name: "first-listener" - address: "0.0.0.0" - port: 10080 - hostnames: - - "*" - path: - mergeSlashes: true - escapedSlashesAction: UnescapeAndRedirect - routes: - - name: "direct-route" - hostname: "*" - destination: - name: "direct-route-dest" - settings: - - endpoints: - - host: "1.2.3.4" - port: 50000 - directResponse: - body: "Unknown custom filter type: UnsupportedType" - statusCode: 500 +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-formatters.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-formatters.yaml index 4dc9c30bf1a..569ec561eb9 100644 --- a/internal/xds/translator/testdata/in/xds-ir/accesslog-formatters.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-formatters.yaml @@ -27,8 +27,14 @@ accesslog: "response_code": "%RESPONSE_CODE%" resources: "cluster_name": "cluster1" - host: otel-collector.default.svc.cluster.local - port: 4317 + authority: "otel-collector.default.svc.cluster.local" + destination: + name: "accesslog-0" + settings: + - endpoints: + - host: "otel-collector.default.svc.cluster.local" + port: 4317 + protocol: "GRPC" http: - name: "first-listener" address: "0.0.0.0" diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml index 37ce0958e73..10768da4354 100644 --- a/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-invalid.yaml @@ -19,8 +19,12 @@ accesslog: "response_code": "%RESPONSE_CODE%" resources: "cluster_name": "cluster1" - host: "" - port: 4317 + destination: + name: "accesslog-0" + settings: + - endpoints: + - host: "" + port: 4317 http: - name: "first-listener" address: "0.0.0.0" diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml index f4ec43ac072..07d910197a7 100644 --- a/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog.yaml @@ -19,8 +19,14 @@ accesslog: "response_code": "%RESPONSE_CODE%" resources: "cluster_name": "cluster1" - host: otel-collector.default.svc.cluster.local - port: 4317 + authority: "otel-collector.default.svc.cluster.local" + destination: + name: "accesslog-0" + settings: + - endpoints: + - host: "otel-collector.default.svc.cluster.local" + port: 4317 + protocol: "GRPC" http: - name: "first-listener" address: "0.0.0.0" diff --git a/internal/xds/translator/testdata/in/xds-ir/tracing-endpoint-stats.yaml b/internal/xds/translator/testdata/in/xds-ir/tracing-endpoint-stats.yaml index 4a566033560..f57949f2ce4 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tracing-endpoint-stats.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tracing-endpoint-stats.yaml @@ -19,8 +19,14 @@ tracing: requestHeader: name: "X-Request-Id" defaultValue: "-" - host: otel-collector.monitoring.svc.cluster.local - port: 4317 + destination: + name: "tracing-0" + authority: "otel-collector.default.svc.cluster.local" + settings: + - endpoints: + - host: "otel-collector.default.svc.cluster.local" + port: 4317 + protocol: "GRPC" http: - name: "first-listener" address: "0.0.0.0" diff --git a/internal/xds/translator/testdata/in/xds-ir/tracing.yaml b/internal/xds/translator/testdata/in/xds-ir/tracing.yaml index 674eb66acef..fd5a29672dd 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tracing.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tracing.yaml @@ -17,26 +17,32 @@ tracing: requestHeader: name: "X-Request-Id" defaultValue: "-" - host: otel-collector.monitoring.svc.cluster.local - port: 4317 + authority: "otel-collector.default.svc.cluster.local" + destination: + name: "tracing-0" + settings: + - endpoints: + - host: "otel-collector.default.svc.cluster.local" + port: 4317 + protocol: "GRPC" http: - - name: "first-listener" - address: "0.0.0.0" - port: 10080 - hostnames: - - "*" - path: - mergeSlashes: true - escapedSlashesAction: UnescapeAndRedirect - routes: - - name: "direct-route" - hostname: "*" - destination: - name: "direct-route-dest" - settings: - - endpoints: - - host: "1.2.3.4" - port: 50000 - directResponse: - body: "Unknown custom filter type: UnsupportedType" - statusCode: 500 +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.clusters.yaml index ac33653a54f..cbf453a1f17 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.clusters.yaml @@ -27,7 +27,7 @@ dnsRefreshRate: 30s lbPolicy: LEAST_REQUEST loadAssignment: - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 endpoints: - lbEndpoints: - endpoint: @@ -38,8 +38,8 @@ loadBalancingWeight: 1 loadBalancingWeight: 1 locality: - region: accesslog|otel-collector.default.svc.cluster.local|4317/backend/0 - name: accesslog|otel-collector.default.svc.cluster.local|4317 + region: accesslog-0/backend/0 + name: accesslog-0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 respectDnsTtl: true diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.listeners.yaml index e6d5535eb15..00dbfe058f3 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-endpoint-stats.listeners.yaml @@ -51,7 +51,7 @@ grpcService: envoyGrpc: authority: otel-collector.default.svc.cluster.local - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 logName: otel_envoy_accesslog transportApiVersion: V3 resourceAttributes: @@ -109,7 +109,7 @@ grpcService: envoyGrpc: authority: otel-collector.default.svc.cluster.local - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 logName: otel_envoy_accesslog transportApiVersion: V3 resourceAttributes: diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.clusters.yaml index 7168156486d..b8874bf24f9 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.clusters.yaml @@ -25,7 +25,7 @@ dnsRefreshRate: 30s lbPolicy: LEAST_REQUEST loadAssignment: - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 endpoints: - lbEndpoints: - endpoint: @@ -36,8 +36,8 @@ loadBalancingWeight: 1 loadBalancingWeight: 1 locality: - region: accesslog|otel-collector.default.svc.cluster.local|4317/backend/0 - name: accesslog|otel-collector.default.svc.cluster.local|4317 + region: accesslog-0/backend/0 + name: accesslog-0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 respectDnsTtl: true diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.listeners.yaml index 3407a6549c7..a54210a550a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-formatters.listeners.yaml @@ -79,7 +79,7 @@ grpcService: envoyGrpc: authority: otel-collector.default.svc.cluster.local - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 logName: otel_envoy_accesslog transportApiVersion: V3 resourceAttributes: @@ -161,7 +161,7 @@ grpcService: envoyGrpc: authority: otel-collector.default.svc.cluster.local - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 logName: otel_envoy_accesslog transportApiVersion: V3 resourceAttributes: diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml index 7168156486d..b8874bf24f9 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog.clusters.yaml @@ -25,7 +25,7 @@ dnsRefreshRate: 30s lbPolicy: LEAST_REQUEST loadAssignment: - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 endpoints: - lbEndpoints: - endpoint: @@ -36,8 +36,8 @@ loadBalancingWeight: 1 loadBalancingWeight: 1 locality: - region: accesslog|otel-collector.default.svc.cluster.local|4317/backend/0 - name: accesslog|otel-collector.default.svc.cluster.local|4317 + region: accesslog-0/backend/0 + name: accesslog-0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 respectDnsTtl: true diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog.listeners.yaml index e6d5535eb15..00dbfe058f3 100644 --- a/internal/xds/translator/testdata/out/xds-ir/accesslog.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog.listeners.yaml @@ -51,7 +51,7 @@ grpcService: envoyGrpc: authority: otel-collector.default.svc.cluster.local - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 logName: otel_envoy_accesslog transportApiVersion: V3 resourceAttributes: @@ -109,7 +109,7 @@ grpcService: envoyGrpc: authority: otel-collector.default.svc.cluster.local - clusterName: accesslog|otel-collector.default.svc.cluster.local|4317 + clusterName: accesslog-0 logName: otel_envoy_accesslog transportApiVersion: V3 resourceAttributes: diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.clusters.yaml index 24a732d8e70..0936f4b459a 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.clusters.yaml @@ -27,19 +27,19 @@ dnsRefreshRate: 30s lbPolicy: LEAST_REQUEST loadAssignment: - clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 + clusterName: tracing-0 endpoints: - lbEndpoints: - endpoint: address: socketAddress: - address: otel-collector.monitoring.svc.cluster.local + address: otel-collector.default.svc.cluster.local portValue: 4317 loadBalancingWeight: 1 loadBalancingWeight: 1 locality: - region: tracing|otel-collector.monitoring.svc.cluster.local|4317/backend/0 - name: tracing|otel-collector.monitoring.svc.cluster.local|4317 + region: tracing-0/backend/0 + name: tracing-0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 respectDnsTtl: true diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml index c8277f1e190..7273073eef0 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing-endpoint-stats.listeners.yaml @@ -51,8 +51,7 @@ '@type': type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig grpcService: envoyGrpc: - authority: otel-collector.monitoring.svc.cluster.local - clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 + clusterName: tracing-0 serviceName: fake-name.fake-ns randomSampling: value: 90 diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml index eea2936ce62..35bc81158da 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing.clusters.yaml @@ -25,19 +25,19 @@ dnsRefreshRate: 30s lbPolicy: LEAST_REQUEST loadAssignment: - clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 + clusterName: tracing-0 endpoints: - lbEndpoints: - endpoint: address: socketAddress: - address: otel-collector.monitoring.svc.cluster.local + address: otel-collector.default.svc.cluster.local portValue: 4317 loadBalancingWeight: 1 loadBalancingWeight: 1 locality: - region: tracing|otel-collector.monitoring.svc.cluster.local|4317/backend/0 - name: tracing|otel-collector.monitoring.svc.cluster.local|4317 + region: tracing-0/backend/0 + name: tracing-0 outlierDetection: {} perConnectionBufferLimitBytes: 32768 respectDnsTtl: true diff --git a/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml index c8277f1e190..3cfe176bc00 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tracing.listeners.yaml @@ -51,8 +51,8 @@ '@type': type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig grpcService: envoyGrpc: - authority: otel-collector.monitoring.svc.cluster.local - clusterName: tracing|otel-collector.monitoring.svc.cluster.local|4317 + authority: otel-collector.default.svc.cluster.local + clusterName: tracing-0 serviceName: fake-name.fake-ns randomSampling: value: 90 diff --git a/internal/xds/translator/tracing.go b/internal/xds/translator/tracing.go index 44e80681c59..c31d25b7290 100644 --- a/internal/xds/translator/tracing.go +++ b/internal/xds/translator/tracing.go @@ -16,7 +16,6 @@ import ( tracingtype "github.com/envoyproxy/go-control-plane/envoy/type/tracing/v3" xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3" "google.golang.org/protobuf/types/known/wrapperspb" - "k8s.io/utils/ptr" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" @@ -33,8 +32,8 @@ func buildHCMTracing(tracing *ir.Tracing) (*hcm.HttpConnectionManager_Tracing, e GrpcService: &corev3.GrpcService{ TargetSpecifier: &corev3.GrpcService_EnvoyGrpc_{ EnvoyGrpc: &corev3.GrpcService_EnvoyGrpc{ - ClusterName: buildClusterName("tracing", tracing.Host, tracing.Port), - Authority: tracing.Host, + ClusterName: tracing.Destination.Name, + Authority: tracing.Authority, }, }, }, @@ -124,16 +123,9 @@ func processClusterForTracing(tCtx *types.ResourceVersionTable, tracing *ir.Trac return nil } - clusterName := buildClusterName("tracing", tracing.Host, tracing.Port) - - ds := &ir.DestinationSetting{ - Weight: ptr.To[uint32](1), - Protocol: ir.GRPC, - Endpoints: []*ir.DestinationEndpoint{ir.NewDestEndpoint(tracing.Host, tracing.Port)}, - } if err := addXdsCluster(tCtx, &xdsClusterArgs{ - name: clusterName, - settings: []*ir.DestinationSetting{ds}, + name: tracing.Destination.Name, + settings: tracing.Destination.Settings, tSocket: nil, endpointType: EndpointTypeDNS, metrics: metrics,