From 51c6eb4b392859afe744c3157aca0975422876d9 Mon Sep 17 00:00:00 2001 From: zirain Date: Sat, 29 Jun 2024 08:40:48 +0800 Subject: [PATCH] feat: AccessLog support CEL Filter (#3688) * translator Signed-off-by: zirain * gatewayapi Signed-off-by: zirain * filename Signed-off-by: zirain * share CEL Env Signed-off-by: zirain --------- Signed-off-by: zirain --- go.mod | 3 + go.sum | 6 + internal/gatewayapi/listener.go | 25 ++ ...oyproxy-accesslog-cel-with-invalid.in.yaml | 90 +++++++ ...yproxy-accesslog-cel-with-invalid.out.yaml | 153 ++++++++++++ .../testdata/envoyproxy-accesslog-cel.in.yaml | 89 +++++++ .../envoyproxy-accesslog-cel.out.yaml | 167 +++++++++++++ internal/ir/xds.go | 1 + internal/ir/zz_generated.deepcopy.go | 5 + internal/xds/translator/accesslog.go | 52 ++++- .../testdata/in/xds-ir/accesslog-cel.yaml | 52 +++++ .../in/xds-ir/accesslog-multi-cel.yaml | 53 +++++ .../out/xds-ir/accesslog-cel.clusters.yaml | 49 ++++ .../out/xds-ir/accesslog-cel.endpoints.yaml | 12 + .../out/xds-ir/accesslog-cel.listeners.yaml | 184 +++++++++++++++ .../out/xds-ir/accesslog-cel.routes.yaml | 14 ++ .../xds-ir/accesslog-multi-cel.clusters.yaml | 49 ++++ .../xds-ir/accesslog-multi-cel.endpoints.yaml | 12 + .../xds-ir/accesslog-multi-cel.listeners.yaml | 220 ++++++++++++++++++ .../xds-ir/accesslog-multi-cel.routes.yaml | 14 ++ 20 files changed, 1246 insertions(+), 4 deletions(-) create mode 100644 internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-invalid.in.yaml create mode 100644 internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-invalid.out.yaml create mode 100644 internal/gatewayapi/testdata/envoyproxy-accesslog-cel.in.yaml create mode 100644 internal/gatewayapi/testdata/envoyproxy-accesslog-cel.out.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/accesslog-cel.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/accesslog-multi-cel.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-cel.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-cel.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-cel.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-cel.routes.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.routes.yaml diff --git a/go.mod b/go.mod index 9d4f56f40d1..b65d907503d 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/go-logr/zapr v1.3.0 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.4 + github.com/google/cel-go v0.20.1 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.19.1 github.com/grafana/tempo v1.5.0 @@ -78,6 +79,7 @@ require ( github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.3 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/containerd v1.7.17 // indirect @@ -120,6 +122,7 @@ require ( github.com/rubenv/sql-migrate v1.6.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.6.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect diff --git a/go.sum b/go.sum index 83196a0ef50..8bc7d41c370 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= @@ -322,6 +324,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -624,6 +628,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 6978d806ad6..162a9eca976 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -9,8 +9,10 @@ import ( "errors" "fmt" + "github.com/google/cel-go/cel" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -301,6 +303,22 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * irAccessLog.OpenTelemetry = append(irAccessLog.OpenTelemetry, al) } } + + var ( + validExprs []string + errs []error + ) + for _, expr := range accessLog.Matches { + if !validCELExpression(expr) { + errs = append(errs, fmt.Errorf("invalid CEL expression: %s", expr)) + continue + } + validExprs = append(validExprs, expr) + } + if len(errs) > 0 { + return nil, utilerrors.NewAggregate(errs) + } + irAccessLog.CELMatches = validExprs } return irAccessLog, nil @@ -411,3 +429,10 @@ func destinationSettingFromHostAndPort(host string, port uint32) []*ir.Destinati }, } } + +var celEnv, _ = cel.NewEnv() + +func validCELExpression(expr string) bool { + _, issue := celEnv.Parse(expr) + return issue.Err() == nil +} diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-invalid.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-invalid.in.yaml new file mode 100644 index 00000000000..dd96ed4b01e --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-invalid.in.yaml @@ -0,0 +1,90 @@ +envoyProxyForGatewayClass: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - matches: + - "response.code >= 400" + - ")++++" # invalid CEL expression will be ignored + 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: + host: otel-collector.monitoring.svc.cluster.local + 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-cel-with-invalid.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-invalid.out.yaml new file mode 100644 index 00000000000..e23a524604d --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel-with-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: invalid CEL expression: )++++' + 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 + matches: + - response.code >= 400 + - )++++ + sinks: + - file: + path: /dev/stdout + type: File + - openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + 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-cel.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel.in.yaml new file mode 100644 index 00000000000..3d9f52018dc --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel.in.yaml @@ -0,0 +1,89 @@ +envoyProxyForGatewayClass: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - matches: + - "response.code >= 400" + 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: + host: otel-collector.monitoring.svc.cluster.local + 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-cel.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel.out.yaml new file mode 100644 index 00000000000..3d0c0ae526d --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-cel.out.yaml @@ -0,0 +1,167 @@ +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: + 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 + matches: + - response.code >= 400 + sinks: + - file: + path: /dev/stdout + type: File + - openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + 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: + accessLog: + celMatches: + - response.code >= 400 + openTelemetry: + - 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: | + [%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 + text: + - format: | + [%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 + 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/ir/xds.go b/internal/ir/xds.go index b3d3ed3efd9..92e6f748e8c 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -1599,6 +1599,7 @@ type RateLimitValue struct { // AccessLog holds the access logging configuration. // +k8s:deepcopy-gen=true type AccessLog struct { + CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` Text []*TextAccessLog `json:"text,omitempty" yaml:"text,omitempty"` JSON []*JSONAccessLog `json:"json,omitempty" yaml:"json,omitempty"` OpenTelemetry []*OpenTelemetryAccessLog `json:"openTelemetry,omitempty" yaml:"openTelemetry,omitempty"` diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 4b87aa93b9c..e98364722d9 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -19,6 +19,11 @@ import ( // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AccessLog) DeepCopyInto(out *AccessLog) { *out = *in + if in.CELMatches != nil { + in, out := &in.CELMatches, &out.CELMatches + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Text != nil { in, out := &in.Text, &out.Text *out = make([]*TextAccessLog, len(*in)) diff --git a/internal/xds/translator/accesslog.go b/internal/xds/translator/accesslog.go index ce58b507c96..05e05bee30d 100644 --- a/internal/xds/translator/accesslog.go +++ b/internal/xds/translator/accesslog.go @@ -13,6 +13,7 @@ import ( accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" cfgcore "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" fileaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" + cel "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/filters/cel/v3" grpcaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3" otelaccesslog "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/open_telemetry/v3" celformatter "github.com/envoyproxy/go-control-plane/envoy/extensions/formatter/cel/v3" @@ -54,6 +55,8 @@ const ( reqWithoutQueryCommandOperator = "%REQ_WITHOUT_QUERY" metadataCommandOperator = "%METADATA" celCommandOperator = "%CEL" + + celFilter = "envoy.access_loggers.extension_filters.cel" ) // for the case when a route does not exist to upstream, hcm logs will not be present @@ -217,16 +220,57 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo }) } - // add filter for listener access logs + // add filter for access logs + filters := make([]*accesslog.AccessLogFilter, 0) + for _, expr := range al.CELMatches { + filters = append(filters, celAccessLogFilter(expr)) + } if forListener { - for _, al := range accessLogs { - al.Filter = listenerAccessLogFilter - } + filters = append(filters, listenerAccessLogFilter) + } + + f := buildAccessLogFilter(filters...) + + for _, log := range accessLogs { + log.Filter = f } return accessLogs } +func celAccessLogFilter(expr string) *accesslog.AccessLogFilter { + fl := &cel.ExpressionFilter{ + Expression: expr, + } + + return &accesslog.AccessLogFilter{ + FilterSpecifier: &accesslog.AccessLogFilter_ExtensionFilter{ + ExtensionFilter: &accesslog.ExtensionFilter{ + Name: celFilter, + ConfigType: &accesslog.ExtensionFilter_TypedConfig{TypedConfig: protocov.ToAny(fl)}, + }, + }, + } +} + +func buildAccessLogFilter(f ...*accesslog.AccessLogFilter) *accesslog.AccessLogFilter { + if len(f) == 0 { + return nil + } + + if len(f) == 1 { + return f[0] + } + + return &accesslog.AccessLogFilter{ + FilterSpecifier: &accesslog.AccessLogFilter_AndFilter{ + AndFilter: &accesslog.AndFilter{ + Filters: f, + }, + }, + } +} + func accessLogTextFormatters(text string) []*cfgcore.TypedExtensionConfig { formatters := make([]*cfgcore.TypedExtensionConfig, 0, 3) diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-cel.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-cel.yaml new file mode 100644 index 00000000000..b0623fd0842 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-cel.yaml @@ -0,0 +1,52 @@ +name: "accesslog" +accesslog: + celMatches: + - response.code >= 400 + 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%" + 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%" + 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" + 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 diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-multi-cel.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-multi-cel.yaml new file mode 100644 index 00000000000..704c19863d6 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-multi-cel.yaml @@ -0,0 +1,53 @@ +name: "accesslog" +accesslog: + celMatches: + - response.code >= 400 + - request.url_path.contains('v1beta3') + 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%" + 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%" + 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" + 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 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.clusters.yaml new file mode 100644 index 00000000000..b8874bf24f9 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.clusters.yaml @@ -0,0 +1,49 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: accesslog-0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.default.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog-0/backend/0 + name: accesslog-0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.endpoints.yaml new file mode 100644 index 00000000000..20c80b3aaaa --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: direct-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: direct-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.listeners.yaml new file mode 100644 index 00000000000..2ccfca8ce50 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.listeners.yaml @@ -0,0 +1,184 @@ +- accessLog: + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + [%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 + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + jsonFormat: + method: '%REQ(:METHOD)%' + path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + response_code: '%RESPONSE_CODE%' + start_time: '%START_TIME%' + path: /dev/stdout + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + - key: response_code + value: + stringValue: '%RESPONSE_CODE%' + body: + stringValue: | + [%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%" + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.default.svc.cluster.local + clusterName: accesslog-0 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: cluster_name + value: + stringValue: cluster1 + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - filter: + extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + [%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 + - filter: + extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + jsonFormat: + method: '%REQ(:METHOD)%' + path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + response_code: '%RESPONSE_CODE%' + start_time: '%START_TIME%' + path: /dev/stdout + - filter: + extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + - key: response_code + value: + stringValue: '%RESPONSE_CODE%' + body: + stringValue: | + [%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%" + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.default.svc.cluster.local + clusterName: accesslog-0 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: cluster_name + value: + stringValue: cluster1 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + name: first-listener + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.routes.yaml new file mode 100644 index 00000000000..d4a7fa5ae20 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-cel.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - directResponse: + body: + inlineString: 'Unknown custom filter type: UnsupportedType' + status: 500 + match: + prefix: / + name: direct-route diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.clusters.yaml new file mode 100644 index 00000000000..b8874bf24f9 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.clusters.yaml @@ -0,0 +1,49 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: accesslog-0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.default.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog-0/backend/0 + name: accesslog-0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.endpoints.yaml new file mode 100644 index 00000000000..20c80b3aaaa --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: direct-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: direct-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.listeners.yaml new file mode 100644 index 00000000000..0bca441a443 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.listeners.yaml @@ -0,0 +1,220 @@ +- accessLog: + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: request.url_path.contains('v1beta3') + - responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + [%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 + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: request.url_path.contains('v1beta3') + - responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + jsonFormat: + method: '%REQ(:METHOD)%' + path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + response_code: '%RESPONSE_CODE%' + start_time: '%START_TIME%' + path: /dev/stdout + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: request.url_path.contains('v1beta3') + - responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + - key: response_code + value: + stringValue: '%RESPONSE_CODE%' + body: + stringValue: | + [%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%" + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.default.svc.cluster.local + clusterName: accesslog-0 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: cluster_name + value: + stringValue: cluster1 + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: request.url_path.contains('v1beta3') + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + [%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 + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: request.url_path.contains('v1beta3') + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + jsonFormat: + method: '%REQ(:METHOD)%' + path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%' + protocol: '%PROTOCOL%' + response_code: '%RESPONSE_CODE%' + start_time: '%START_TIME%' + path: /dev/stdout + - filter: + andFilter: + filters: + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: response.code >= 400 + - extensionFilter: + name: envoy.access_loggers.extension_filters.cel + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.filters.cel.v3.ExpressionFilter + expression: request.url_path.contains('v1beta3') + name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + - key: response_code + value: + stringValue: '%RESPONSE_CODE%' + body: + stringValue: | + [%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%" + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.default.svc.cluster.local + clusterName: accesslog-0 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: cluster_name + value: + stringValue: cluster1 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http + useRemoteAddress: true + name: first-listener + drainType: MODIFY_ONLY + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.routes.yaml new file mode 100644 index 00000000000..d4a7fa5ae20 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-multi-cel.routes.yaml @@ -0,0 +1,14 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - directResponse: + body: + inlineString: 'Unknown custom filter type: UnsupportedType' + status: 500 + match: + prefix: / + name: direct-route