diff --git a/docs/latest/design/envoy-patch-policy.md b/docs/latest/design/envoy-patch-policy.md index 1d7e82e15c..0116c8e633 100644 --- a/docs/latest/design/envoy-patch-policy.md +++ b/docs/latest/design/envoy-patch-policy.md @@ -31,23 +31,46 @@ Here is an example highlighting how a user can configure global ratelimiting usi ``` apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 kind: Gateway metadata: - name: eg-gw + name: eg namespace: default spec: - gatewayClassName: eg-gc + gatewayClassName: eg listeners: - - name: example - protocol: HTTPS - port: 443 - hostname: example.com - tls: - certificateRefs: - - kind: Secret - group: "" - name: example-cert ---- + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyPatchPolicy metadata: @@ -55,19 +78,19 @@ metadata: namespace: default spec: targetRef: - group: gateway.networking.k8s.io/v1beta1 + group: gateway.networking.k8s.io kind: Gateway - name: eg-gw + name: eg namespace: default type: JSONPatch jsonPatches: - type: "type.googleapis.com/envoy.config.listener.v3.Listener" - # The listener name is of the form -- - name: default-eg-gw-example + # The listener name is of the form // + name: default/eg/http operation: op: add - path: "/filter_chains/0/filters/0/http_filters/0" - value: | + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: name: "envoy.filters.http.ratelimit" typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" @@ -80,20 +103,20 @@ spec: cluster_name: rate-limit-cluster transport_api_version: V3 - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" - # The route name is of the form -- - name: default-eg-gw-example + # The route name is of the form // + name: default/eg/http operation: op: add path: "/virtual_hosts/0/rate_limits" - value: | + value: - actions: - remote_address: {} - - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" name: rate-limit-cluster operation: op: add - path: "/" - value: | + path: "" + value: name: rate-limit-cluster type: STRICT_DNS connect_timeout: 10s @@ -107,7 +130,7 @@ spec: address: socket_address: address: ratelimit.svc.cluster.local - port_value: 8081 + port_value: 8081 ``` diff --git a/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml b/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml new file mode 100644 index 0000000000..4851a46841 --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/in/envoy-patch-policy.yaml @@ -0,0 +1,157 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: http + protocol: HTTP + port: 80 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: backend + namespace: default +--- +apiVersion: v1 +kind: Service +metadata: + name: backend + namespace: default + labels: + app: backend + service: backend +spec: + ports: + - name: http + port: 3000 + targetPort: 3000 + selector: + app: backend +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: backend + version: v1 + template: + metadata: + labels: + app: backend + version: v1 + spec: + serviceAccountName: backend + containers: + - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e + imagePullPolicy: IfNotPresent + name: backend + ports: + - containerPort: 3000 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + name: ratelimit-patch-policy + namespace: default +spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default + type: JSONPatch + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + # The listener name is of the form // + name: default/eg/http + operation: + op: add + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eag-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + # The route name is of the form // + name: default/eg/http + operation: + op: add + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 diff --git a/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml b/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml new file mode 100644 index 0000000000..3c4158540e --- /dev/null +++ b/internal/cmd/egctl/testdata/translate/out/envoy-patch-policy.all.yaml @@ -0,0 +1,203 @@ +xds: + default/eg: + configs: + - '@type': type.googleapis.com/envoy.admin.v3.BootstrapConfigDump + bootstrap: + admin: + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socketAddress: + address: 127.0.0.1 + portValue: 19000 + dynamicResources: + adsConfig: + apiType: DELTA_GRPC + grpcServices: + - envoyGrpc: + clusterName: xds_cluster + setNodeOnFirstMessageOnly: true + transportApiVersion: V3 + cdsConfig: + ads: {} + resourceApiVersion: V3 + ldsConfig: + ads: {} + resourceApiVersion: V3 + layeredRuntime: + layers: + - name: runtime-0 + rtdsLayer: + name: runtime-0 + rtdsConfig: + ads: {} + resourceApiVersion: V3 + staticResources: + clusters: + - connectTimeout: 10s + loadAssignment: + clusterName: xds_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-gateway + portValue: 18000 + name: xds_cluster + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificateSdsSecretConfigs: + - name: xds_certificate + sdsConfig: + pathConfigSource: + path: /sds/xds-certificate.json + resourceApiVersion: V3 + tlsParams: + tlsMaximumProtocolVersion: TLSv1_3 + validationContextSdsSecretConfig: + name: xds_trusted_ca + sdsConfig: + pathConfigSource: + path: /sds/xds-trusted-ca.json + resourceApiVersion: V3 + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: {} + listeners: + - address: + socketAddress: + address: 0.0.0.0 + portValue: 19001 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.health_check + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + headers: + - name: :path + stringMatch: + exact: /ready + passThroughMode: false + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + routeConfig: + name: local_route + statPrefix: eg-ready-http + name: envoy-gateway-proxy-ready-0.0.0.0-19001 + - '@type': type.googleapis.com/envoy.admin.v3.EndpointsConfigDump + - '@type': type.googleapis.com/envoy.admin.v3.ClustersConfigDump + dynamicActiveClusters: + - cluster: + '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster + connectTimeout: 10s + http2ProtocolOptions: {} + loadAssignment: + clusterName: rate-limit-cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: ratelimit.svc.cluster.local + portValue: 8081 + name: rate-limit-cluster + type: STRICT_DNS + - '@type': type.googleapis.com/envoy.admin.v3.ListenersConfigDump + dynamicListeners: + - activeState: + listener: + '@type': type.googleapis.com/envoy.config.listener.v3.Listener + accessLog: + - filter: + 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 + 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: + - 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 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.ratelimit + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + domain: eag-ratelimit + failureModeDeny: true + rateLimitService: + grpcService: + envoyGrpc: + clusterName: rate-limit-cluster + transportApiVersion: V3 + timeout: 1s + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: default/eg/http + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: default/eg/http + perConnectionBufferLimitBytes: 32768 + - '@type': type.googleapis.com/envoy.admin.v3.RoutesConfigDump + dynamicRouteConfigs: + - routeConfig: + '@type': type.googleapis.com/envoy.config.route.v3.RouteConfiguration + ignorePortInHostMatching: true + name: default/eg/http + virtualHosts: + - domains: + - '*' + name: default/eg/http + rateLimits: + - actions: + - remoteAddress: {} diff --git a/internal/cmd/egctl/translate.go b/internal/cmd/egctl/translate.go index f2bb6ba95c..93a5edd298 100644 --- a/internal/cmd/egctl/translate.go +++ b/internal/cmd/egctl/translate.go @@ -736,6 +736,20 @@ func kubernetesYAMLToResources(str string, addMissingResources bool) (*gatewayap Spec: typedSpec.(egv1a1.AuthenticationFilterSpec), } resources.AuthenticationFilters = append(resources.AuthenticationFilters, authenticationFilter) + case egv1a1.KindEnvoyPatchPolicy: + typedSpec := spec.Interface() + envoyPatchPolicy := &egv1a1.EnvoyPatchPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: egv1a1.KindEnvoyPatchPolicy, + APIVersion: egv1a1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: typedSpec.(egv1a1.EnvoyPatchPolicySpec), + } + resources.EnvoyPatchPolicies = append(resources.EnvoyPatchPolicies, envoyPatchPolicy) } } diff --git a/internal/cmd/egctl/translate_test.go b/internal/cmd/egctl/translate_test.go index 8631c047ce..459408b060 100644 --- a/internal/cmd/egctl/translate_test.go +++ b/internal/cmd/egctl/translate_test.go @@ -178,6 +178,14 @@ func TestTranslate(t *testing.T) { resourceType: string(RouteEnvoyConfigType), expect: true, }, + { + name: "envoy-patch-policy", + from: "gateway-api", + to: "xds", + output: yamlOutput, + resourceType: string(AllEnvoyConfigType), + expect: true, + }, { name: "default-resources", from: "gateway-api",