diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f26d96333..5dd7624e01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: - name: Run Tests run: make cover - name: Upload coverage to Codecov - uses: codecov/codecov-action@40a12dcee2df644d47232dde008099a3e9e4f865 # v3.1.2 + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 with: files: ./coverage.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1d3ff8f32c..75f0a8aa75 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,7 +45,7 @@ repos: - id: golangci-lint args: [--new-from-patch=/tmp/diff.patch] - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.3.2 hooks: - id: pyupgrade - repo: https://github.com/PyCQA/isort diff --git a/deployments/common/crds/k8s.nginx.org_virtualservers.yaml b/deployments/common/crds/k8s.nginx.org_virtualservers.yaml index 968573fae7..c5015904fd 100644 --- a/deployments/common/crds/k8s.nginx.org_virtualservers.yaml +++ b/deployments/common/crds/k8s.nginx.org_virtualservers.yaml @@ -86,6 +86,8 @@ spec: format: int64 recordType: type: string + gunzip: + type: string host: type: string http-snippets: diff --git a/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml b/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml index 968573fae7..c5015904fd 100644 --- a/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml +++ b/deployments/helm-chart/crds/k8s.nginx.org_virtualservers.yaml @@ -86,6 +86,8 @@ spec: format: int64 recordType: type: string + gunzip: + type: string host: type: string http-snippets: diff --git a/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md b/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md index dab909e831..412cf848e6 100644 --- a/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md +++ b/docs/content/configuration/virtualserver-and-virtualserverroute-resources.md @@ -26,6 +26,7 @@ spec: host: cafe.example.com tls: secret: cafe-secret + gunzip: on upstreams: - name: tea service: tea-svc @@ -53,6 +54,7 @@ spec: | ---| ---| ---| --- | |``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. When using a wildcard domain like ``*.example.com`` the domain must be contained in double quotes. The ``host`` value needs to be unique among all Ingress and VirtualServer resources. See also [Handling Host and Listener Collisions](/nginx-ingress-controller/configuration/handling-host-and-listener-collisions). | ``string`` | Yes | |``tls`` | The TLS termination configuration. | [tls](#virtualservertls) | No | +|``gunzip`` | Enables or disables [decompression](https://docs.nginx.com/nginx/admin-guide/web-server/compression/) of gzipped responses for clients. Allowed values are: "on" or "off". If the ``gunzip`` value is not set, it defaults to ``off``. | ``string`` | No | |``externalDNS`` | The externalDNS configuration for a VirtualServer. | [externalDNS](#virtualserverexternaldns) | No | |``dos`` | A reference to a DosProtectedResource, setting this enables DOS protection of the VirtualServer. | ``string`` | No | |``policies`` | A list of policies. | [[]policy](#virtualserverpolicy) | No | diff --git a/internal/configs/version2/http.go b/internal/configs/version2/http.go index 8da6938a79..3152d51e2d 100644 --- a/internal/configs/version2/http.go +++ b/internal/configs/version2/http.go @@ -79,6 +79,7 @@ type Server struct { VSNamespace string VSName string DisableIPV6 bool + Gunzip string } // SSL defines SSL configuration for a server. diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl index 72648d11f9..973b4a5852 100644 --- a/internal/configs/version2/nginx-plus.virtualserver.tmpl +++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl @@ -68,6 +68,7 @@ match {{ $m.Name }} { {{ end }} server { + {{ if (eq $s.Gunzip "on") }}gunzip {{ $s.Gunzip }};{{end}} listen 80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }}; {{ if not $s.DisableIPV6 }}listen [::]:80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }};{{ end }} diff --git a/internal/configs/version2/nginx.virtualserver.tmpl b/internal/configs/version2/nginx.virtualserver.tmpl index 0ea8e47d51..1b03b44df5 100644 --- a/internal/configs/version2/nginx.virtualserver.tmpl +++ b/internal/configs/version2/nginx.virtualserver.tmpl @@ -40,6 +40,7 @@ limit_req_zone {{ $z.Key }} zone={{ $z.ZoneName }}:{{ $z.ZoneSize }} rate={{ $z. {{ $s := .Server }} server { + {{ if (eq $s.Gunzip "on") }}gunzip {{ $s.Gunzip }};{{end}} listen 80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }}; {{ if not $s.DisableIPV6 }}listen [::]:80{{ if $s.ProxyProtocol }} proxy_protocol{{ end }};{{ end }} diff --git a/internal/configs/version2/templates_test.go b/internal/configs/version2/templates_test.go index 534d182ed6..e2af009e90 100644 --- a/internal/configs/version2/templates_test.go +++ b/internal/configs/version2/templates_test.go @@ -11,797 +11,73 @@ const ( nginxTransportServerTmpl = "nginx.transportserver.tmpl" ) -var virtualServerCfg = VirtualServerConfig{ - LimitReqZones: []LimitReqZone{ - { - ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", - }, - }, - Upstreams: []Upstream{ - { - Name: "test-upstream", - Servers: []UpstreamServer{ - { - Address: "10.0.0.20:8001", - }, - }, - LBMethod: "random", - Keepalive: 32, - MaxFails: 4, - FailTimeout: "10s", - MaxConns: 31, - SlowStart: "10s", - UpstreamZoneSize: "256k", - Queue: &Queue{Size: 10, Timeout: "60s"}, - SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, - NTLM: true, - }, - { - Name: "coffee-v1", - Servers: []UpstreamServer{ - { - Address: "10.0.0.31:8001", - }, - }, - MaxFails: 8, - FailTimeout: "15s", - MaxConns: 2, - UpstreamZoneSize: "256k", - }, - { - Name: "coffee-v2", - Servers: []UpstreamServer{ - { - Address: "10.0.0.32:8001", - }, - }, - MaxFails: 12, - FailTimeout: "20s", - MaxConns: 4, - UpstreamZoneSize: "256k", - }, - }, - SplitClients: []SplitClient{ - { - Source: "$request_id", - Variable: "$split_0", - Distributions: []Distribution{ - { - Weight: "50%", - Value: "@loc0", - }, - { - Weight: "50%", - Value: "@loc1", - }, - }, - }, - }, - Maps: []Map{ - { - Source: "$match_0_0", - Variable: "$match", - Parameters: []Parameter{ - { - Value: "~^1", - Result: "@match_loc_0", - }, - { - Value: "default", - Result: "@match_loc_default", - }, - }, - }, - { - Source: "$http_x_version", - Variable: "$match_0_0", - Parameters: []Parameter{ - { - Value: "v2", - Result: "1", - }, - { - Value: "default", - Result: "0", - }, - }, - }, - }, - HTTPSnippets: []string{"# HTTP snippet"}, - Server: Server{ - ServerName: "example.com", - StatusZone: "example.com", - ProxyProtocol: true, - SSL: &SSL{ - HTTP2: true, - Certificate: "cafe-secret.pem", - CertificateKey: "cafe-secret.pem", - }, - TLSRedirect: &TLSRedirect{ - BasedOn: "$scheme", - Code: 301, - }, - ServerTokens: "off", - SetRealIPFrom: []string{"0.0.0.0/0"}, - RealIPHeader: "X-Real-IP", - RealIPRecursive: true, - Allow: []string{"127.0.0.1"}, - Deny: []string{"127.0.0.1"}, - LimitReqs: []LimitReq{ - { - ZoneName: "pol_rl_test_test_test", - Delay: 10, - Burst: 5, - }, - }, - LimitReqOptions: LimitReqOptions{ - LogLevel: "error", - RejectCode: 503, - }, - JWTAuth: &JWTAuth{ - Realm: "My Api", - Secret: "jwk-secret", - }, - IngressMTLS: &IngressMTLS{ - ClientCert: "ingress-mtls-secret", - VerifyClient: "on", - VerifyDepth: 2, - }, - WAF: &WAF{ - ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", - ApSecurityLogEnable: true, - ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, - }, - Snippets: []string{"# server snippet"}, - InternalRedirectLocations: []InternalRedirectLocation{ - { - Path: "/split", - Destination: "@split_0", - }, - { - Path: "/coffee", - Destination: "@match", - }, - }, - HealthChecks: []HealthCheck{ - { - Name: "coffee", - URI: "/", - Interval: "5s", - Jitter: "0s", - Fails: 1, - Passes: 1, - Port: 50, - ProxyPass: "http://coffee-v2", - Mandatory: true, - Persistent: true, - }, - { - Name: "tea", - Interval: "5s", - Jitter: "0s", - Fails: 1, - Passes: 1, - Port: 50, - ProxyPass: "http://tea-v2", - GRPCPass: "grpc://tea-v3", - GRPCStatus: createPointerFromInt(12), - GRPCService: "tea-servicev2", - }, - }, - Locations: []Location{ - { - Path: "/", - Snippets: []string{"# location snippet"}, - Allow: []string{"127.0.0.1"}, - Deny: []string{"127.0.0.1"}, - LimitReqs: []LimitReq{ - { - ZoneName: "loc_pol_rl_test_test_test", - }, - }, - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyBuffering: true, - ProxyBuffers: "8 4k", - ProxyBufferSize: "4k", - ProxyMaxTempFileSize: "1024m", - ProxyPass: "http://test-upstream", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - Internal: true, - ProxyPassRequestHeaders: false, - ProxyPassHeaders: []string{"Host"}, - ProxyPassRewrite: "$request_uri", - ProxyHideHeaders: []string{"Header"}, - ProxyIgnoreHeaders: "Cache", - Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, - AddHeaders: []AddHeader{ - { - Header: Header{ - Name: "Header-Name", - Value: "Header Value", - }, - Always: true, - }, - }, - EgressMTLS: &EgressMTLS{ - Certificate: "egress-mtls-secret.pem", - CertificateKey: "egress-mtls-secret.pem", - VerifyServer: true, - VerifyDepth: 1, - Ciphers: "DEFAULT", - Protocols: "TLSv1.3", - TrustedCert: "trusted-cert.pem", - SessionReuse: true, - ServerName: true, - }, - }, - { - Path: "@loc0", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v1", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - ProxyInterceptErrors: true, - ErrorPages: []ErrorPage{ - { - Name: "@error_page_1", - Codes: "400 500", - ResponseCode: 200, - }, - { - Name: "@error_page_2", - Codes: "500", - ResponseCode: 0, - }, - }, - }, - { - Path: "@loc1", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v2", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - }, - { - Path: "@loc2", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v2", - GRPCPass: "grpc://coffee-v3", - }, - { - Path: "@match_loc_0", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v2", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - }, - { - Path: "@match_loc_default", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v1", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - }, - { - Path: "/return", - ProxyInterceptErrors: true, - ErrorPages: []ErrorPage{ - { - Name: "@return_0", - Codes: "418", - ResponseCode: 200, - }, - }, - InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", - }, - }, - ErrorPageLocations: []ErrorPageLocation{ - { - Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", - DefaultType: "application/json", - Return: &Return{ - Code: 200, - Text: "Hello World", - }, - Headers: nil, - }, - { - Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", - DefaultType: "", - Return: &Return{ - Code: 200, - Text: "Hello World", - }, - Headers: []Header{ - { - Name: "Set-Cookie", - Value: "cookie1=test", - }, - { - Name: "Set-Cookie", - Value: "cookie2=test; Secure", - }, - }, - }, - }, - ReturnLocations: []ReturnLocation{ - { - Name: "@return_0", - DefaultType: "text/html", - Return: Return{ - Code: 200, - Text: "Hello!", - }, - }, - }, - }, +func createPointerFromInt(n int) *int { + return &n } -var virtualServerCfgWithWAFApBundle = VirtualServerConfig{ - LimitReqZones: []LimitReqZone{ - { - ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", - }, - }, - Upstreams: []Upstream{ - { - Name: "test-upstream", - Servers: []UpstreamServer{ - { - Address: "10.0.0.20:8001", - }, - }, - LBMethod: "random", - Keepalive: 32, - MaxFails: 4, - FailTimeout: "10s", - MaxConns: 31, - SlowStart: "10s", - UpstreamZoneSize: "256k", - Queue: &Queue{Size: 10, Timeout: "60s"}, - SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, - NTLM: true, - }, - { - Name: "coffee-v1", - Servers: []UpstreamServer{ - { - Address: "10.0.0.31:8001", - }, - }, - MaxFails: 8, - FailTimeout: "15s", - MaxConns: 2, - UpstreamZoneSize: "256k", - }, - { - Name: "coffee-v2", - Servers: []UpstreamServer{ - { - Address: "10.0.0.32:8001", - }, - }, - MaxFails: 12, - FailTimeout: "20s", - MaxConns: 4, - UpstreamZoneSize: "256k", - }, - }, - SplitClients: []SplitClient{ - { - Source: "$request_id", - Variable: "$split_0", - Distributions: []Distribution{ - { - Weight: "50%", - Value: "@loc0", - }, - { - Weight: "50%", - Value: "@loc1", - }, - }, - }, - }, - Maps: []Map{ - { - Source: "$match_0_0", - Variable: "$match", - Parameters: []Parameter{ - { - Value: "~^1", - Result: "@match_loc_0", - }, - { - Value: "default", - Result: "@match_loc_default", - }, - }, - }, - { - Source: "$http_x_version", - Variable: "$match_0_0", - Parameters: []Parameter{ - { - Value: "v2", - Result: "1", - }, - { - Value: "default", - Result: "0", - }, - }, - }, - }, - HTTPSnippets: []string{"# HTTP snippet"}, - Server: Server{ - ServerName: "example.com", - StatusZone: "example.com", - ProxyProtocol: true, - SSL: &SSL{ - HTTP2: true, - Certificate: "cafe-secret.pem", - CertificateKey: "cafe-secret.pem", - }, - TLSRedirect: &TLSRedirect{ - BasedOn: "$scheme", - Code: 301, - }, - ServerTokens: "off", - SetRealIPFrom: []string{"0.0.0.0/0"}, - RealIPHeader: "X-Real-IP", - RealIPRecursive: true, - Allow: []string{"127.0.0.1"}, - Deny: []string{"127.0.0.1"}, - LimitReqs: []LimitReq{ - { - ZoneName: "pol_rl_test_test_test", - Delay: 10, - Burst: 5, - }, - }, - LimitReqOptions: LimitReqOptions{ - LogLevel: "error", - RejectCode: 503, - }, - JWTAuth: &JWTAuth{ - Realm: "My Api", - Secret: "jwk-secret", - }, - IngressMTLS: &IngressMTLS{ - ClientCert: "ingress-mtls-secret", - VerifyClient: "on", - VerifyDepth: 2, - }, - WAF: &WAF{ - ApBundle: "/etc/nginx/waf/bundles/NginxDefaultPolicy.tgz", - ApSecurityLogEnable: true, - ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, - }, - Snippets: []string{"# server snippet"}, - InternalRedirectLocations: []InternalRedirectLocation{ - { - Path: "/split", - Destination: "@split_0", - }, - { - Path: "/coffee", - Destination: "@match", - }, - }, - HealthChecks: []HealthCheck{ - { - Name: "coffee", - URI: "/", - Interval: "5s", - Jitter: "0s", - Fails: 1, - Passes: 1, - Port: 50, - ProxyPass: "http://coffee-v2", - Mandatory: true, - Persistent: true, - }, - { - Name: "tea", - Interval: "5s", - Jitter: "0s", - Fails: 1, - Passes: 1, - Port: 50, - ProxyPass: "http://tea-v2", - GRPCPass: "grpc://tea-v3", - GRPCStatus: createPointerFromInt(12), - GRPCService: "tea-servicev2", - }, - }, - Locations: []Location{ - { - Path: "/", - Snippets: []string{"# location snippet"}, - Allow: []string{"127.0.0.1"}, - Deny: []string{"127.0.0.1"}, - LimitReqs: []LimitReq{ - { - ZoneName: "loc_pol_rl_test_test_test", - }, - }, - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyBuffering: true, - ProxyBuffers: "8 4k", - ProxyBufferSize: "4k", - ProxyMaxTempFileSize: "1024m", - ProxyPass: "http://test-upstream", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - Internal: true, - ProxyPassRequestHeaders: false, - ProxyPassHeaders: []string{"Host"}, - ProxyPassRewrite: "$request_uri", - ProxyHideHeaders: []string{"Header"}, - ProxyIgnoreHeaders: "Cache", - Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, - AddHeaders: []AddHeader{ - { - Header: Header{ - Name: "Header-Name", - Value: "Header Value", - }, - Always: true, - }, - }, - EgressMTLS: &EgressMTLS{ - Certificate: "egress-mtls-secret.pem", - CertificateKey: "egress-mtls-secret.pem", - VerifyServer: true, - VerifyDepth: 1, - Ciphers: "DEFAULT", - Protocols: "TLSv1.3", - TrustedCert: "trusted-cert.pem", - SessionReuse: true, - ServerName: true, - }, - }, - { - Path: "@loc0", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v1", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - ProxyInterceptErrors: true, - ErrorPages: []ErrorPage{ - { - Name: "@error_page_1", - Codes: "400 500", - ResponseCode: 200, - }, - { - Name: "@error_page_2", - Codes: "500", - ResponseCode: 0, - }, - }, - }, - { - Path: "@loc1", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v2", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - }, - { - Path: "@loc2", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v2", - GRPCPass: "grpc://coffee-v3", - }, - { - Path: "@match_loc_0", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v2", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - }, - { - Path: "@match_loc_default", - ProxyConnectTimeout: "30s", - ProxyReadTimeout: "31s", - ProxySendTimeout: "32s", - ClientMaxBodySize: "1m", - ProxyPass: "http://coffee-v1", - ProxyNextUpstream: "error timeout", - ProxyNextUpstreamTimeout: "5s", - }, - { - Path: "/return", - ProxyInterceptErrors: true, - ErrorPages: []ErrorPage{ - { - Name: "@return_0", - Codes: "418", - ResponseCode: 200, - }, - }, - InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", - }, - }, - ErrorPageLocations: []ErrorPageLocation{ - { - Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", - DefaultType: "application/json", - Return: &Return{ - Code: 200, - Text: "Hello World", - }, - Headers: nil, - }, - { - Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", - DefaultType: "", - Return: &Return{ - Code: 200, - Text: "Hello World", - }, - Headers: []Header{ - { - Name: "Set-Cookie", - Value: "cookie1=test", - }, - { - Name: "Set-Cookie", - Value: "cookie2=test; Secure", - }, - }, - }, - }, - ReturnLocations: []ReturnLocation{ - { - Name: "@return_0", - DefaultType: "text/html", - Return: Return{ - Code: 200, - Text: "Hello!", - }, - }, - }, - }, +func TestVirtualServerForNginxPlus(t *testing.T) { + t.Parallel() + executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl) + if err != nil { + t.Fatalf("Failed to create template executor: %v", err) + } + data, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfg) + if err != nil { + t.Errorf("Failed to execute template: %v", err) + } + t.Log(string(data)) } -var transportServerCfg = TransportServerConfig{ - Upstreams: []StreamUpstream{ - { - Name: "udp-upstream", - Servers: []StreamUpstreamServer{ - { - Address: "10.0.0.20:5001", - }, - }, - }, - }, - Match: &Match{ - Name: "match_udp-upstream", - Send: `GET / HTTP/1.0\r\nHost: localhost\r\n\r\n`, - ExpectRegexModifier: "~*", - Expect: "200 OK", - }, - Server: StreamServer{ - Port: 1234, - UDP: true, - StatusZone: "udp-app", - ProxyRequests: createPointerFromInt(1), - ProxyResponses: createPointerFromInt(2), - ProxyPass: "udp-upstream", - ProxyTimeout: "10s", - ProxyConnectTimeout: "10s", - ProxyNextUpstream: true, - ProxyNextUpstreamTimeout: "10s", - ProxyNextUpstreamTries: 5, - HealthCheck: &StreamHealthCheck{ - Enabled: false, - Timeout: "5s", - Jitter: "0", - Port: 8080, - Interval: "5s", - Passes: 1, - Fails: 1, - Match: "match_udp-upstream", - }, - }, +func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipOn(t *testing.T) { + t.Parallel() + executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl) + if err != nil { + t.Fatal(err) + } + got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithGunzipOn) + if err != nil { + t.Error(err) + } + t.Log(string(got)) } -var transportServerCfgWithResolver = TransportServerConfig{ - Upstreams: []StreamUpstream{ - { - Name: "udp-upstream", - Servers: []StreamUpstreamServer{ - { - Address: "10.0.0.20:5001", - }, - }, - Resolve: true, - }, - }, - Match: &Match{ - Name: "match_udp-upstream", - Send: `GET / HTTP/1.0\r\nHost: localhost\r\n\r\n`, - ExpectRegexModifier: "~*", - Expect: "200 OK", - }, - Server: StreamServer{ - Port: 1234, - UDP: true, - StatusZone: "udp-app", - ProxyRequests: createPointerFromInt(1), - ProxyResponses: createPointerFromInt(2), - ProxyPass: "udp-upstream", - ProxyTimeout: "10s", - ProxyConnectTimeout: "10s", - ProxyNextUpstream: true, - ProxyNextUpstreamTimeout: "10s", - ProxyNextUpstreamTries: 5, - HealthCheck: &StreamHealthCheck{ - Enabled: false, - Timeout: "5s", - Jitter: "0", - Port: 8080, - Interval: "5s", - Passes: 1, - Fails: 1, - Match: "match_udp-upstream", - }, - }, +func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipOff(t *testing.T) { + t.Parallel() + executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl) + if err != nil { + t.Fatal(err) + } + got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithGunzipOff) + if err != nil { + t.Error(err) + } + t.Log(string(got)) } -func createPointerFromInt(n int) *int { - return &n +func TestExecuteVirtualServerTemplate_RendersTemplateWithServerGunzipEmpty(t *testing.T) { + t.Parallel() + executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl) + if err != nil { + t.Fatal(err) + } + got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithEmptyGunzip) + if err != nil { + t.Error(err) + } + t.Log(string(got)) } -func TestVirtualServerForNginxPlus(t *testing.T) { +func TestExecuteVirtualServerTemplate_RendersTemplateWithoutServerGunzip(t *testing.T) { t.Parallel() executor, err := NewTemplateExecutor(nginxPlusVirtualServerTmpl, nginxPlusTransportServerTmpl) if err != nil { - t.Fatalf("Failed to create template executor: %v", err) + t.Fatal(err) } - - data, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfg) + got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfg) if err != nil { - t.Errorf("Failed to execute template: %v", err) + t.Error(err) } - t.Log(string(data)) + t.Log(string(got)) } func TestVirtualServerForNginxPlusWithWAFApBundle(t *testing.T) { @@ -810,7 +86,6 @@ func TestVirtualServerForNginxPlusWithWAFApBundle(t *testing.T) { if err != nil { t.Fatalf("Failed to create template executor: %v", err) } - data, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithWAFApBundle) if err != nil { t.Errorf("Failed to execute template: %v", err) @@ -889,3 +164,1818 @@ func TestTLSPassthroughHosts(t *testing.T) { } t.Log(string(data)) } + +var ( + virtualServerCfg = VirtualServerConfig{ + LimitReqZones: []LimitReqZone{ + { + ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", + }, + }, + Upstreams: []Upstream{ + { + Name: "test-upstream", + Servers: []UpstreamServer{ + { + Address: "10.0.0.20:8001", + }, + }, + LBMethod: "random", + Keepalive: 32, + MaxFails: 4, + FailTimeout: "10s", + MaxConns: 31, + SlowStart: "10s", + UpstreamZoneSize: "256k", + Queue: &Queue{Size: 10, Timeout: "60s"}, + SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, + NTLM: true, + }, + { + Name: "coffee-v1", + Servers: []UpstreamServer{ + { + Address: "10.0.0.31:8001", + }, + }, + MaxFails: 8, + FailTimeout: "15s", + MaxConns: 2, + UpstreamZoneSize: "256k", + }, + { + Name: "coffee-v2", + Servers: []UpstreamServer{ + { + Address: "10.0.0.32:8001", + }, + }, + MaxFails: 12, + FailTimeout: "20s", + MaxConns: 4, + UpstreamZoneSize: "256k", + }, + }, + SplitClients: []SplitClient{ + { + Source: "$request_id", + Variable: "$split_0", + Distributions: []Distribution{ + { + Weight: "50%", + Value: "@loc0", + }, + { + Weight: "50%", + Value: "@loc1", + }, + }, + }, + }, + Maps: []Map{ + { + Source: "$match_0_0", + Variable: "$match", + Parameters: []Parameter{ + { + Value: "~^1", + Result: "@match_loc_0", + }, + { + Value: "default", + Result: "@match_loc_default", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$match_0_0", + Parameters: []Parameter{ + { + Value: "v2", + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + }, + HTTPSnippets: []string{"# HTTP snippet"}, + Server: Server{ + ServerName: "example.com", + StatusZone: "example.com", + ProxyProtocol: true, + SSL: &SSL{ + HTTP2: true, + Certificate: "cafe-secret.pem", + CertificateKey: "cafe-secret.pem", + }, + TLSRedirect: &TLSRedirect{ + BasedOn: "$scheme", + Code: 301, + }, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "pol_rl_test_test_test", + Delay: 10, + Burst: 5, + }, + }, + LimitReqOptions: LimitReqOptions{ + LogLevel: "error", + RejectCode: 503, + }, + JWTAuth: &JWTAuth{ + Realm: "My Api", + Secret: "jwk-secret", + }, + IngressMTLS: &IngressMTLS{ + ClientCert: "ingress-mtls-secret", + VerifyClient: "on", + VerifyDepth: 2, + }, + WAF: &WAF{ + ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", + ApSecurityLogEnable: true, + ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, + }, + Snippets: []string{"# server snippet"}, + InternalRedirectLocations: []InternalRedirectLocation{ + { + Path: "/split", + Destination: "@split_0", + }, + { + Path: "/coffee", + Destination: "@match", + }, + }, + HealthChecks: []HealthCheck{ + { + Name: "coffee", + URI: "/", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://coffee-v2", + Mandatory: true, + Persistent: true, + }, + { + Name: "tea", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://tea-v2", + GRPCPass: "grpc://tea-v3", + GRPCStatus: createPointerFromInt(12), + GRPCService: "tea-servicev2", + }, + }, + Locations: []Location{ + { + Path: "/", + Snippets: []string{"# location snippet"}, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "loc_pol_rl_test_test_test", + }, + }, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyMaxTempFileSize: "1024m", + ProxyPass: "http://test-upstream", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + Internal: true, + ProxyPassRequestHeaders: false, + ProxyPassHeaders: []string{"Host"}, + ProxyPassRewrite: "$request_uri", + ProxyHideHeaders: []string{"Header"}, + ProxyIgnoreHeaders: "Cache", + Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, + AddHeaders: []AddHeader{ + { + Header: Header{ + Name: "Header-Name", + Value: "Header Value", + }, + Always: true, + }, + }, + EgressMTLS: &EgressMTLS{ + Certificate: "egress-mtls-secret.pem", + CertificateKey: "egress-mtls-secret.pem", + VerifyServer: true, + VerifyDepth: 1, + Ciphers: "DEFAULT", + Protocols: "TLSv1.3", + TrustedCert: "trusted-cert.pem", + SessionReuse: true, + ServerName: true, + }, + }, + { + Path: "@loc0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@error_page_1", + Codes: "400 500", + ResponseCode: 200, + }, + { + Name: "@error_page_2", + Codes: "500", + ResponseCode: 0, + }, + }, + }, + { + Path: "@loc1", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@loc2", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + GRPCPass: "grpc://coffee-v3", + }, + { + Path: "@match_loc_0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@match_loc_default", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "/return", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@return_0", + Codes: "418", + ResponseCode: 200, + }, + }, + InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", + }, + }, + ErrorPageLocations: []ErrorPageLocation{ + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", + DefaultType: "application/json", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: nil, + }, + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", + DefaultType: "", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: []Header{ + { + Name: "Set-Cookie", + Value: "cookie1=test", + }, + { + Name: "Set-Cookie", + Value: "cookie2=test; Secure", + }, + }, + }, + }, + ReturnLocations: []ReturnLocation{ + { + Name: "@return_0", + DefaultType: "text/html", + Return: Return{ + Code: 200, + Text: "Hello!", + }, + }, + }, + }, + } + + virtualServerCfgWithGunzipOn = VirtualServerConfig{ + LimitReqZones: []LimitReqZone{ + { + ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", + }, + }, + Upstreams: []Upstream{ + { + Name: "test-upstream", + Servers: []UpstreamServer{ + { + Address: "10.0.0.20:8001", + }, + }, + LBMethod: "random", + Keepalive: 32, + MaxFails: 4, + FailTimeout: "10s", + MaxConns: 31, + SlowStart: "10s", + UpstreamZoneSize: "256k", + Queue: &Queue{Size: 10, Timeout: "60s"}, + SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, + NTLM: true, + }, + { + Name: "coffee-v1", + Servers: []UpstreamServer{ + { + Address: "10.0.0.31:8001", + }, + }, + MaxFails: 8, + FailTimeout: "15s", + MaxConns: 2, + UpstreamZoneSize: "256k", + }, + { + Name: "coffee-v2", + Servers: []UpstreamServer{ + { + Address: "10.0.0.32:8001", + }, + }, + MaxFails: 12, + FailTimeout: "20s", + MaxConns: 4, + UpstreamZoneSize: "256k", + }, + }, + SplitClients: []SplitClient{ + { + Source: "$request_id", + Variable: "$split_0", + Distributions: []Distribution{ + { + Weight: "50%", + Value: "@loc0", + }, + { + Weight: "50%", + Value: "@loc1", + }, + }, + }, + }, + Maps: []Map{ + { + Source: "$match_0_0", + Variable: "$match", + Parameters: []Parameter{ + { + Value: "~^1", + Result: "@match_loc_0", + }, + { + Value: "default", + Result: "@match_loc_default", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$match_0_0", + Parameters: []Parameter{ + { + Value: "v2", + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + }, + HTTPSnippets: []string{"# HTTP snippet"}, + Server: Server{ + ServerName: "example.com", + StatusZone: "example.com", + ProxyProtocol: true, + SSL: &SSL{ + HTTP2: true, + Certificate: "cafe-secret.pem", + CertificateKey: "cafe-secret.pem", + }, + TLSRedirect: &TLSRedirect{ + BasedOn: "$scheme", + Code: 301, + }, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "pol_rl_test_test_test", + Delay: 10, + Burst: 5, + }, + }, + LimitReqOptions: LimitReqOptions{ + LogLevel: "error", + RejectCode: 503, + }, + JWTAuth: &JWTAuth{ + Realm: "My Api", + Secret: "jwk-secret", + }, + IngressMTLS: &IngressMTLS{ + ClientCert: "ingress-mtls-secret", + VerifyClient: "on", + VerifyDepth: 2, + }, + WAF: &WAF{ + ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", + ApSecurityLogEnable: true, + ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, + }, + Snippets: []string{"# server snippet"}, + InternalRedirectLocations: []InternalRedirectLocation{ + { + Path: "/split", + Destination: "@split_0", + }, + { + Path: "/coffee", + Destination: "@match", + }, + }, + HealthChecks: []HealthCheck{ + { + Name: "coffee", + URI: "/", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://coffee-v2", + Mandatory: true, + Persistent: true, + }, + { + Name: "tea", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://tea-v2", + GRPCPass: "grpc://tea-v3", + GRPCStatus: createPointerFromInt(12), + GRPCService: "tea-servicev2", + }, + }, + Locations: []Location{ + { + Path: "/", + Snippets: []string{"# location snippet"}, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "loc_pol_rl_test_test_test", + }, + }, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyMaxTempFileSize: "1024m", + ProxyPass: "http://test-upstream", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + Internal: true, + ProxyPassRequestHeaders: false, + ProxyPassHeaders: []string{"Host"}, + ProxyPassRewrite: "$request_uri", + ProxyHideHeaders: []string{"Header"}, + ProxyIgnoreHeaders: "Cache", + Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, + AddHeaders: []AddHeader{ + { + Header: Header{ + Name: "Header-Name", + Value: "Header Value", + }, + Always: true, + }, + }, + EgressMTLS: &EgressMTLS{ + Certificate: "egress-mtls-secret.pem", + CertificateKey: "egress-mtls-secret.pem", + VerifyServer: true, + VerifyDepth: 1, + Ciphers: "DEFAULT", + Protocols: "TLSv1.3", + TrustedCert: "trusted-cert.pem", + SessionReuse: true, + ServerName: true, + }, + }, + { + Path: "@loc0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@error_page_1", + Codes: "400 500", + ResponseCode: 200, + }, + { + Name: "@error_page_2", + Codes: "500", + ResponseCode: 0, + }, + }, + }, + { + Path: "@loc1", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@loc2", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + GRPCPass: "grpc://coffee-v3", + }, + { + Path: "@match_loc_0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@match_loc_default", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "/return", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@return_0", + Codes: "418", + ResponseCode: 200, + }, + }, + InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", + }, + }, + ErrorPageLocations: []ErrorPageLocation{ + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", + DefaultType: "application/json", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: nil, + }, + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", + DefaultType: "", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: []Header{ + { + Name: "Set-Cookie", + Value: "cookie1=test", + }, + { + Name: "Set-Cookie", + Value: "cookie2=test; Secure", + }, + }, + }, + }, + ReturnLocations: []ReturnLocation{ + { + Name: "@return_0", + DefaultType: "text/html", + Return: Return{ + Code: 200, + Text: "Hello!", + }, + }, + }, + Gunzip: "on", + }, + } + + virtualServerCfgWithGunzipOff = VirtualServerConfig{ + LimitReqZones: []LimitReqZone{ + { + ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", + }, + }, + Upstreams: []Upstream{ + { + Name: "test-upstream", + Servers: []UpstreamServer{ + { + Address: "10.0.0.20:8001", + }, + }, + LBMethod: "random", + Keepalive: 32, + MaxFails: 4, + FailTimeout: "10s", + MaxConns: 31, + SlowStart: "10s", + UpstreamZoneSize: "256k", + Queue: &Queue{Size: 10, Timeout: "60s"}, + SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, + NTLM: true, + }, + { + Name: "coffee-v1", + Servers: []UpstreamServer{ + { + Address: "10.0.0.31:8001", + }, + }, + MaxFails: 8, + FailTimeout: "15s", + MaxConns: 2, + UpstreamZoneSize: "256k", + }, + { + Name: "coffee-v2", + Servers: []UpstreamServer{ + { + Address: "10.0.0.32:8001", + }, + }, + MaxFails: 12, + FailTimeout: "20s", + MaxConns: 4, + UpstreamZoneSize: "256k", + }, + }, + SplitClients: []SplitClient{ + { + Source: "$request_id", + Variable: "$split_0", + Distributions: []Distribution{ + { + Weight: "50%", + Value: "@loc0", + }, + { + Weight: "50%", + Value: "@loc1", + }, + }, + }, + }, + Maps: []Map{ + { + Source: "$match_0_0", + Variable: "$match", + Parameters: []Parameter{ + { + Value: "~^1", + Result: "@match_loc_0", + }, + { + Value: "default", + Result: "@match_loc_default", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$match_0_0", + Parameters: []Parameter{ + { + Value: "v2", + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + }, + HTTPSnippets: []string{"# HTTP snippet"}, + Server: Server{ + ServerName: "example.com", + StatusZone: "example.com", + ProxyProtocol: true, + SSL: &SSL{ + HTTP2: true, + Certificate: "cafe-secret.pem", + CertificateKey: "cafe-secret.pem", + }, + TLSRedirect: &TLSRedirect{ + BasedOn: "$scheme", + Code: 301, + }, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "pol_rl_test_test_test", + Delay: 10, + Burst: 5, + }, + }, + LimitReqOptions: LimitReqOptions{ + LogLevel: "error", + RejectCode: 503, + }, + JWTAuth: &JWTAuth{ + Realm: "My Api", + Secret: "jwk-secret", + }, + IngressMTLS: &IngressMTLS{ + ClientCert: "ingress-mtls-secret", + VerifyClient: "on", + VerifyDepth: 2, + }, + WAF: &WAF{ + ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", + ApSecurityLogEnable: true, + ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, + }, + Snippets: []string{"# server snippet"}, + InternalRedirectLocations: []InternalRedirectLocation{ + { + Path: "/split", + Destination: "@split_0", + }, + { + Path: "/coffee", + Destination: "@match", + }, + }, + HealthChecks: []HealthCheck{ + { + Name: "coffee", + URI: "/", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://coffee-v2", + Mandatory: true, + Persistent: true, + }, + { + Name: "tea", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://tea-v2", + GRPCPass: "grpc://tea-v3", + GRPCStatus: createPointerFromInt(12), + GRPCService: "tea-servicev2", + }, + }, + Locations: []Location{ + { + Path: "/", + Snippets: []string{"# location snippet"}, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "loc_pol_rl_test_test_test", + }, + }, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyMaxTempFileSize: "1024m", + ProxyPass: "http://test-upstream", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + Internal: true, + ProxyPassRequestHeaders: false, + ProxyPassHeaders: []string{"Host"}, + ProxyPassRewrite: "$request_uri", + ProxyHideHeaders: []string{"Header"}, + ProxyIgnoreHeaders: "Cache", + Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, + AddHeaders: []AddHeader{ + { + Header: Header{ + Name: "Header-Name", + Value: "Header Value", + }, + Always: true, + }, + }, + EgressMTLS: &EgressMTLS{ + Certificate: "egress-mtls-secret.pem", + CertificateKey: "egress-mtls-secret.pem", + VerifyServer: true, + VerifyDepth: 1, + Ciphers: "DEFAULT", + Protocols: "TLSv1.3", + TrustedCert: "trusted-cert.pem", + SessionReuse: true, + ServerName: true, + }, + }, + { + Path: "@loc0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@error_page_1", + Codes: "400 500", + ResponseCode: 200, + }, + { + Name: "@error_page_2", + Codes: "500", + ResponseCode: 0, + }, + }, + }, + { + Path: "@loc1", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@loc2", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + GRPCPass: "grpc://coffee-v3", + }, + { + Path: "@match_loc_0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@match_loc_default", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "/return", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@return_0", + Codes: "418", + ResponseCode: 200, + }, + }, + InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", + }, + }, + ErrorPageLocations: []ErrorPageLocation{ + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", + DefaultType: "application/json", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: nil, + }, + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", + DefaultType: "", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: []Header{ + { + Name: "Set-Cookie", + Value: "cookie1=test", + }, + { + Name: "Set-Cookie", + Value: "cookie2=test; Secure", + }, + }, + }, + }, + ReturnLocations: []ReturnLocation{ + { + Name: "@return_0", + DefaultType: "text/html", + Return: Return{ + Code: 200, + Text: "Hello!", + }, + }, + }, + Gunzip: "off", + }, + } + + virtualServerCfgWithEmptyGunzip = VirtualServerConfig{ + LimitReqZones: []LimitReqZone{ + { + ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", + }, + }, + Upstreams: []Upstream{ + { + Name: "test-upstream", + Servers: []UpstreamServer{ + { + Address: "10.0.0.20:8001", + }, + }, + LBMethod: "random", + Keepalive: 32, + MaxFails: 4, + FailTimeout: "10s", + MaxConns: 31, + SlowStart: "10s", + UpstreamZoneSize: "256k", + Queue: &Queue{Size: 10, Timeout: "60s"}, + SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, + NTLM: true, + }, + { + Name: "coffee-v1", + Servers: []UpstreamServer{ + { + Address: "10.0.0.31:8001", + }, + }, + MaxFails: 8, + FailTimeout: "15s", + MaxConns: 2, + UpstreamZoneSize: "256k", + }, + { + Name: "coffee-v2", + Servers: []UpstreamServer{ + { + Address: "10.0.0.32:8001", + }, + }, + MaxFails: 12, + FailTimeout: "20s", + MaxConns: 4, + UpstreamZoneSize: "256k", + }, + }, + SplitClients: []SplitClient{ + { + Source: "$request_id", + Variable: "$split_0", + Distributions: []Distribution{ + { + Weight: "50%", + Value: "@loc0", + }, + { + Weight: "50%", + Value: "@loc1", + }, + }, + }, + }, + Maps: []Map{ + { + Source: "$match_0_0", + Variable: "$match", + Parameters: []Parameter{ + { + Value: "~^1", + Result: "@match_loc_0", + }, + { + Value: "default", + Result: "@match_loc_default", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$match_0_0", + Parameters: []Parameter{ + { + Value: "v2", + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + }, + HTTPSnippets: []string{"# HTTP snippet"}, + Server: Server{ + ServerName: "example.com", + StatusZone: "example.com", + ProxyProtocol: true, + SSL: &SSL{ + HTTP2: true, + Certificate: "cafe-secret.pem", + CertificateKey: "cafe-secret.pem", + }, + TLSRedirect: &TLSRedirect{ + BasedOn: "$scheme", + Code: 301, + }, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "pol_rl_test_test_test", + Delay: 10, + Burst: 5, + }, + }, + LimitReqOptions: LimitReqOptions{ + LogLevel: "error", + RejectCode: 503, + }, + JWTAuth: &JWTAuth{ + Realm: "My Api", + Secret: "jwk-secret", + }, + IngressMTLS: &IngressMTLS{ + ClientCert: "ingress-mtls-secret", + VerifyClient: "on", + VerifyDepth: 2, + }, + WAF: &WAF{ + ApPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", + ApSecurityLogEnable: true, + ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, + }, + Snippets: []string{"# server snippet"}, + InternalRedirectLocations: []InternalRedirectLocation{ + { + Path: "/split", + Destination: "@split_0", + }, + { + Path: "/coffee", + Destination: "@match", + }, + }, + HealthChecks: []HealthCheck{ + { + Name: "coffee", + URI: "/", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://coffee-v2", + Mandatory: true, + Persistent: true, + }, + { + Name: "tea", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://tea-v2", + GRPCPass: "grpc://tea-v3", + GRPCStatus: createPointerFromInt(12), + GRPCService: "tea-servicev2", + }, + }, + Locations: []Location{ + { + Path: "/", + Snippets: []string{"# location snippet"}, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "loc_pol_rl_test_test_test", + }, + }, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyMaxTempFileSize: "1024m", + ProxyPass: "http://test-upstream", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + Internal: true, + ProxyPassRequestHeaders: false, + ProxyPassHeaders: []string{"Host"}, + ProxyPassRewrite: "$request_uri", + ProxyHideHeaders: []string{"Header"}, + ProxyIgnoreHeaders: "Cache", + Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, + AddHeaders: []AddHeader{ + { + Header: Header{ + Name: "Header-Name", + Value: "Header Value", + }, + Always: true, + }, + }, + EgressMTLS: &EgressMTLS{ + Certificate: "egress-mtls-secret.pem", + CertificateKey: "egress-mtls-secret.pem", + VerifyServer: true, + VerifyDepth: 1, + Ciphers: "DEFAULT", + Protocols: "TLSv1.3", + TrustedCert: "trusted-cert.pem", + SessionReuse: true, + ServerName: true, + }, + }, + { + Path: "@loc0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@error_page_1", + Codes: "400 500", + ResponseCode: 200, + }, + { + Name: "@error_page_2", + Codes: "500", + ResponseCode: 0, + }, + }, + }, + { + Path: "@loc1", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@loc2", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + GRPCPass: "grpc://coffee-v3", + }, + { + Path: "@match_loc_0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@match_loc_default", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "/return", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@return_0", + Codes: "418", + ResponseCode: 200, + }, + }, + InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", + }, + }, + ErrorPageLocations: []ErrorPageLocation{ + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", + DefaultType: "application/json", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: nil, + }, + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", + DefaultType: "", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: []Header{ + { + Name: "Set-Cookie", + Value: "cookie1=test", + }, + { + Name: "Set-Cookie", + Value: "cookie2=test; Secure", + }, + }, + }, + }, + ReturnLocations: []ReturnLocation{ + { + Name: "@return_0", + DefaultType: "text/html", + Return: Return{ + Code: 200, + Text: "Hello!", + }, + }, + }, + Gunzip: "", + }, + } + + virtualServerCfgWithWAFApBundle = VirtualServerConfig{ + LimitReqZones: []LimitReqZone{ + { + ZoneName: "pol_rl_test_test_test", Rate: "10r/s", ZoneSize: "10m", Key: "$url", + }, + }, + Upstreams: []Upstream{ + { + Name: "test-upstream", + Servers: []UpstreamServer{ + { + Address: "10.0.0.20:8001", + }, + }, + LBMethod: "random", + Keepalive: 32, + MaxFails: 4, + FailTimeout: "10s", + MaxConns: 31, + SlowStart: "10s", + UpstreamZoneSize: "256k", + Queue: &Queue{Size: 10, Timeout: "60s"}, + SessionCookie: &SessionCookie{Enable: true, Name: "test", Path: "/tea", Expires: "25s"}, + NTLM: true, + }, + { + Name: "coffee-v1", + Servers: []UpstreamServer{ + { + Address: "10.0.0.31:8001", + }, + }, + MaxFails: 8, + FailTimeout: "15s", + MaxConns: 2, + UpstreamZoneSize: "256k", + }, + { + Name: "coffee-v2", + Servers: []UpstreamServer{ + { + Address: "10.0.0.32:8001", + }, + }, + MaxFails: 12, + FailTimeout: "20s", + MaxConns: 4, + UpstreamZoneSize: "256k", + }, + }, + SplitClients: []SplitClient{ + { + Source: "$request_id", + Variable: "$split_0", + Distributions: []Distribution{ + { + Weight: "50%", + Value: "@loc0", + }, + { + Weight: "50%", + Value: "@loc1", + }, + }, + }, + }, + Maps: []Map{ + { + Source: "$match_0_0", + Variable: "$match", + Parameters: []Parameter{ + { + Value: "~^1", + Result: "@match_loc_0", + }, + { + Value: "default", + Result: "@match_loc_default", + }, + }, + }, + { + Source: "$http_x_version", + Variable: "$match_0_0", + Parameters: []Parameter{ + { + Value: "v2", + Result: "1", + }, + { + Value: "default", + Result: "0", + }, + }, + }, + }, + HTTPSnippets: []string{"# HTTP snippet"}, + Server: Server{ + ServerName: "example.com", + StatusZone: "example.com", + ProxyProtocol: true, + SSL: &SSL{ + HTTP2: true, + Certificate: "cafe-secret.pem", + CertificateKey: "cafe-secret.pem", + }, + TLSRedirect: &TLSRedirect{ + BasedOn: "$scheme", + Code: 301, + }, + ServerTokens: "off", + SetRealIPFrom: []string{"0.0.0.0/0"}, + RealIPHeader: "X-Real-IP", + RealIPRecursive: true, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "pol_rl_test_test_test", + Delay: 10, + Burst: 5, + }, + }, + LimitReqOptions: LimitReqOptions{ + LogLevel: "error", + RejectCode: 503, + }, + JWTAuth: &JWTAuth{ + Realm: "My Api", + Secret: "jwk-secret", + }, + IngressMTLS: &IngressMTLS{ + ClientCert: "ingress-mtls-secret", + VerifyClient: "on", + VerifyDepth: 2, + }, + WAF: &WAF{ + ApBundle: "/etc/nginx/waf/bundles/NginxDefaultPolicy.tgz", + ApSecurityLogEnable: true, + ApLogConf: []string{"/etc/nginx/waf/nac-logconfs/default-logconf"}, + }, + Snippets: []string{"# server snippet"}, + InternalRedirectLocations: []InternalRedirectLocation{ + { + Path: "/split", + Destination: "@split_0", + }, + { + Path: "/coffee", + Destination: "@match", + }, + }, + HealthChecks: []HealthCheck{ + { + Name: "coffee", + URI: "/", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://coffee-v2", + Mandatory: true, + Persistent: true, + }, + { + Name: "tea", + Interval: "5s", + Jitter: "0s", + Fails: 1, + Passes: 1, + Port: 50, + ProxyPass: "http://tea-v2", + GRPCPass: "grpc://tea-v3", + GRPCStatus: createPointerFromInt(12), + GRPCService: "tea-servicev2", + }, + }, + Locations: []Location{ + { + Path: "/", + Snippets: []string{"# location snippet"}, + Allow: []string{"127.0.0.1"}, + Deny: []string{"127.0.0.1"}, + LimitReqs: []LimitReq{ + { + ZoneName: "loc_pol_rl_test_test_test", + }, + }, + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyBuffering: true, + ProxyBuffers: "8 4k", + ProxyBufferSize: "4k", + ProxyMaxTempFileSize: "1024m", + ProxyPass: "http://test-upstream", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + Internal: true, + ProxyPassRequestHeaders: false, + ProxyPassHeaders: []string{"Host"}, + ProxyPassRewrite: "$request_uri", + ProxyHideHeaders: []string{"Header"}, + ProxyIgnoreHeaders: "Cache", + Rewrites: []string{"$request_uri $request_uri", "$request_uri $request_uri"}, + AddHeaders: []AddHeader{ + { + Header: Header{ + Name: "Header-Name", + Value: "Header Value", + }, + Always: true, + }, + }, + EgressMTLS: &EgressMTLS{ + Certificate: "egress-mtls-secret.pem", + CertificateKey: "egress-mtls-secret.pem", + VerifyServer: true, + VerifyDepth: 1, + Ciphers: "DEFAULT", + Protocols: "TLSv1.3", + TrustedCert: "trusted-cert.pem", + SessionReuse: true, + ServerName: true, + }, + }, + { + Path: "@loc0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@error_page_1", + Codes: "400 500", + ResponseCode: 200, + }, + { + Name: "@error_page_2", + Codes: "500", + ResponseCode: 0, + }, + }, + }, + { + Path: "@loc1", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@loc2", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + GRPCPass: "grpc://coffee-v3", + }, + { + Path: "@match_loc_0", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v2", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "@match_loc_default", + ProxyConnectTimeout: "30s", + ProxyReadTimeout: "31s", + ProxySendTimeout: "32s", + ClientMaxBodySize: "1m", + ProxyPass: "http://coffee-v1", + ProxyNextUpstream: "error timeout", + ProxyNextUpstreamTimeout: "5s", + }, + { + Path: "/return", + ProxyInterceptErrors: true, + ErrorPages: []ErrorPage{ + { + Name: "@return_0", + Codes: "418", + ResponseCode: 200, + }, + }, + InternalProxyPass: "http://unix:/var/lib/nginx/nginx-418-server.sock", + }, + }, + ErrorPageLocations: []ErrorPageLocation{ + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_0", + DefaultType: "application/json", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: nil, + }, + { + Name: "@vs_cafe_cafe_vsr_tea_tea_tea__tea_error_page_1", + DefaultType: "", + Return: &Return{ + Code: 200, + Text: "Hello World", + }, + Headers: []Header{ + { + Name: "Set-Cookie", + Value: "cookie1=test", + }, + { + Name: "Set-Cookie", + Value: "cookie2=test; Secure", + }, + }, + }, + }, + ReturnLocations: []ReturnLocation{ + { + Name: "@return_0", + DefaultType: "text/html", + Return: Return{ + Code: 200, + Text: "Hello!", + }, + }, + }, + }, + } + + transportServerCfg = TransportServerConfig{ + Upstreams: []StreamUpstream{ + { + Name: "udp-upstream", + Servers: []StreamUpstreamServer{ + { + Address: "10.0.0.20:5001", + }, + }, + }, + }, + Match: &Match{ + Name: "match_udp-upstream", + Send: `GET / HTTP/1.0\r\nHost: localhost\r\n\r\n`, + ExpectRegexModifier: "~*", + Expect: "200 OK", + }, + Server: StreamServer{ + Port: 1234, + UDP: true, + StatusZone: "udp-app", + ProxyRequests: createPointerFromInt(1), + ProxyResponses: createPointerFromInt(2), + ProxyPass: "udp-upstream", + ProxyTimeout: "10s", + ProxyConnectTimeout: "10s", + ProxyNextUpstream: true, + ProxyNextUpstreamTimeout: "10s", + ProxyNextUpstreamTries: 5, + HealthCheck: &StreamHealthCheck{ + Enabled: false, + Timeout: "5s", + Jitter: "0", + Port: 8080, + Interval: "5s", + Passes: 1, + Fails: 1, + Match: "match_udp-upstream", + }, + }, + } + + transportServerCfgWithResolver = TransportServerConfig{ + Upstreams: []StreamUpstream{ + { + Name: "udp-upstream", + Servers: []StreamUpstreamServer{ + { + Address: "10.0.0.20:5001", + }, + }, + Resolve: true, + }, + }, + Match: &Match{ + Name: "match_udp-upstream", + Send: `GET / HTTP/1.0\r\nHost: localhost\r\n\r\n`, + ExpectRegexModifier: "~*", + Expect: "200 OK", + }, + Server: StreamServer{ + Port: 1234, + UDP: true, + StatusZone: "udp-app", + ProxyRequests: createPointerFromInt(1), + ProxyResponses: createPointerFromInt(2), + ProxyPass: "udp-upstream", + ProxyTimeout: "10s", + ProxyConnectTimeout: "10s", + ProxyNextUpstream: true, + ProxyNextUpstreamTimeout: "10s", + ProxyNextUpstreamTries: 5, + HealthCheck: &StreamHealthCheck{ + Enabled: false, + Timeout: "5s", + Jitter: "0", + Port: 8080, + Interval: "5s", + Passes: 1, + Fails: 1, + Match: "match_udp-upstream", + }, + }, + } +) diff --git a/pkg/apis/configuration/v1/types.go b/pkg/apis/configuration/v1/types.go index c6a134383a..d6d55e5e77 100644 --- a/pkg/apis/configuration/v1/types.go +++ b/pkg/apis/configuration/v1/types.go @@ -39,6 +39,7 @@ type VirtualServerSpec struct { IngressClass string `json:"ingressClassName"` Host string `json:"host"` TLS *TLS `json:"tls"` + Gunzip string `json:"gunzip"` Policies []PolicyReference `json:"policies"` Upstreams []Upstream `json:"upstreams"` Routes []Route `json:"routes"` diff --git a/pkg/apis/configuration/validation/virtualserver.go b/pkg/apis/configuration/validation/virtualserver.go index 1ac7be51e8..ee036d9cf3 100644 --- a/pkg/apis/configuration/validation/virtualserver.go +++ b/pkg/apis/configuration/validation/virtualserver.go @@ -77,6 +77,7 @@ func (vsv *VirtualServerValidator) validateVirtualServerSpec(spec *v1.VirtualSer allErrs := field.ErrorList{} allErrs = append(allErrs, validateHost(spec.Host, fieldPath.Child("host"))...) + allErrs = append(allErrs, validateGunzip(spec.Gunzip, fieldPath.Child("gunzip"))...) allErrs = append(allErrs, vsv.validateTLS(spec.TLS, fieldPath.Child("tls"))...) allErrs = append(allErrs, validatePolicies(spec.Policies, fieldPath.Child("policies"), namespace)...) @@ -114,6 +115,15 @@ func validateHost(host string, fieldPath *field.Path) field.ErrorList { return allErrs } +func validateGunzip(fieldValue string, fl *field.Path) field.ErrorList { + switch fieldValue { + case "on", "off", "": + return nil + default: + return field.ErrorList{field.NotSupported(fl, fieldValue, []string{"on", "off"})} + } +} + func validatePolicies(policies []v1.PolicyReference, fieldPath *field.Path, namespace string) field.ErrorList { allErrs := field.ErrorList{} policyKeys := sets.Set[string]{} diff --git a/pkg/apis/configuration/validation/virtualserver_test.go b/pkg/apis/configuration/validation/virtualserver_test.go index 17fdcb30e1..6eae75a179 100644 --- a/pkg/apis/configuration/validation/virtualserver_test.go +++ b/pkg/apis/configuration/validation/virtualserver_test.go @@ -12,6 +12,7 @@ import ( func TestValidateVirtualServer(t *testing.T) { t.Parallel() + virtualServer := v1.VirtualServer{ ObjectMeta: meta_v1.ObjectMeta{ Name: "cafe", @@ -68,6 +69,215 @@ func TestValidateVirtualServer(t *testing.T) { } } +func TestValidateVirtualServer_PassesOnValidGunzipOn(t *testing.T) { + t.Parallel() + + vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false} + err := vsv.ValidateVirtualServer(&virtualServerWithValidGunzipOn) + if err != nil { + t.Errorf("ValidateVirtualServer() returned error %v for valid input %+v", err, virtualServerWithValidGunzipOn) + } +} + +func TestValidateVirtualServer_PassesOnValidGunzipOff(t *testing.T) { + t.Parallel() + + vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false} + err := vsv.ValidateVirtualServer(&virtualServerWithValidGunzipOff) + if err != nil { + t.Errorf("ValidateVirtualServer() returned error %v for valid input %+v", err, virtualServerWithValidGunzipOff) + } +} + +func TestValidateVirtualServer_PassesOnNoGunzip(t *testing.T) { + t.Parallel() + + vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false} + err := vsv.ValidateVirtualServer(&virtualServerWithNoGunzip) + if err != nil { + t.Errorf("ValidateVirtualServer() returned error %v for valid input %+v", err, virtualServerWithNoGunzip) + } +} + +func TestValidateVirtualServer_FailsOnBogusGunzipValue(t *testing.T) { + t.Parallel() + + vsv := &VirtualServerValidator{isPlus: false, isDosEnabled: false} + err := vsv.ValidateVirtualServer(&virtualServerWithBogusGunzipValue) + if err == nil { + t.Error("ValidateVirtualServer() returned no error on bogus gunzip value") + } +} + +var ( + virtualServerWithValidGunzipOn = v1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: v1.VirtualServerSpec{ + Host: "example.com", + Gunzip: "on", + Upstreams: []v1.Upstream{ + { + Name: "first", + Service: "service-1", + LBMethod: "random", + Port: 80, + MaxFails: createPointerFromInt(8), + MaxConns: createPointerFromInt(16), + Keepalive: createPointerFromInt(32), + Type: "grpc", + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Routes: []v1.Route{ + { + Path: "/first", + Action: &v1.Action{ + Pass: "first", + }, + }, + { + Path: "/second", + Action: &v1.Action{ + Pass: "second", + }, + }, + }, + }, + } + + virtualServerWithValidGunzipOff = v1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: v1.VirtualServerSpec{ + Host: "example.com", + Gunzip: "off", + Upstreams: []v1.Upstream{ + { + Name: "first", + Service: "service-1", + LBMethod: "random", + Port: 80, + MaxFails: createPointerFromInt(8), + MaxConns: createPointerFromInt(16), + Keepalive: createPointerFromInt(32), + Type: "grpc", + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Routes: []v1.Route{ + { + Path: "/first", + Action: &v1.Action{ + Pass: "first", + }, + }, + { + Path: "/second", + Action: &v1.Action{ + Pass: "second", + }, + }, + }, + }, + } + + virtualServerWithNoGunzip = v1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: v1.VirtualServerSpec{ + Host: "example.com", + Upstreams: []v1.Upstream{ + { + Name: "first", + Service: "service-1", + LBMethod: "random", + Port: 80, + MaxFails: createPointerFromInt(8), + MaxConns: createPointerFromInt(16), + Keepalive: createPointerFromInt(32), + Type: "grpc", + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Routes: []v1.Route{ + { + Path: "/first", + Action: &v1.Action{ + Pass: "first", + }, + }, + { + Path: "/second", + Action: &v1.Action{ + Pass: "second", + }, + }, + }, + }, + } + + virtualServerWithBogusGunzipValue = v1.VirtualServer{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "cafe", + Namespace: "default", + }, + Spec: v1.VirtualServerSpec{ + Host: "example.com", + Gunzip: "bogus", + Upstreams: []v1.Upstream{ + { + Name: "first", + Service: "service-1", + LBMethod: "random", + Port: 80, + MaxFails: createPointerFromInt(8), + MaxConns: createPointerFromInt(16), + Keepalive: createPointerFromInt(32), + Type: "grpc", + }, + { + Name: "second", + Service: "service-2", + Port: 80, + }, + }, + Routes: []v1.Route{ + { + Path: "/first", + Action: &v1.Action{ + Pass: "first", + }, + }, + { + Path: "/second", + Action: &v1.Action{ + Pass: "second", + }, + }, + }, + }, + } +) + func TestValidateHost(t *testing.T) { t.Parallel() validHosts := []string{ @@ -101,6 +311,26 @@ func TestValidateHost(t *testing.T) { } } +func TestValidateGunzip_FailsOnBogusGunzipValue(t *testing.T) { + t.Parallel() + bogusGunzipValue := "bogus" + allErr := validateGunzip(bogusGunzipValue, field.NewPath("gunzip")) + if len(allErr) == 0 { + t.Errorf("validateGunzip(%q) did not return error on invalid input.", bogusGunzipValue) + } +} + +func TestValidateGunzip_PassesOnValidGunzipValues(t *testing.T) { + t.Parallel() + tt := []string{"on", "off", ""} + for _, v := range tt { + allErr := validateGunzip(v, field.NewPath("gunzip")) + if len(allErr) > 0 { + t.Errorf("validateGunzip(%q) returned errors %v for valid input", v, allErr) + } + } +} + func TestValidateDos(t *testing.T) { t.Parallel() validDosResources := []string{ diff --git a/tests/requirements.txt b/tests/requirements.txt index bc22fedf1e..2c8b926bde 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -373,9 +373,9 @@ py==1.11.0 \ # via # -r requirements.txt # pytest-html -pyasn1==0.4.8 \ - --hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \ - --hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba +pyasn1==0.5.0 \ + --hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \ + --hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde # via # -r requirements.txt # pyasn1-modules