diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c925264a86..7dd66a61e9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -320,3 +320,18 @@ jobs: with: k8s-version: ${{ matrix.k8s }} variation: "CHROOT" + + kubernetes-crossplane: + name: Kubernetes Crossplane + needs: + - changes + - build + if: | + (needs.changes.outputs.go == 'true') || (needs.changes.outputs.baseimage == 'true') || ${{ github.event.workflow_dispatch.run_e2e == 'true' }} + strategy: + matrix: + k8s: [v1.30.4, v1.31.0] + uses: ./.github/workflows/zz-tmpl-k8s-e2e.yaml + with: + k8s-version: ${{ matrix.k8s }} + variation: "CROSSPLANE" \ No newline at end of file diff --git a/.github/workflows/zz-tmpl-k8s-e2e.yaml b/.github/workflows/zz-tmpl-k8s-e2e.yaml index 996b673f94..935538a8e1 100644 --- a/.github/workflows/zz-tmpl-k8s-e2e.yaml +++ b/.github/workflows/zz-tmpl-k8s-e2e.yaml @@ -44,6 +44,7 @@ jobs: SKIP_INGRESS_IMAGE_CREATION: true SKIP_E2E_IMAGE_CREATION: true IS_CHROOT: ${{ inputs.variation == 'CHROOT' }} + IS_CROSSPLANE: ${{ inputs.variation == 'CROSSPLANE' }} run: | kind get kubeconfig > $HOME/.kube/kind-config-kind make kind-e2e-test diff --git a/.golangci.yml b/.golangci.yml index 2d73e14e77..feaf26b285 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,6 @@ linters: - errname - ginkgolinter - gocheckcompilerdirectives - - goconst - gocritic - gocyclo - godox diff --git a/charts/ingress-nginx/README.md b/charts/ingress-nginx/README.md index c0b00e56d1..788a044417 100644 --- a/charts/ingress-nginx/README.md +++ b/charts/ingress-nginx/README.md @@ -489,6 +489,7 @@ metadata: | controller.sysctls | object | `{}` | sysctls for controller pods # Ref: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ | | controller.tcp.annotations | object | `{}` | Annotations to be added to the tcp config configmap | | controller.tcp.configMapNamespace | string | `""` | Allows customization of the tcp-services-configmap; defaults to $(POD_NAMESPACE) | +| controller.templateEngine | string | `"go-template"` | Defines which template engine should be used when creating NGINX configuration. Can be 'go-template' or 'crossplane' | | controller.terminationGracePeriodSeconds | int | `300` | `terminationGracePeriodSeconds` to avoid killing pods before we are ready # wait up to five minutes for the drain of connections # | | controller.tolerations | list | `[]` | Node tolerations for server scheduling to nodes with taints # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ # | | controller.topologySpreadConstraints | list | `[]` | Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. # Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ # | diff --git a/charts/ingress-nginx/templates/_params.tpl b/charts/ingress-nginx/templates/_params.tpl index 0051dc9c09..c4530ead64 100644 --- a/charts/ingress-nginx/templates/_params.tpl +++ b/charts/ingress-nginx/templates/_params.tpl @@ -60,6 +60,9 @@ {{- if .Values.controller.enableTopologyAwareRouting }} - --enable-topology-aware-routing=true {{- end }} +{{- if .Values.controller.templateEngine }} +- --configuration-template-engine={{ .Values.controller.templateEngine }} +{{- end }} {{- if .Values.controller.disableLeaderElection }} - --disable-leader-election=true {{- end }} diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml index 116adf7ca5..afe280fc3e 100644 --- a/charts/ingress-nginx/values.yaml +++ b/charts/ingress-nginx/values.yaml @@ -21,6 +21,8 @@ commonLabels: {} controller: name: controller + # -- Defines which template engine should be used when creating NGINX configuration. Can be 'go-template' or 'crossplane' + templateEngine: "go-template" enableAnnotationValidations: true image: ## Keep false as default for now! diff --git a/go.mod b/go.mod index 8826fb0d2b..52ec37fbc7 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/moul/pb v0.0.0-20220425114252-bca18df4138c github.com/ncabatoff/process-exporter v0.8.4 + github.com/nginxinc/nginx-go-crossplane v0.4.66 github.com/onsi/ginkgo/v2 v2.21.0 github.com/opencontainers/runc v1.2.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 @@ -48,7 +49,9 @@ require ( require ( github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/jstemmer/go-junit-report v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/x448/float16 v0.8.4 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect @@ -132,7 +135,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo/v2 v2.0.0-20240404160639-a0386bf69313 // indirect k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect diff --git a/go.sum b/go.sum index 174d9074e8..29e03b0209 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v1.0.0 h1:8X1gzZpR+nVQLAht+L/foqOeX2l9DTZoaIPbEQHxsds= +github.com/jstemmer/go-junit-report v1.0.0/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -129,6 +131,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0 h1:9WsegDYiSKtZXru+NcOB4z7iqb00n4atjmQlyy5TRXI= +github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0/go.mod h1:TeVdzh+5QB5IpWDJAU/uviXA6kOg9yXzLrrjeLKJXqY= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= @@ -158,6 +162,8 @@ github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833 h1:t4WWQ9I797y7QU github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833/go.mod h1:0CznHmXSjMEqs5Tezj/w2emQoM41wzYM9KpDKUHPYag= github.com/ncabatoff/process-exporter v0.8.4 h1:qj0pWbP6AytVQ1fMYabRd5LnuV6NPh0O6WCfenPJT54= github.com/ncabatoff/process-exporter v0.8.4/go.mod h1:MxEOWl740VK/hlWycJkq91VrA2mI+U9Bvc1wuyAaxA4= +github.com/nginxinc/nginx-go-crossplane v0.4.66 h1:BS6ZyRTWP+ZSFZ7j0VxBdpUyuS8QRMSdU/y+EsCPY9w= +github.com/nginxinc/nginx-go-crossplane v0.4.66/go.mod h1:LgUBS07yefkK+cEjw2seernNp5Q86GG8IuNHLj1RQOo= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -193,6 +199,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= diff --git a/go.work.sum b/go.work.sum index 06d0a1ab92..67a15a2d82 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,1199 +1,129 @@ -cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= -cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= -cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go/accessapproval v1.6.0 h1:x0cEHro/JFPd7eS4BlEWNTMecIj2HdXjOVB5BtvwER0= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accessapproval v1.7.4/go.mod h1:/aTEh45LzplQgFYdQdwPMR9YdX0UlhBmvB84uAmQKUc= -cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= -cloud.google.com/go/accesscontextmanager v1.7.0 h1:MG60JgnEoawHJrbWw0jGdv6HLNSf6gQvYRiXpuzqgEA= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= -cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M= -cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= -cloud.google.com/go/aiplatform v1.37.0 h1:zTw+suCVchgZyO+k847wjzdVjWmrAuehxdvcZvJwfGg= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.52.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= -cloud.google.com/go/analytics v0.19.0 h1:LqAo3tAh2FU9+w/r7vc3hBjU23Kv7GhO/PDIW7kIYgM= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.6/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.23.0/go.mod h1:YPd7Bvik3WS95KBok2gPXDqQPHy08TsCQG6CdUCb+u0= -cloud.google.com/go/apigateway v1.5.0 h1:ZI9mVO7x3E9RK/BURm2p1aw9YTBSCQe3klmyP1WxWEg= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= -cloud.google.com/go/apigateway v1.6.5/go.mod h1:6wCwvYRckRQogyDDltpANi3zsCDl6kWi0b4Je+w2UiI= -cloud.google.com/go/apigeeconnect v1.5.0 h1:sWOmgDyAsi1AZ48XRHcATC0tsi9SkPT7DA/+VCfkaeA= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeconnect v1.6.4/go.mod h1:CapQCWZ8TCjnU0d7PobxhpOdVz/OVJ2Hr/Zcuu1xFx0= -cloud.google.com/go/apigeeconnect v1.6.5/go.mod h1:MEKm3AiT7s11PqTfKE3KZluZA9O91FNysvd3E6SJ6Ow= -cloud.google.com/go/apigeeregistry v0.6.0 h1:E43RdhhCxdlV+I161gUY2rI4eOaMzHTA5kNkvRsFXvc= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apigeeregistry v0.8.2/go.mod h1:h4v11TDGdeXJDJvImtgK2AFVvMIgGWjSb0HRnBSjcX8= -cloud.google.com/go/apigeeregistry v0.8.3/go.mod h1:aInOWnqF4yMQx8kTjDqHNXjZGh/mxeNlAf52YqtASUs= -cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= -cloud.google.com/go/appengine v1.7.1 h1:aBGDKmRIaRRoWJ2tAoN0oVSHoWLhtO9aj/NvUyP4aYs= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= -cloud.google.com/go/appengine v1.8.4/go.mod h1:TZ24v+wXBujtkK77CXCpjZbnuTvsFNT41MUaZ28D6vg= -cloud.google.com/go/appengine v1.8.5/go.mod h1:uHBgNoGLTS5di7BvU25NFDuKa82v0qQLjyMJLuPQrVo= -cloud.google.com/go/area120 v0.7.1 h1:ugckkFh4XkHJMPhTIx0CyvdoBxmOpMe8rNs4Ok8GAag= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= -cloud.google.com/go/area120 v0.8.4/go.mod h1:jfawXjxf29wyBXr48+W+GyX/f8fflxp642D/bb9v68M= -cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5UFu6GMyA0= -cloud.google.com/go/artifactregistry v1.13.0 h1:o1Q80vqEB6Qp8WLEH3b8FBLNUCrGQ4k5RFj0sn/sgO8= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= -cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= -cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= -cloud.google.com/go/asset v1.13.0 h1:YAsssO08BqZ6mncbb6FPlj9h6ACS7bJQUOlzciSfbNk= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= -cloud.google.com/go/asset v1.15.3/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= -cloud.google.com/go/assuredworkloads v1.10.0 h1:VLGnVFta+N4WM+ASHbhc14ZOItOabDLH1MSoDv+Xuag= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= -cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= -cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= -cloud.google.com/go/automl v1.12.0 h1:50VugllC+U4IGl3tDNcZaWvApHBTrn/TvyHDJ0wM+Uw= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/automl v1.13.4/go.mod h1:ULqwX/OLZ4hBVfKQaMtxMSTlPx0GqGbWN8uA/1EqCP8= -cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= -cloud.google.com/go/baremetalsolution v0.5.0 h1:2AipdYXL0VxMboelTTw8c1UJ7gYu35LZYUbuRv9Q28s= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.3/go.mod h1:/UAQ5xG3faDdy180rCUv47e0jvpp3BFxT+Cl0PFjw5g= -cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= -cloud.google.com/go/batch v0.7.0 h1:YbMt0E6BtqeD5FvSv1d56jbVsWEzlGm55lYte+M6Mzs= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.6.3/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.7.0/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= -cloud.google.com/go/beyondcorp v0.5.0 h1:UkY2BTZkEUAVrgqnSdOJ4p3y9ZRBPEe1LkjgC8Bj/Pc= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.3/go.mod h1:HcBvnEd7eYr+HGDd5ZbuVmBYX019C6CEXBonXbCVwJo= -cloud.google.com/go/beyondcorp v1.0.4/go.mod h1:Gx8/Rk2MxrvWfn4WIhHIG1NV7IBfg14pTKv1+EArVcc= -cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= -cloud.google.com/go/bigquery v1.57.1/go.mod h1:iYzC0tGVWt1jqSzBHqCr3lrRn0u13E8e+AqowBsDgug= -cloud.google.com/go/bigquery v1.58.0/go.mod h1:0eh4mWNY0KrBTjUzLjoYImapGORq9gEPT7MWjCy9lik= -cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc= -cloud.google.com/go/billing v1.13.0 h1:JYj28UYF5w6VBAh0gQYlgHJ/OD1oA+JgW29YZQU+UHM= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.4/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.0/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.2/go.mod h1:PPIwVsOOQ7xzbADCwNe8nvK776QpfrOAUkvKjCUcpSE= -cloud.google.com/go/binaryauthorization v1.5.0 h1:d3pMDBCCNivxt5a4eaV7FwL7cSH0H7RrEnFrTb1QKWs= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.3/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.0/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.1/go.mod h1:1HVRyBerREA/nhI7yLang4Zn7vfNVA3okoAR9qYQJAQ= -cloud.google.com/go/certificatemanager v1.6.0 h1:5C5UWeSt8Jkgp7OWn2rCkLmYurar/vIWIoSQ2+LaTOc= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= -cloud.google.com/go/certificatemanager v1.7.5/go.mod h1:uX+v7kWqy0Y3NG/ZhNvffh0kuqkKZIXdvlZRO7z0VtM= -cloud.google.com/go/channel v1.12.0 h1:GpcQY5UJKeOekYgsX3QXbzzAc/kRGtBq43fTmyKe6Uw= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.3/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.5/go.mod h1:FlpaOSINDAXgEext0KMaBq/vwpLMkkPAw9b2mApQeHc= -cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.3/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.1/go.mod h1:gIofXZSu+XD2Uy+qkOrGKEx45zd7s28u/k8f99qKals= -cloud.google.com/go/clouddms v1.5.0 h1:E7v4TpDGUyEm1C/4KIrpVSOCTm0P6vWdHT0I4mostRA= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.3/go.mod h1:fkN2HQQNUYInAU3NQ3vRLkV2iWs8lIdmBKOx4nrL6Hc= -cloud.google.com/go/clouddms v1.7.4/go.mod h1:RdrVqoFG9RWI5AvZ81SxJ/xvxPdtcRhFotwdE79DieY= -cloud.google.com/go/cloudtasks v1.10.0 h1:uK5k6abf4yligFgYFnG0ni8msai/dSv6mDmiBulU0hU= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= -cloud.google.com/go/cloudtasks v1.12.6/go.mod h1:b7c7fe4+TJsFZfDyzO51F7cjq7HLUlRi/KZQLQjDsaY= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.11.3/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.1/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= -cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.27.1/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.29.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= -cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.3/go.mod h1:kMeST7yWFQMGjiG9K7Eov+fPNQcGhb8mXj/UcTiWw9U= -cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= -cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.3/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.2/go.mod h1:2YbODwmhpLM4lOFe3PuEhHK9EyTzQJ5AXgIy7EDKTEE= -cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4= -cloud.google.com/go/dataflow v0.8.0 h1:eYyD9o/8Nm6EttsKZaEGD84xC17bNgSKCu0ZxwqUbpg= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= -cloud.google.com/go/dataflow v0.9.4/go.mod h1:4G8vAkHYCSzU8b/kmsoR2lWyHJD85oMJPHMtan40K8w= -cloud.google.com/go/dataflow v0.9.5/go.mod h1:udl6oi8pfUHnL0z6UN9Lf9chGqzDMVqcYTcZ1aPnCZQ= -cloud.google.com/go/dataform v0.7.0 h1:Dyk+fufup1FR6cbHjFpMuP4SfPiF3LI3JtoIIALoq48= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/dataform v0.9.1/go.mod h1:pWTg+zGQ7i16pyn0bS1ruqIE91SdL2FDMvEYu/8oQxs= -cloud.google.com/go/dataform v0.9.2/go.mod h1:S8cQUwPNWXo7m/g3DhWHsLBoufRNn9EgFrMgne2j7cI= -cloud.google.com/go/datafusion v1.6.0 h1:sZjRnS3TWkGsu1LjYPFD/fHeMLZNXDK6PDHi2s2s/bk= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= -cloud.google.com/go/datafusion v1.7.4/go.mod h1:BBs78WTOLYkT4GVZIXQCZT3GFpkpDN4aBY4NDX/jVlM= -cloud.google.com/go/datafusion v1.7.5/go.mod h1:bYH53Oa5UiqahfbNK9YuYKteeD4RbQSNMx7JF7peGHc= -cloud.google.com/go/datalabeling v0.7.0 h1:ch4qA2yvddGRUrlfwrNJCr79qLqhS9QBwofPHfFlDIk= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= -cloud.google.com/go/datalabeling v0.8.5/go.mod h1:IABB2lxQnkdUbMnQaOl2prCOfms20mcPxDBm36lps+s= -cloud.google.com/go/dataplex v1.6.0 h1:RvoZ5T7gySwm1CHzAw7yY1QwwqaGswunmqEssPxU/AM= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.11.1/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.2/go.mod h1:0oGOSFlEKef1cQeAHXy4GZPB/Ife0fz/PxBf+ZymA2U= -cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.3/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.3.0/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.4.0/go.mod h1:3B1Ht2aRB8VZIteGxQS/iNSJGzt9+CA0WGnDVMEm7Z4= -cloud.google.com/go/dataqna v0.7.0 h1:yFzi/YU4YAdjyo7pXkBE2FeHbgz5OQQBVDdbErEHmVQ= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= -cloud.google.com/go/dataqna v0.8.4/go.mod h1:mySRKjKg5Lz784P6sCov3p1QD+RZQONRMRjzGNcFd0c= -cloud.google.com/go/dataqna v0.8.5/go.mod h1:vgihg1mz6n7pb5q2YJF7KlXve6tCglInd6XO0JGOlWM= -cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastream v1.7.0 h1:BBCBTnWMDwwEzQQmipUXxATa7Cm7CA/gKjKcR2w35T0= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.3/go.mod h1:YR0USzgjhqA/Id0Ycu1VvZe8hEWwrkjuXrGbzeDOSEA= -cloud.google.com/go/datastream v1.10.4/go.mod h1:7kRxPdxZxhPg3MFeCSulmAJnil8NJGGvSNdn4p1sRZo= -cloud.google.com/go/deploy v1.8.0 h1:otshdKEbmsi1ELYeCKNYppwV0UH5xD05drSdBm7ouTk= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.14.2/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.17.0/go.mod h1:XBr42U5jIr64t92gcpOXxNrqL2PStQCXHuKK5GRUuYo= -cloud.google.com/go/deploy v1.17.1/go.mod h1:SXQyfsXrk0fBmgBHRzBjQbZhMfKZ3hMQBw5ym7MN/50= -cloud.google.com/go/dialogflow v1.32.0 h1:uVlKKzp6G/VtSW0E7IH1Y5o0H48/UOCmqksG2riYCwQ= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.3/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.1/go.mod h1:C1sjs2/g9cEwjCltkKeYp3FFpz8BOzNondEaAlCpt+A= -cloud.google.com/go/dialogflow v1.49.0/go.mod h1:dhVrXKETtdPlpPhE7+2/k4Z8FRNUp6kMV3EW3oz/fe0= -cloud.google.com/go/dlp v1.9.0 h1:1JoJqezlgu6NWCroBxr4rOZnwNFILXr4cB9dMaSKO4A= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= -cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= -cloud.google.com/go/dlp v1.11.2/go.mod h1:9Czi+8Y/FegpWzgSfkRlyz+jwW6Te9Rv26P3UfU/h/w= -cloud.google.com/go/documentai v1.18.0 h1:KM3Xh0QQyyEdC8Gs2vhZfU+rt6OCPF0dwVwxKgLmWfI= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.5/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.7/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.25.0/go.mod h1:ftLnzw5VcXkLItp6pw1mFic91tMRyfv6hHEY5br4KzY= -cloud.google.com/go/domains v0.8.0 h1:2ti/o9tlWL4N+wIuWUNH+LbfgpwxPr8J1sv9RHA4bYQ= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= -cloud.google.com/go/domains v0.9.4/go.mod h1:27jmJGShuXYdUNjyDG0SodTfT5RwLi7xmH334Gvi3fY= -cloud.google.com/go/domains v0.9.5/go.mod h1:dBzlxgepazdFhvG7u23XMhmMKBjrkoUNaw0A8AQB55Y= -cloud.google.com/go/edgecontainer v1.0.0 h1:O0YVE5v+O0Q/ODXYsQHmHb+sYM8KNjGZw2pjX2Ws41c= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/edgecontainer v1.1.4/go.mod h1:AvFdVuZuVGdgaE5YvlL1faAoa1ndRR/5XhXZvPBHbsE= -cloud.google.com/go/edgecontainer v1.1.5/go.mod h1:rgcjrba3DEDEQAidT4yuzaKWTbkTI5zAMu3yy6ZWS0M= -cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= -cloud.google.com/go/essentialcontacts v1.5.0 h1:gIzEhCoOT7bi+6QZqZIzX1Erj4SswMPIteNvYVlu+pM= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/essentialcontacts v1.6.5/go.mod h1:jjYbPzw0x+yglXC890l6ECJWdYeZ5dlYACTFL0U/VuM= -cloud.google.com/go/essentialcontacts v1.6.6/go.mod h1:XbqHJGaiH0v2UvtuucfOzFXN+rpL/aU5BCZLn4DYl1Q= -cloud.google.com/go/eventarc v1.11.0 h1:fsJmNeqvqtk74FsaVDU6cH79lyZNCYP8Rrv7EhaB/PU= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.3/go.mod h1:RWH10IAZIRcj1s/vClXkBgMHwh59ts7hSWcqD3kaclg= -cloud.google.com/go/eventarc v1.13.4/go.mod h1:zV5sFVoAa9orc/52Q+OuYUG9xL2IIZTbbuTHC6JSY8s= -cloud.google.com/go/filestore v1.6.0 h1:ckTEXN5towyTMu4q0uQ1Mde/JwTHur0gXs8oaIZnKfw= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/filestore v1.7.4/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.0/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.1/go.mod h1:MbN9KcaM47DRTIuLfQhJEsjaocVebNtNQhSLhKCF5GM= -cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= -cloud.google.com/go/functions v1.13.0 h1:pPDqtsXG2g9HeOQLoquLbmvmb82Y4Ezdo1GXuotFoWg= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= -cloud.google.com/go/functions v1.15.4/go.mod h1:CAsTc3VlRMVvx+XqXxKqVevguqJpnVip4DdonFsX28I= -cloud.google.com/go/functions v1.16.0/go.mod h1:nbNpfAG7SG7Duw/o1iZ6ohvL7mc6MapWQVpqtM29n8k= -cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= -cloud.google.com/go/gkebackup v0.4.0 h1:za3QZvw6ujR0uyqkhomKKKNoXDyqYGPJies3voUK8DA= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.4/go.mod h1:gLVlbM8h/nHIs09ns1qx3q3eaXcGSELgNu1DWXYz1HI= -cloud.google.com/go/gkebackup v1.3.5/go.mod h1:KJ77KkNN7Wm1LdMopOelV6OodM01pMuK2/5Zt1t4Tvc= -cloud.google.com/go/gkeconnect v0.7.0 h1:gXYKciHS/Lgq0GJ5Kc9SzPA35NGc3yqu6SkjonpEr2Q= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= -cloud.google.com/go/gkeconnect v0.8.4/go.mod h1:84hZz4UMlDCKl8ifVW8layK4WHlMAFeq8vbzjU0yJkw= -cloud.google.com/go/gkeconnect v0.8.5/go.mod h1:LC/rS7+CuJ5fgIbXv8tCD/mdfnlAadTaUufgOkmijuk= -cloud.google.com/go/gkehub v0.12.0 h1:TqCSPsEBQ6oZSJgEYZ3XT8x2gUadbvfwI32YB0kuHCs= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= -cloud.google.com/go/gkehub v0.14.5/go.mod h1:6bzqxM+a+vEH/h8W8ec4OJl4r36laxTs3A/fMNHJ0wA= -cloud.google.com/go/gkemulticloud v0.5.0 h1:8I84Q4vl02rJRsFiinBxl7WCozfdLlUVBQuSrqr9Wtk= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.3/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.1/go.mod h1:C+a4vcHlWeEIf45IB5FFR5XGjTeYhF83+AYIpTy4i2Q= -cloud.google.com/go/grafeas v0.2.0 h1:CYjC+xzdPvbV65gi6Dr4YowKcmLo045pm18L0DhdELM= -cloud.google.com/go/gsuiteaddons v1.5.0 h1:1mvhXqJzV0Vg5Fa95QwckljODJJfDFXV4pn+iL50zzA= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/gsuiteaddons v1.6.4/go.mod h1:rxtstw7Fx22uLOXBpsvb9DUbC+fiXs7rF4U29KHM/pE= -cloud.google.com/go/gsuiteaddons v1.6.5/go.mod h1:Lo4P2IvO8uZ9W+RaC6s1JVxo42vgy+TX5a6hfBZ0ubs= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iap v1.7.1 h1:PxVHFuMxmSZyfntKXHXhd8bo82WJ+LcATenq7HLdVnU= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.3/go.mod h1:DTdutSZBqkkOm2HEOTBzhZxh2mwwxshfD/h3yofAiCw= -cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= -cloud.google.com/go/ids v1.3.0 h1:fodnCDtOXuMmS8LTC2y3h8t24U8F3eKWfhi+3LY6Qf0= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/ids v1.4.4/go.mod h1:z+WUc2eEl6S/1aZWzwtVNWoSZslgzPxAboS0lZX0HjI= -cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= -cloud.google.com/go/iot v1.6.0 h1:39W5BFSarRNZfVG0eXI5LYux+OVQT8GkgpHCnrZL2vM= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/iot v1.7.4/go.mod h1:3TWqDVvsddYBG++nHSZmluoCAVGr1hAcabbWZNKEZLk= -cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= -cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= -cloud.google.com/go/language v1.9.0 h1:7Ulo2mDk9huBoBi8zCE3ONOoBrL6UXfAI71CLQ9GEIM= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.12.2/go.mod h1:9idWapzr/JKXBBQ4lWqVX/hcadxB194ry20m/bTrhWc= -cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= -cloud.google.com/go/lifesciences v0.8.0 h1:uWrMjWTsGjLZpCTWEAzYvyXj+7fhiZST45u9AgasasI= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/lifesciences v0.9.4/go.mod h1:bhm64duKhMi7s9jR9WYJYvjAFJwRqNj+Nia7hF0Z7JA= -cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= -cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= -cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= -cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= -cloud.google.com/go/managedidentities v1.5.0 h1:ZRQ4k21/jAhrHBVKl/AY7SjgzeJwG1iZa+mJ82P+VNg= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/managedidentities v1.6.4/go.mod h1:WgyaECfHmF00t/1Uk8Oun3CQ2PGUtjc3e9Alh79wyiM= -cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= -cloud.google.com/go/maps v0.7.0 h1:mv9YaczD4oZBZkM5XJl6fXQ984IkJNHPwkc8MUsdkBo= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.6.1/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.3/go.mod h1:VGAn809ADswi1ASofL5lveOHPnE6Rk/SFTTBx1yuOLw= -cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= -cloud.google.com/go/mediatranslation v0.7.0 h1:anPxH+/WWt8Yc3EdoEJhPMBRF7EhIdz426A+tuoA0OU= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= -cloud.google.com/go/mediatranslation v0.8.4/go.mod h1:9WstgtNVAdN53m6TQa5GjIjLqKQPXe74hwSCxUP6nj4= -cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= -cloud.google.com/go/memcache v1.9.0 h1:8/VEmWCpnETCrBwS3z4MhT+tIdKgR1Z4Tr2tvYH32rg= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= -cloud.google.com/go/memcache v1.10.4/go.mod h1:v/d8PuC8d1gD6Yn5+I3INzLR01IDn0N4Ym56RgikSI0= -cloud.google.com/go/memcache v1.10.5/go.mod h1:/FcblbNd0FdMsx4natdj+2GWzTq+cjZvMa1I+9QsuMA= -cloud.google.com/go/metastore v1.10.0 h1:QCFhZVe2289KDBQ7WxaHV2rAmPrmRAdLC6gbjUd3HPo= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.13.3/go.mod h1:K+wdjXdtkdk7AQg4+sXS8bRrQa9gcOr+foOMF2tqINE= -cloud.google.com/go/metastore v1.13.4/go.mod h1:FMv9bvPInEfX9Ac1cVcRXp8EBBQnBcqH6gz3KvJ9BAE= -cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg= -cloud.google.com/go/networkconnectivity v1.11.0 h1:ZD6b4Pk1jEtp/cx9nx0ZYcL3BKqDa+KixNDZ6Bjs1B8= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.3/go.mod h1:4aoeFdrJpYEXNvrnfyD5kIzs8YtHg945Og4koAjHQek= -cloud.google.com/go/networkconnectivity v1.14.4/go.mod h1:PU12q++/IMnDJAB+3r+tJtuCXCfwfN+C6Niyj6ji1Po= -cloud.google.com/go/networkmanagement v1.6.0 h1:8KWEUNGcpSX9WwZXq7FtciuNGPdPdPN/ruDm769yAEM= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.3/go.mod h1:y7WMO1bRLaP5h3Obm4tey+NquUvB93Co1oh4wpL+XcU= -cloud.google.com/go/networkmanagement v1.9.4/go.mod h1:daWJAl0KTFytFL7ar33I6R/oNBH8eEOX/rBNHrC/8TA= -cloud.google.com/go/networksecurity v0.8.0 h1:sOc42Ig1K2LiKlzG71GUVloeSJ0J3mffEBYmvu+P0eo= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= -cloud.google.com/go/networksecurity v0.9.4/go.mod h1:E9CeMZ2zDsNBkr8axKSYm8XyTqNhiCHf1JO/Vb8mD1w= -cloud.google.com/go/networksecurity v0.9.5/go.mod h1:KNkjH/RsylSGyyZ8wXpue8xpCEK+bTtvof8SBfIhMG8= -cloud.google.com/go/notebooks v1.8.0 h1:Kg2K3K7CbSXYJHZ1aGQpf1xi5x2GUvQWf2sFVuiZh8M= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.11.2/go.mod h1:z0tlHI/lREXC8BS2mIsUeR3agM1AkgLiS+Isov3SS70= -cloud.google.com/go/notebooks v1.11.3/go.mod h1:0wQyI2dQC3AZyQqWnRsp+yA+kY4gC7ZIVP4Qg3AQcgo= -cloud.google.com/go/optimization v1.3.1 h1:dj8O4VOJRB4CUwZXdmwNViH1OtI0WtWL867/lnYH248= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.6.2/go.mod h1:mWNZ7B9/EyMCcwNl1frUGEuY6CPijSkz88Fz2vwKPOY= -cloud.google.com/go/optimization v1.6.3/go.mod h1:8ve3svp3W6NFcAEFr4SfJxrldzhUl4VMUJmhrqVKtYA= -cloud.google.com/go/orchestration v1.6.0 h1:Vw+CEXo8M/FZ1rb4EjcLv0gJqqw89b7+g+C/EmniTb8= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= -cloud.google.com/go/orchestration v1.8.5/go.mod h1:C1J7HesE96Ba8/hZ71ISTV2UAat0bwN+pi85ky38Yq8= -cloud.google.com/go/orgpolicy v1.10.0 h1:XDriMWug7sd0kYT1QKofRpRHzjad0bK8Q8uA9q+XrU4= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= -cloud.google.com/go/orgpolicy v1.11.4/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.1/go.mod h1:aibX78RDl5pcK3jA8ysDQCFkVxLj3aOQqrbBaUL2V5I= -cloud.google.com/go/osconfig v1.11.0 h1:PkSQx4OHit5xz2bNyr11KGcaFccL5oqglFPdTboyqwQ= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= -cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= -cloud.google.com/go/osconfig v1.12.5/go.mod h1:D9QFdxzfjgw3h/+ZaAb5NypM8bhOMqBzgmbhzWViiW8= -cloud.google.com/go/oslogin v1.9.0 h1:whP7vhpmc+ufZa90eVpkfbgzJRK/Xomjz+XCD4aGwWw= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= -cloud.google.com/go/oslogin v1.12.2/go.mod h1:CQ3V8Jvw4Qo4WRhNPF0o+HAM4DiLuE27Ul9CX9g2QdY= -cloud.google.com/go/oslogin v1.13.0/go.mod h1:xPJqLwpTZ90LSE5IL1/svko+6c5avZLluiyylMb/sRA= -cloud.google.com/go/oslogin v1.13.1/go.mod h1:vS8Sr/jR7QvPWpCjNqy6LYZr5Zs1e8ZGW/KPn9gmhws= -cloud.google.com/go/phishingprotection v0.7.0 h1:l6tDkT7qAEV49MNEJkEJTB6vOO/onbSOcNtAT09HPuA= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/phishingprotection v0.8.4/go.mod h1:6b3kNPAc2AQ6jZfFHioZKg9MQNybDg4ixFd4RPZZ2nE= -cloud.google.com/go/phishingprotection v0.8.5/go.mod h1:g1smd68F7mF1hgQPuYn3z8HDbNre8L6Z0b7XMYFmX7I= -cloud.google.com/go/policytroubleshooter v1.6.0 h1:yKAGC4p9O61ttZUswaq9GAn1SZnEzTd0vUYXD7ZBT7Y= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.10.2/go.mod h1:m4uF3f6LseVEnMV6nknlN2vYGRb+75ylQwJdnOXfnv0= -cloud.google.com/go/policytroubleshooter v1.10.3/go.mod h1:+ZqG3agHT7WPb4EBIRqUv4OyIwRTZvsVDHZ8GlZaoxk= -cloud.google.com/go/privatecatalog v0.8.0 h1:EPEJ1DpEGXLDnmc7mnCAqFmkwUJbIsaLAiLHVOkkwtc= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= -cloud.google.com/go/privatecatalog v0.9.4/go.mod h1:SOjm93f+5hp/U3PqMZAHTtBtluqLygrDrVO8X8tYtG0= -cloud.google.com/go/privatecatalog v0.9.5/go.mod h1:fVWeBOVe7uj2n3kWRGlUQqR/pOd450J9yZoOECcQqJk= -cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= -cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= -cloud.google.com/go/pubsublite v1.7.0 h1:cb9fsrtpINtETHiJ3ECeaVzrfIVhcGjhhJEjybHXHao= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= -cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0 h1:6iOCujSNJ0YS7oNymI64hXsjGq60T4FK1zdLugxbzvU= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.3/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.0/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.2/go.mod h1:trwwGkfhCmp05Ll5MSJPXY7yvnO0p4v3orGANAFHAuU= -cloud.google.com/go/recommendationengine v0.7.0 h1:VibRFCwWXrFebEWKHfZAt2kta6pS7Tlimsnms0fjv7k= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= -cloud.google.com/go/recommendationengine v0.8.4/go.mod h1:GEteCf1PATl5v5ZsQ60sTClUE0phbWmo3rQ1Js8louU= -cloud.google.com/go/recommendationengine v0.8.5/go.mod h1:A38rIXHGFvoPvmy6pZLozr0g59NRNREz4cx7F58HAsQ= -cloud.google.com/go/recommender v1.9.0 h1:ZnFRY5R6zOVk2IDS1Jbv5Bw+DExCI5rFumsTnMXiu/A= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.3/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.0/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.1/go.mod h1:gf95SInWNND5aPas3yjwl0I572dtudMhMIG4ni8nr+0= -cloud.google.com/go/redis v1.11.0 h1:JoAd3SkeDt3rLFAAxEvw6wV4t+8y4ZzfZcZmddqphQ8= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/redis v1.14.1/go.mod h1:MbmBxN8bEnQI4doZPC1BzADU4HGocHBk2de3SbgOkqs= -cloud.google.com/go/redis v1.14.2/go.mod h1:g0Lu7RRRz46ENdFKQ2EcQZBAJ2PtJHJLuiiRuEXwyQw= -cloud.google.com/go/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcemanager v1.9.4/go.mod h1:N1dhP9RFvo3lUfwtfLWVxfUWq8+KUQ+XLlHLH3BoFJ0= -cloud.google.com/go/resourcemanager v1.9.5/go.mod h1:hep6KjelHA+ToEjOfO3garMKi/CLYwTqeAw7YiEI9x8= -cloud.google.com/go/resourcesettings v1.5.0 h1:8Dua37kQt27CCWHm4h/Q1XqCF6ByD7Ouu49xg95qJzI= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= -cloud.google.com/go/resourcesettings v1.6.4/go.mod h1:pYTTkWdv2lmQcjsthbZLNBP4QW140cs7wqA3DuqErVI= -cloud.google.com/go/resourcesettings v1.6.5/go.mod h1:WBOIWZraXZOGAgoR4ukNj0o0HiSMO62H9RpFi9WjP9I= -cloud.google.com/go/retail v1.12.0 h1:1Dda2OpFNzIb4qWgFZjYlpP7sxX3aLeypKG6A3H4Yys= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/retail v1.14.4/go.mod h1:l/N7cMtY78yRnJqp5JW8emy7MB1nz8E4t2yfOmklYfg= -cloud.google.com/go/retail v1.16.0/go.mod h1:LW7tllVveZo4ReWt68VnldZFWJRzsh9np+01J9dYWzE= -cloud.google.com/go/run v0.9.0 h1:ydJQo+k+MShYnBfhaRHSZYeD/SQKZzZLAROyfpeD9zw= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= -cloud.google.com/go/run v1.3.3/go.mod h1:WSM5pGyJ7cfYyYbONVQBN4buz42zFqwG67Q3ch07iK4= -cloud.google.com/go/run v1.3.4/go.mod h1:FGieuZvQ3tj1e9GnzXqrMABSuir38AJg5xhiYq+SF3o= -cloud.google.com/go/scheduler v1.9.0 h1:NpQAHtx3sulByTLe2dMwWmah8PWgeoieFPpJpArwFV0= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= -cloud.google.com/go/scheduler v1.10.4/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.5/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE= -cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= -cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= -cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4= -cloud.google.com/go/security v1.13.0 h1:PYvDxopRQBfYAXKAuDpFCKBvDOWPWzp9k/H5nB3ud3o= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= -cloud.google.com/go/security v1.15.4/go.mod h1:oN7C2uIZKhxCLiAAijKUCuHLZbIt/ghYEo8MqwD/Ty4= -cloud.google.com/go/security v1.15.5/go.mod h1:KS6X2eG3ynWjqcIX976fuToN5juVkF6Ra6c7MPnldtc= -cloud.google.com/go/securitycenter v1.19.0 h1:AF3c2s3awNTMoBtMX3oCUoOMmGlYxGOeuXSYHNBkf14= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/securitycenter v1.24.2/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.3/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.4/go.mod h1:PSccin+o1EMYKcFQzz9HMMnZ2r9+7jbc+LvPjXhpwcU= -cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= -cloud.google.com/go/servicedirectory v1.9.0 h1:SJwk0XX2e26o25ObYUORXx6torSFiYgsGkWSkZgkoSU= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.3/go.mod h1:LV+cHkomRLr67YoQy3Xq2tUXBGOs5z5bPofdq7qtiAw= -cloud.google.com/go/servicedirectory v1.11.4/go.mod h1:Bz2T9t+/Ehg6x+Y7Ycq5xiShYLD96NfEsWNHyitj1qM= -cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= -cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= -cloud.google.com/go/shell v1.6.0 h1:wT0Uw7ib7+AgZST9eCDygwTJn4+bHMDtZo5fh7kGWDU= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/shell v1.7.4/go.mod h1:yLeXB8eKLxw0dpEmXQ/FjriYrBijNsONpwnWsdPqlKM= -cloud.google.com/go/shell v1.7.5/go.mod h1:hL2++7F47/IfpfTO53KYf1EC+F56k3ThfNEXd4zcuiE= -cloud.google.com/go/spanner v1.45.0 h1:7VdjZ8zj4sHbDw55atp5dfY6kn1j9sam9DRNpPQhqR4= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.51.0/go.mod h1:c5KNo5LQ1X5tJwma9rSQZsXNBDNvj4/n8BVc3LNahq0= -cloud.google.com/go/spanner v1.55.0/go.mod h1:HXEznMUVhC+PC+HDyo9YFG2Ajj5BQDkcbqB9Z2Ffxi0= -cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0= -cloud.google.com/go/spanner v1.57.0/go.mod h1:aXQ5QDdhPRIqVhYmnkAdwPYvj/DRN0FguclhEWw+jOo= -cloud.google.com/go/speech v1.15.0 h1:JEVoWGNnTF128kNty7T4aG4eqv2z86yiMJPT9Zjp+iw= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= -cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc= -cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs= -cloud.google.com/go/talent v1.5.0 h1:nI9sVZPjMKiO2q3Uu0KhTDVov3Xrlpt63fghP9XjyEM= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/talent v1.6.5/go.mod h1:Mf5cma696HmE+P2BWJ/ZwYqeJXEeU0UqjHFXVLadEDI= -cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ= -cloud.google.com/go/texttospeech v1.6.0 h1:H4g1ULStsbVtalbZGktyzXzw6jP26RjVGYx9RaYjBzc= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/texttospeech v1.7.4/go.mod h1:vgv0002WvR4liGuSd5BJbWy4nDn5Ozco0uJymY5+U74= -cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M= -cloud.google.com/go/tpu v1.5.0 h1:/34T6CbSi+kTv5E19Q9zbU/ix8IviInZpzwz3rsFE+A= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/tpu v1.6.4/go.mod h1:NAm9q3Rq2wIlGnOhpYICNI7+bpBebMJbh0yyp3aNw1Y= -cloud.google.com/go/tpu v1.6.5/go.mod h1:P9DFOEBIBhuEcZhXi+wPoVy/cji+0ICFi4TtTkMHSSs= -cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= -cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= -cloud.google.com/go/translate v1.7.0 h1:GvLP4oQ4uPdChBmBaUSa/SaZxCdyWELtlAaKzpHsXdA= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.3/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.1/go.mod h1:adGZcQNom/3ogU65N9UXHOnnSvjPwA/jKQUMnsYXOyk= -cloud.google.com/go/video v1.15.0 h1:upIbnGI0ZgACm58HPjAeBMleW3sl5cT84AbYQ8PWOgM= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= -cloud.google.com/go/video v1.20.4/go.mod h1:LyUVjyW+Bwj7dh3UJnUGZfyqjEto9DnrvTe1f/+QrW0= -cloud.google.com/go/videointelligence v1.10.0 h1:Uh5BdoET8XXqXX2uXIahGb+wTKbLkGH7s4GXR58RrG8= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= -cloud.google.com/go/videointelligence v1.11.4/go.mod h1:kPBMAYsTPFiQxMLmmjpcZUMklJp3nC9+ipJJtprccD8= -cloud.google.com/go/videointelligence v1.11.5/go.mod h1:/PkeQjpRponmOerPeJxNPuxvi12HlW7Em0lJO14FC3I= -cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= -cloud.google.com/go/vision/v2 v2.7.0 h1:8C8RXUJoflCI4yVdqhTy9tRyygSHmp60aP363z23HKg= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vision/v2 v2.7.5/go.mod h1:GcviprJLFfK9OLf0z8Gm6lQb6ZFUulvpZws+mm6yPLM= -cloud.google.com/go/vision/v2 v2.8.0/go.mod h1:ocqDiA2j97pvgogdyhoxiQp2ZkDCyr0HWpicywGGRhU= -cloud.google.com/go/vmmigration v1.6.0 h1:Azs5WKtfOC8pxvkyrDvt7J0/4DYBch0cVbuFfCCFt5k= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmmigration v1.7.4/go.mod h1:yBXCmiLaB99hEl/G9ZooNx2GyzgsjKnw5fWcINRgD70= -cloud.google.com/go/vmmigration v1.7.5/go.mod h1:pkvO6huVnVWzkFioxSghZxIGcsstDvYiVCxQ9ZH3eYI= -cloud.google.com/go/vmwareengine v0.3.0 h1:b0NBu7S294l0gmtrT0nOJneMYgZapr5x9tVWvgDoVEM= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.3/go.mod h1:QSpdZ1stlbfKtyt6Iu19M6XRxjmXO+vb5a/R6Fvy2y4= -cloud.google.com/go/vmwareengine v1.1.1/go.mod h1:nMpdsIVkUrSaX8UvmnBhzVzG7PPvNYc5BszcvIVudYs= -cloud.google.com/go/vpcaccess v1.6.0 h1:FOe6CuiQD3BhHJWt7E8QlbBcaIzVRddupwJlp7eqmn4= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= -cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmGu6bb4IoMKk= -cloud.google.com/go/vpcaccess v1.7.5/go.mod h1:slc5ZRvvjP78c2dnL7m4l4R9GwL3wDLcpIWz6P/ziig= -cloud.google.com/go/webrisk v1.8.0 h1:IY+L2+UwxcVm2zayMAtBhZleecdIFLiC+QJMzgb0kT0= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= -cloud.google.com/go/webrisk v1.9.5/go.mod h1:aako0Fzep1Q714cPEM5E+mtYX8/jsfegAuS8aivxy3U= -cloud.google.com/go/websecurityscanner v1.5.0 h1:AHC1xmaNMOZtNqxI9Rmm87IJEyPaRkOxeI0gpAacXGk= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= -cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= -cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ= -cloud.google.com/go/workflows v1.10.0 h1:FfGp9w0cYnaKZJhUOMqCOJCYT/WlvYBfTQhFWV3sRKI= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= -cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= -gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho= -git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= -github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= -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/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= -github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= -github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= -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= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89 h1:aPflPkRFkVwbW6dmcVqfgwp1i+UWGFH6VgR1Jim5Ygc= -github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= -github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= -github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= -github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= -github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= -github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= -github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= -github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= -github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= -github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getsentry/sentry-go v0.21.0 h1:c9l5F1nPF30JIppulk4veau90PK6Smu3abgVtVQWon4= github.com/getsentry/sentry-go v0.21.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= -github.com/go-fonts/latin-modern v0.2.0 h1:5/Tv1Ek/QCr20C6ZOz15vw3g7GELYL98KWr8Hgo+3vk= -github.com/go-fonts/liberation v0.2.0 h1:jAkAWJP4S+OsrPLZM4/eC9iW7CtHy+HBXrEwZXWo5VM= -github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.5/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= -github.com/go-openapi/swag v0.22.6/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= -github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= -github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= -github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= -github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto= -github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= -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/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= -github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= -github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= -github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= -github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= -github.com/lyft/protoc-gen-star/v2 v2.0.1 h1:keaAo8hRuAT0O3DfJ/wM3rufbAjGeJ1lAtWZHDjKGB0= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= -github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm/Q= github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncabatoff/fakescraper v0.0.0-20201102132415-4b37ba603d65 h1:Og+dVkxEQNvRGU2vUKeOwYT2UJ+pEaDMWB6tIQnIh6A= github.com/ncabatoff/fakescraper v0.0.0-20201102132415-4b37ba603d65/go.mod h1:Tx6UMSMyIsjLG/VU/F6xA1+0XI+/f9o1dGJnf1l+bPg= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/onsi/ginkgo/v2 v/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= -github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= -github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= -github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ= -github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= -github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/uwu-tools/magex v0.10.0/go.mod h1:TrSEhrL1xHfJVy6n05AUwFdcQndgwrbgL5ybPNKWmVY= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= -go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= -go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0= go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU= -go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= -go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= -go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ= go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= -go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4= -go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= -go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8= go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg= -go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= -go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= -go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= -go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM= -go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= -go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M= go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0= -go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA= -go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc= -go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA= go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw= -go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg= -go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo= -go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok= go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 h1:KfYpVmrjI7JuToy5k8XV3nkapjWx48k4E4JOtVstzQI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/dl v0.0.0-20190829154251-82a15e2f2ead h1:jeP6FgaSLNTMP+Yri3qjlACywQLye+huGLmNGhBzm6k= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= -gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= -google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= -google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= -google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc h1:g3hIDl0jRNd9PPTs2uBzYuaD5mQuwOkZY0vSc0LR32o= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231212172506-995d672761c0/go.mod h1:guYXGPwC6jwxgWKW5Y405fKWOFNwlvUlUnzyp9i0uqo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= -gopkg.in/evanphx/json-patch.v5 v5.6.0 h1:BMT6KIwBD9CaU91PJCZIe46bDmBWa9ynTQgJIOpfQBk= -gopkg.in/evanphx/json-patch.v5 v5.6.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.29.3/go.mod h1:TBGbJKpRUMk59neTMDMddjIDL+D4HuFUbpuiuzmOPg0= -k8s.io/kms v0.30.0 h1:ZlnD/ei5lpvUlPw6eLfVvH7d8i9qZ6HwUQgydNVks8g= -k8s.io/kms v0.30.0/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4= -k8s.io/kms v0.30.1/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4= -k8s.io/kms v0.31.0 h1:KchILPfB1ZE+ka7223mpU5zeFNkmb45jl7RHnlImUaI= -k8s.io/kms v0.31.0/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= k8s.io/kms v0.31.2/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= -modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= -modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= -rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 h1:/U5vjBbQn3RChhv7P11uhYvCSm5G2GaIi5AIGBS6r4c= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/images/kube-webhook-certgen/rootfs/go.mod b/images/kube-webhook-certgen/rootfs/go.mod index 4efc9c6d3e..2d6541291e 100644 --- a/images/kube-webhook-certgen/rootfs/go.mod +++ b/images/kube-webhook-certgen/rootfs/go.mod @@ -14,12 +14,12 @@ require ( require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.3 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/swag v0.22.9 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect @@ -40,7 +40,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect @@ -51,7 +51,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/images/kube-webhook-certgen/rootfs/go.sum b/images/kube-webhook-certgen/rootfs/go.sum index fae381cc43..0a1c8284c4 100644 --- a/images/kube-webhook-certgen/rootfs/go.sum +++ b/images/kube-webhook-certgen/rootfs/go.sum @@ -3,18 +3,17 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.11.3 h1:yagOQz/38xJmcNeZJtrUcKjkHRltIaIFXKWeG1SkWGE= -github.com/emicklei/go-restful/v3 v3.11.3/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= -github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -102,8 +101,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -156,8 +154,7 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-aggregator v0.31.2 h1:Uw1zUP2D/4wiSjKWVVzSOcCGLuW/+IdRwjjC0FJooYU= k8s.io/kube-aggregator v0.31.2/go.mod h1:41/VIXH+/Qcg9ERNAY6bRF/WQR6xL1wFgYagdHac1X4= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/internal/ingress/annotations/annotations_test.go b/internal/ingress/annotations/annotations_test.go index 5df3cdc0ee..0c041cc2ca 100644 --- a/internal/ingress/annotations/annotations_test.go +++ b/internal/ingress/annotations/annotations_test.go @@ -133,7 +133,6 @@ func TestSSLPassthrough(t *testing.T) { ec := NewAnnotationExtractor(mockCfg{}) ing := buildIngress() - //nolint:goconst //already a constant fooAnns := []struct { annotations map[string]string er bool diff --git a/internal/ingress/annotations/parser/validators.go b/internal/ingress/annotations/parser/validators.go index 31524508f5..0be4d4fe33 100644 --- a/internal/ingress/annotations/parser/validators.go +++ b/internal/ingress/annotations/parser/validators.go @@ -49,8 +49,6 @@ var ( // IsValidRegex checks if the tested string can be used as a regex, but without any weird character. // It includes regex characters for paths that may contain regexes -// -//nolint:goconst //already a constant var IsValidRegex = regexp.MustCompile("^[/" + alphaNumericChars + regexEnabledChars + "]*$") // SizeRegex validates sizes understood by NGINX, like 1000, 100k, 1000M diff --git a/internal/ingress/controller/controller.go b/internal/ingress/controller/controller.go index aa8f4c4b92..63ea76dcff 100644 --- a/internal/ingress/controller/controller.go +++ b/internal/ingress/controller/controller.go @@ -61,89 +61,59 @@ const ( // Configuration contains all the settings required by an Ingress controller type Configuration struct { - APIServerHost string - RootCAFile string - - KubeConfigFile string - - Client clientset.Interface - - ResyncPeriod time.Duration - - ConfigMapName string - DefaultService string - - Namespace string - - WatchNamespaceSelector labels.Selector - - // +optional - TCPConfigMapName string - // +optional - UDPConfigMapName string - - DefaultSSLCertificate string - - // +optional - PublishService string - PublishStatusAddress string - - UpdateStatus bool - UseNodeInternalIP bool - ElectionID string - ElectionTTL time.Duration - UpdateStatusOnShutdown bool - - HealthCheckHost string - ListenPorts *ngx_config.ListenPorts - - DisableServiceExternalName bool - - EnableSSLPassthrough bool - - DisableLeaderElection bool - - EnableProfiling bool - - EnableMetrics bool - MetricsPerHost bool - MetricsPerUndefinedHost bool - MetricsBuckets *collectors.HistogramBuckets - MetricsBucketFactor float64 - MetricsMaxBuckets uint32 - ReportStatusClasses bool - ExcludeSocketMetrics []string - - FakeCertificate *ingress.SSLCert - - SyncRateLimit float32 - - DisableCatchAll bool - - IngressClassConfiguration *ingressclass.Configuration - - ValidationWebhook string - ValidationWebhookCertPath string - ValidationWebhookKeyPath string - DisableFullValidationTest bool - - GlobalExternalAuth *ngx_config.GlobalExternalAuth - MaxmindEditionFiles *[]string - - MonitorMaxBatchSize int - - PostShutdownGracePeriod int - ShutdownGracePeriod int - - InternalLoggerAddress string - IsChroot bool - DeepInspector bool - + APIServerHost string + RootCAFile string + KubeConfigFile string + Client clientset.Interface + ResyncPeriod time.Duration + ConfigMapName string + DefaultService string + Namespace string + WatchNamespaceSelector labels.Selector + TCPConfigMapName string + UDPConfigMapName string + DefaultSSLCertificate string + PublishService string + PublishStatusAddress string + UpdateStatus bool + UseNodeInternalIP bool + ElectionID string + ElectionTTL time.Duration + UpdateStatusOnShutdown bool + HealthCheckHost string + ListenPorts *ngx_config.ListenPorts + DisableServiceExternalName bool + EnableSSLPassthrough bool + DisableLeaderElection bool + EnableProfiling bool + EnableMetrics bool + MetricsPerHost bool + MetricsPerUndefinedHost bool + MetricsBuckets *collectors.HistogramBuckets + MetricsBucketFactor float64 + MetricsMaxBuckets uint32 + ReportStatusClasses bool + ExcludeSocketMetrics []string + FakeCertificate *ingress.SSLCert + SyncRateLimit float32 + DisableCatchAll bool + IngressClassConfiguration *ingressclass.Configuration + ValidationWebhook string + ValidationWebhookCertPath string + ValidationWebhookKeyPath string + DisableFullValidationTest bool + GlobalExternalAuth *ngx_config.GlobalExternalAuth + MaxmindEditionFiles *[]string + MonitorMaxBatchSize int + PostShutdownGracePeriod int + ShutdownGracePeriod int + InternalLoggerAddress string + IsChroot bool + DeepInspector bool DynamicConfigurationRetries int - - DisableSyncEvents bool - - EnableTopologyAwareRouting bool + DisableSyncEvents bool + EnableTopologyAwareRouting bool + ConfigurationTemplateEngine string } func getIngressPodZone(svc *apiv1.Service) string { diff --git a/internal/ingress/controller/controller_test.go b/internal/ingress/controller/controller_test.go index 9d3fea4708..022f23e8eb 100644 --- a/internal/ingress/controller/controller_test.go +++ b/internal/ingress/controller/controller_test.go @@ -157,6 +157,10 @@ func (ntc testNginxTestCommand) Test(cfg string) ([]byte, error) { type fakeTemplate struct{} +func (fakeTemplate) Validate(_ string) error { + return nil +} + func (fakeTemplate) Write(conf *ngx_config.TemplateConfig) ([]byte, error) { r := []byte{} for _, s := range conf.Servers { diff --git a/internal/ingress/controller/nginx.go b/internal/ingress/controller/nginx.go index 20fad5afb8..9559790ed4 100644 --- a/internal/ingress/controller/nginx.go +++ b/internal/ingress/controller/nginx.go @@ -52,6 +52,7 @@ import ( "k8s.io/ingress-nginx/internal/ingress/controller/process" "k8s.io/ingress-nginx/internal/ingress/controller/store" ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane" "k8s.io/ingress-nginx/internal/ingress/metric" "k8s.io/ingress-nginx/internal/ingress/status" ing_net "k8s.io/ingress-nginx/internal/net" @@ -70,6 +71,7 @@ import ( const ( tempNginxPattern = "nginx-cfg" emptyUID = "-1" + goTemplateEngine = "go-template" ) // NewNGINXController creates a new NGINX Ingress controller. @@ -158,6 +160,9 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro } onTemplateChange := func() { + if config.ConfigurationTemplateEngine != goTemplateEngine { + return + } template, err := ngx_template.NewTemplate(nginx.TemplatePath) if err != nil { // this error is different from the rest because it must be clear why nginx is not working @@ -170,18 +175,28 @@ func NewNGINXController(config *Configuration, mc metric.Collector) *NGINXContro n.syncQueue.EnqueueTask(task.GetDummyObject("template-change")) } - ngxTpl, err := ngx_template.NewTemplate(nginx.TemplatePath) - if err != nil { - klog.Fatalf("Invalid NGINX configuration template: %v", err) + var ngxTpl ngx_template.Writer + switch config.ConfigurationTemplateEngine { + case goTemplateEngine: + ngxTpl, err = ngx_template.NewTemplate(nginx.TemplatePath) + if err != nil { + klog.Fatalf("Invalid NGINX configuration template: %v", err) + } + _, err = file.NewFileWatcher(nginx.TemplatePath, onTemplateChange) + if err != nil { + klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, err) + } + case "crossplane": + ngxTpl, err = crossplane.NewTemplate() + if err != nil { + klog.Fatalf("Invalid NGINX configuration template: %v", err) + } + default: + klog.Fatal("Invalid template engine, please use 'go-template' or 'crossplane'") } n.t = ngxTpl - _, err = file.NewFileWatcher(nginx.TemplatePath, onTemplateChange) - if err != nil { - klog.Fatalf("Error creating file watcher for %v: %v", nginx.TemplatePath, err) - } - filesToWatch := []string{} if err := os.Mkdir("/etc/ingress-controller/geoip/", 0o755); err != nil && !os.IsExist(err) { @@ -652,6 +667,11 @@ func (n *NGINXController) testTemplate(cfg []byte) error { if err != nil { return err } + + if err := n.t.Validate(tmpfile.Name()); err != nil { + return fmt.Errorf("error during template validation: %w", err) + } + out, err := n.command.Test(tmpfile.Name()) if err != nil { // this error is different from the rest because it must be clear why nginx is not working @@ -868,11 +888,13 @@ func (n *NGINXController) configureDynamically(pcfg *ingress.Configuration) erro } } - streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) - if streamConfigurationChanged { - err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints) - if err != nil { - return err + if n.cfg.ConfigurationTemplateEngine == goTemplateEngine { + streamConfigurationChanged := !reflect.DeepEqual(n.runningConfig.TCPEndpoints, pcfg.TCPEndpoints) || !reflect.DeepEqual(n.runningConfig.UDPEndpoints, pcfg.UDPEndpoints) + if streamConfigurationChanged { + err := updateStreamConfiguration(pcfg.TCPEndpoints, pcfg.UDPEndpoints) + if err != nil { + return err + } } } diff --git a/internal/ingress/controller/template/crossplane/authlocation.go b/internal/ingress/controller/template/crossplane/authlocation.go new file mode 100644 index 0000000000..113f288785 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/authlocation.go @@ -0,0 +1,228 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "fmt" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "k8s.io/ingress-nginx/internal/ingress/annotations/authreq" + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/pkg/apis/ingress" +) + +type externalAuth struct { + URL string `json:"url"` + // Host contains the hostname defined in the URL + Host string `json:"host"` + SigninURL string `json:"signinUrl"` + SigninURLRedirectParam string `json:"signinUrlRedirectParam,omitempty"` + Method string `json:"method"` + ResponseHeaders []string `json:"responseHeaders,omitempty"` + RequestRedirect string `json:"requestRedirect"` + AuthSnippet string `json:"authSnippet"` + AuthCacheKey string `json:"authCacheKey"` + AuthCacheDuration []string `json:"authCacheDuration"` + KeepaliveConnections int `json:"keepaliveConnections"` + KeepaliveShareVars bool `json:"keepaliveShareVars"` + KeepaliveRequests int `json:"keepaliveRequests"` + KeepaliveTimeout int `json:"keepaliveTimeout"` + ProxySetHeaders map[string]string `json:"proxySetHeaders,omitempty"` + AlwaysSetCookie bool `json:"alwaysSetCookie,omitempty"` +} + +func buildExternalAuth(cfg any) *externalAuth { + switch v := cfg.(type) { + case config.GlobalExternalAuth: + return &externalAuth{ + AlwaysSetCookie: v.AlwaysSetCookie, + AuthCacheKey: v.AuthCacheKey, + AuthCacheDuration: v.AuthCacheDuration, + Method: v.Method, + Host: v.Host, + RequestRedirect: v.RequestRedirect, + ProxySetHeaders: v.ProxySetHeaders, + ResponseHeaders: v.ResponseHeaders, + URL: v.URL, + SigninURL: v.SigninURL, + SigninURLRedirectParam: v.SigninURLRedirectParam, + } + case authreq.Config: + return &externalAuth{ + AlwaysSetCookie: v.AlwaysSetCookie, + AuthCacheKey: v.AuthCacheKey, + AuthCacheDuration: v.AuthCacheDuration, + Method: v.Method, + Host: v.Host, + RequestRedirect: v.RequestRedirect, + ProxySetHeaders: v.ProxySetHeaders, + ResponseHeaders: v.ResponseHeaders, + URL: v.URL, + SigninURL: v.SigninURL, + SigninURLRedirectParam: v.SigninURLRedirectParam, + KeepaliveShareVars: v.KeepaliveShareVars, + KeepaliveConnections: v.KeepaliveConnections, + KeepaliveRequests: v.KeepaliveRequests, + KeepaliveTimeout: v.KeepaliveTimeout, + } + default: + return nil + } +} + +func (c *Template) buildAuthLocation(server *ingress.Server, + location *ingress.Location, locationConfig locationCfg, +) *ngx_crossplane.Directive { + locationDirectives := ngx_crossplane.Directives{ + buildDirective("internal"), + } + + if c.tplConfig.Cfg.EnableOpentelemetry || location.Opentelemetry.Enabled { + locationDirectives = append(locationDirectives, + buildDirective("opentelemetry", "on"), + buildDirective("opentelemetry_propagate"), + ) + } + + if !c.tplConfig.Cfg.EnableAuthAccessLog { + locationDirectives = append(locationDirectives, buildDirective("access_log", "off")) + } + + if locationConfig.externalAuth.AuthCacheKey != "" { + locationDirectives = append(locationDirectives, + buildDirective("set", "$tmp_cache_key", fmt.Sprintf("%s%s%s", server.Hostname, locationConfig.authPath, locationConfig.externalAuth.AuthCacheKey)), + buildDirective("set", "$cache_key", ""), + buildDirective("rewrite_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_rewrite_auth.lua"), + buildDirective("proxy_cache", "auth_cache"), + buildDirective("proxy_cache_key", "$cache_key"), + ) + for i := range locationConfig.externalAuth.AuthCacheDuration { + locationDirectives = append(locationDirectives, + buildDirective("proxy_cache_valid", strings.Split(locationConfig.externalAuth.AuthCacheDuration[i], " ")), + ) + } + } + + /* + ngx_auth_request module overrides variables in the parent request, + therefore we have to explicitly set this variable again so that when the parent request + resumes it has the correct value set for this variable so that Lua can pick backend correctly + */ + locationDirectives = append(locationDirectives, + buildDirective("set", "$proxy_upstream_name", location.Backend), + buildDirective("proxy_pass_request_body", "off"), + buildDirective("proxy_ssl_server_name", "on"), + buildDirective("proxy_pass_request_headers", "on"), + buildDirective("proxy_set_header", "Content-Length", ""), + buildDirective("proxy_set_header", "X-Forwarded-Proto", ""), + buildDirective("proxy_set_header", "X-Request-ID", "$req_id"), + buildDirective("proxy_set_header", "Host", locationConfig.externalAuth.Host), + buildDirective("proxy_set_header", "X-Original-URL", "$scheme://$http_host$request_uri"), + buildDirective("proxy_set_header", "X-Original-Method", "$request_method"), + buildDirective("proxy_set_header", "X-Sent-From", "nginx-ingress-controller"), + buildDirective("proxy_set_header", "X-Real-IP", "$remote_addr"), + ) + + if locationConfig.externalAuth.Method != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_method", locationConfig.externalAuth.Method), + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri"), + buildDirective("proxy_set_header", "X-Scheme", "$pass_access_scheme")) + } + + if c.tplConfig.Cfg.UseForwardedHeaders && c.tplConfig.Cfg.ComputeFullForwardedFor { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Forwarded-For", "$full_x_forwarded_for")) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Forwarded-For", "$remote_addr")) + } + + if locationConfig.externalAuth.RequestRedirect != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Auth-Request-Redirect", locationConfig.externalAuth.RequestRedirect)) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Auth-Request-Redirect", "$request_uri")) + } + + if locationConfig.externalAuth.Method != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri"), + buildDirective("proxy_set_header", "X-Scheme", "$pass_access_scheme")) + } + + if locationConfig.externalAuth.AuthCacheKey != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffering", "on")) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffering", location.Proxy.ProxyBuffering)) + } + + locationDirectives = append(locationDirectives, + buildDirective("proxy_buffer_size", location.Proxy.BufferSize), + buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize), + buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering), + ) + + if isValidByteSize(location.Proxy.BodySize, true) { + locationDirectives = append(locationDirectives, + buildDirective("client_max_body_size", location.Proxy.BodySize)) + } + + if isValidByteSize(location.ClientBodyBufferSize, false) { + locationDirectives = append(locationDirectives, + buildDirective("client_body_buffer_size", location.ClientBodyBufferSize)) + } + + if server.CertificateAuth.CAFileName != "" { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "ssl-client-verify", "$ssl_client_verify"), + buildDirective("proxy_set_header", "ssl-client-subject-dn", "$ssl_client_s_dn"), + buildDirective("proxy_set_header", "ssl-client-issuer-dn", "$ssl_client_i_dn"), + ) + + if server.CertificateAuth.PassCertToUpstream { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", "ssl-client-cert", "$ssl_client_escaped_cert")) + } + } + + for name, value := range locationConfig.externalAuth.ProxySetHeaders { + locationDirectives = append(locationDirectives, + buildDirective("proxy_set_header", name, value)) + } + + if locationConfig.applyAuthUpstream && locationConfig.applyGlobalAuth { + locationDirectives = append(locationDirectives, + buildDirective("proxy_http_version", "1.1"), + buildDirective("proxy_set_header", "Connection", ""), + buildDirective("set", "$target", + changeHostPort(locationConfig.externalAuth.URL, buildAuthUpstreamName(location, server.Hostname)))) + } else { + locationDirectives = append(locationDirectives, + buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion), + buildDirective("set", "$target", locationConfig.externalAuth.URL)) + } + locationDirectives = append(locationDirectives, + buildDirective("proxy_pass", "$target")) + + return buildBlockDirective("location", + []string{"=", locationConfig.authPath}, locationDirectives) +} diff --git a/internal/ingress/controller/template/crossplane/config.go b/internal/ingress/controller/template/crossplane/config.go new file mode 100644 index 0000000000..5ad5467652 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/config.go @@ -0,0 +1,62 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" +) + +func (c *Template) buildConfig() { + // Write basic directives + config := &ngx_crossplane.Config{ + Parsed: ngx_crossplane.Directives{ + buildDirective("pid", c.tplConfig.PID), + buildDirective("daemon", "off"), + buildDirective("worker_processes", c.tplConfig.Cfg.WorkerProcesses), + buildDirective("worker_rlimit_nofile", c.tplConfig.Cfg.MaxWorkerOpenFiles), + buildDirective("worker_shutdown_timeout", c.tplConfig.Cfg.WorkerShutdownTimeout), + }, + } + if c.tplConfig.Cfg.WorkerCPUAffinity != "" { + config.Parsed = append(config.Parsed, buildDirective("worker_cpu_affinity", strings.Split(c.tplConfig.Cfg.WorkerCPUAffinity, " "))) + } + + if c.tplConfig.Cfg.EnableBrotli { + config.Parsed = append(config.Parsed, + buildDirective("load_module", "/etc/nginx/modules/ngx_http_brotli_filter_module.so"), + buildDirective("load_module", "/etc/nginx/modules/ngx_http_brotli_static_module.so"), + ) + } + + if shouldLoadAuthDigestModule(c.tplConfig.Servers) { + config.Parsed = append(config.Parsed, buildDirective("load_module", "/etc/nginx/modules/ngx_http_auth_digest_module.so")) + } + + if c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers) { + config.Parsed = append(config.Parsed, buildDirective("load_module", "/etc/nginx/modules/otel_ngx_module.so")) + } + + if c.tplConfig.Cfg.UseGeoIP2 { + config.Parsed = append(config.Parsed, + buildDirective("load_module", "/etc/nginx/modules/ngx_http_geoip2_module.so"), + ) + } + + c.config = config +} diff --git a/internal/ingress/controller/template/crossplane/cors.go b/internal/ingress/controller/template/crossplane/cors.go new file mode 100644 index 0000000000..252fa17029 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/cors.go @@ -0,0 +1,78 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "fmt" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "k8s.io/ingress-nginx/internal/ingress/annotations/cors" +) + +func buildCorsDirectives(locationcors *cors.Config) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, 0) + if len(locationcors.CorsAllowOrigin) > 0 { + directives = append(directives, buildCorsOriginRegex(locationcors.CorsAllowOrigin)...) + } + directives = append(directives, + buildBlockDirective("if", + []string{"$request_method", "=", "OPTIONS"}, ngx_crossplane.Directives{ + buildDirective("set", "$cors", "${cors}options"), + }, + ), + commonCorsDirective(locationcors, false), + commonCorsDirective(locationcors, true), + ) + + return directives +} + +// commonCorsDirective builds the common cors directives for a location +func commonCorsDirective(cfg *cors.Config, options bool) *ngx_crossplane.Directive { + corsDir := "true" + if options { + corsDir = "trueoptions" + } + corsBlock := buildBlockDirective("if", []string{"$cors", "=", corsDir}, + ngx_crossplane.Directives{ + buildDirective("more_set_headers", "Access-Control-Allow-Origin: $http_origin"), + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Allow-Methods: %s", cfg.CorsAllowMethods)), + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Allow-Headers: %s", cfg.CorsAllowHeaders)), + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Max-Age: %d", cfg.CorsMaxAge)), + }, + ) + + if cfg.CorsAllowCredentials { + corsBlock.Block = append(corsBlock.Block, + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Allow-Credentials: %t", cfg.CorsAllowCredentials)), + ) + } + if cfg.CorsExposeHeaders != "" { + corsBlock.Block = append(corsBlock.Block, + buildDirective("more_set_headers", fmt.Sprintf("Access-Control-Expose-Headers: %s", cfg.CorsExposeHeaders)), + ) + } + + if options { + corsBlock.Block = append(corsBlock.Block, + buildDirective("more_set_headers", "Content-Type: text/plain charset=UTF-8"), + buildDirective("more_set_headers", "Content-Length: 0"), + buildDirective("return", "204"), + ) + } + return corsBlock +} diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go new file mode 100644 index 0000000000..200e6ec52d --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -0,0 +1,107 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "bytes" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane/extramodules" +) + +/* +Unsupported directives: +- opentelemetry +- modsecurity +- any stream directive (TCP/UDP forwarding) +*/ + +// On this case we will try to use the go ngx_crossplane to write the template instead of the template renderer + +type Template struct { + options *ngx_crossplane.BuildOptions + parseOptions *ngx_crossplane.ParseOptions + config *ngx_crossplane.Config + tplConfig *config.TemplateConfig + mimeFile string +} + +func NewTemplate() (*Template, error) { + lua := ngx_crossplane.Lua{} + buildOptions := &ngx_crossplane.BuildOptions{ + Builders: []ngx_crossplane.RegisterBuilder{ + lua.RegisterBuilder(), + }, + } + + parseOptions := &ngx_crossplane.ParseOptions{ + ParseComments: true, + ErrorOnUnknownDirectives: true, + StopParsingOnError: true, + DirectiveSources: []ngx_crossplane.MatchFunc{ + ngx_crossplane.DefaultDirectivesMatchFunc, + ngx_crossplane.MatchLuaLatest, + ngx_crossplane.MatchHeadersMoreLatest, + extramodules.BrotliMatchFn, + extramodules.OpentelemetryMatchFn, + extramodules.SetMiscMatchFn, + ngx_crossplane.MatchGeoip2Latest, + }, + LexOptions: ngx_crossplane.LexOptions{ + Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, + }, + } + + return &Template{ + mimeFile: "/etc/nginx/mime.types", + options: buildOptions, + parseOptions: parseOptions, + }, nil +} + +func (c *Template) SetMimeFile(file string) { + c.mimeFile = file +} + +func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { + c.tplConfig = conf + + // build root directives + c.buildConfig() + + // build events directive + c.buildEvents() + + // build http directive + c.buildHTTP() + + var buf bytes.Buffer + + err := ngx_crossplane.Build(&buf, *c.config, &ngx_crossplane.BuildOptions{}) + if err != nil { + return nil, err + } + + return buf.Bytes(), err +} + +func (c *Template) Validate(filename string) error { + _, err := ngx_crossplane.Parse(filename, c.parseOptions) + return err +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go new file mode 100644 index 0000000000..73093c1a8e --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go @@ -0,0 +1,92 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "testing" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "github.com/stretchr/testify/require" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" +) + +// THIS FILE SHOULD BE USED JUST FOR INTERNAL TESTS - Private functions + +func Test_Internal_buildEvents(t *testing.T) { + t.Run("should fill correctly events directives with defaults", func(t *testing.T) { + c := ngx_crossplane.Config{} + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + + expectedEvents := &ngx_crossplane.Config{ + File: "", + Parsed: ngx_crossplane.Directives{ + { + Directive: "events", + Block: ngx_crossplane.Directives{ + buildDirective("worker_connections", 16384), + buildDirective("use", "epoll"), + buildDirective("multi_accept", true), + }, + }, + }, + } + + cplane, err := NewTemplate() + require.NoError(t, err) + cplane.config = &c + cplane.tplConfig = tplConfig + cplane.buildEvents() + require.Equal(t, expectedEvents, cplane.config) + }) + + t.Run("should fill correctly events directives with specific values", func(t *testing.T) { + c := ngx_crossplane.Config{} + tplConfig := &config.TemplateConfig{ + Cfg: config.Configuration{ + MaxWorkerConnections: 50, + EnableMultiAccept: false, + DebugConnections: []string{"127.0.0.1/32", "192.168.0.10"}, + }, + } + + expectedEvents := &ngx_crossplane.Config{ + File: "", + Parsed: ngx_crossplane.Directives{ + { + Directive: "events", + Block: ngx_crossplane.Directives{ + buildDirective("worker_connections", 50), + buildDirective("use", "epoll"), + buildDirective("multi_accept", false), + buildDirective("debug_connection", "127.0.0.1/32"), + buildDirective("debug_connection", "192.168.0.10"), + }, + }, + }, + } + + cplane, err := NewTemplate() + require.NoError(t, err) + cplane.config = &c + cplane.tplConfig = tplConfig + cplane.buildEvents() + require.Equal(t, expectedEvents, cplane.config) + }) +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go new file mode 100644 index 0000000000..6d8d813368 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go @@ -0,0 +1,86 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "reflect" + "testing" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "github.com/stretchr/testify/require" +) + +// THIS FILE SHOULD BE USED JUST FOR INTERNAL TESTS - Private functions + +func Test_Internal_buildDirectives(t *testing.T) { + t.Run("should be able to run a directive with a single argument", func(t *testing.T) { + directive := buildDirective("somedirective", "bla") + require.Equal(t, directive.Directive, "somedirective", []string{"bla"}) + }) + t.Run("should be able to run a directive with multiple different arguments", func(t *testing.T) { + directive := buildDirective("somedirective", "bla", 5, true, seconds(10), []string{"xpto", "bla"}) + require.Equal(t, directive.Directive, "somedirective", []string{"bla", "5", "on", "10s", "xpto", "bla"}) + }) +} + +func Test_Internal_buildMapDirectives(t *testing.T) { + t.Run("should be able to run build a map directive with empty block", func(t *testing.T) { + directive := buildMapDirective("somedirective", "bla", ngx_crossplane.Directives{buildDirective("something", "otherstuff")}) + require.Equal(t, directive.Directive, "map") + require.Equal(t, directive.Args, []string{"somedirective", "bla"}) + require.Equal(t, directive.Block[0].Directive, "something") + require.Equal(t, directive.Block[0].Args, []string{"otherstuff"}) + }) +} + +func Test_Internal_boolToStr(t *testing.T) { + require.Equal(t, boolToStr(true), "on") + require.Equal(t, boolToStr(false), "off") +} + +func Test_Internal_buildCorsOriginRegex(t *testing.T) { + tests := []struct { + name string + corsOrigins []string + want ngx_crossplane.Directives + }{ + { + name: "wildcard returns a single directive", + corsOrigins: []string{"*"}, + want: ngx_crossplane.Directives{ + buildDirective("set", "$http_origin", "*"), + buildDirective("set", "$cors", "true"), + }, + }, + { + name: "multiple hosts should be changed properly", + corsOrigins: []string{"*.xpto.com", " lalala.com"}, + want: ngx_crossplane.Directives{ + buildBlockDirective("if", []string{"$http_origin", "~*", "(([A-Za-z0-9\\-]+\\.xpto\\.com)|(lalala\\.com))$"}, + ngx_crossplane.Directives{buildDirective("set", "$cors", "true")}, + ), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := buildCorsOriginRegex(tt.corsOrigins); !reflect.DeepEqual(got, tt.want) { + t.Errorf("buildCorsOriginRegex() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go new file mode 100644 index 0000000000..c52077c757 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -0,0 +1,370 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane_test + +import ( + "net" + "os" + "testing" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "github.com/stretchr/testify/require" + + "k8s.io/ingress-nginx/internal/ingress/annotations/authreq" + "k8s.io/ingress-nginx/internal/ingress/annotations/authtls" + "k8s.io/ingress-nginx/internal/ingress/annotations/cors" + "k8s.io/ingress-nginx/internal/ingress/annotations/mirror" + "k8s.io/ingress-nginx/internal/ingress/annotations/proxy" + "k8s.io/ingress-nginx/internal/ingress/annotations/proxyssl" + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane" + "k8s.io/ingress-nginx/internal/ingress/controller/template/crossplane/extramodules" + "k8s.io/ingress-nginx/internal/ingress/resolver" + "k8s.io/ingress-nginx/pkg/apis/ingress" + utilingress "k8s.io/ingress-nginx/pkg/util/ingress" +) + +const mockMimeTypes = ` +types { + text/html html htm shtml; + text/css css; + text/xml xml; +} +` + +func defaultConfig() *config.TemplateConfig { + tplConfig := &config.TemplateConfig{ + Cfg: config.NewDefault(), + } + tplConfig.ListenPorts = &config.ListenPorts{ + HTTP: 80, + HTTPS: 443, + Health: 10245, + Default: 8080, + SSLProxy: 442, + } + defaultCertificate := &ingress.SSLCert{ + PemFileName: "bla.crt", + PemCertKey: "bla.key", + } + tplConfig.StatusPort = 10246 + tplConfig.StatusPath = "/status" + tplConfig.HealthzURI = "/healthz" + tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate + return tplConfig +} + +var resolvers = []net.IP{net.ParseIP("::1"), net.ParseIP("192.168.20.10")} + +// TestTemplate should be a roundtrip test. +// We should initialize the scenarios based on the template configuration +// Then Parse and write a crossplane configuration, and roundtrip/parse back to check +// if the directives matches +// we should ignore line numbers and comments +func TestCrossplaneTemplate(t *testing.T) { + lua := ngx_crossplane.Lua{} + options := ngx_crossplane.ParseOptions{ + ParseComments: true, + ErrorOnUnknownDirectives: true, + StopParsingOnError: true, + DirectiveSources: []ngx_crossplane.MatchFunc{ + ngx_crossplane.DefaultDirectivesMatchFunc, + ngx_crossplane.MatchLuaLatest, + ngx_crossplane.MatchHeadersMoreLatest, + extramodules.BrotliMatchFn, + extramodules.OpentelemetryMatchFn, + extramodules.SetMiscMatchFn, + ngx_crossplane.MatchGeoip2Latest, + }, + LexOptions: ngx_crossplane.LexOptions{ + Lexers: []ngx_crossplane.RegisterLexer{lua.RegisterLexer()}, + }, + } + + mimeFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = mimeFile.WriteString(mockMimeTypes) + require.NoError(t, err) + require.NoError(t, mimeFile.Close()) + + tpl, err := crossplane.NewTemplate() + require.NoError(t, err) + + t.Run("it should be able to marshall and unmarshall the default configuration", func(t *testing.T) { + tplConfig := defaultConfig() + tplConfig.Cfg.EnableBrotli = true + tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + tplConfig.Cfg.Resolver = resolvers + tplConfig.Cfg.DisableIpv6DNS = true + tplConfig.Cfg.UseForwardedHeaders = true + tplConfig.Cfg.LogFormatEscapeNone = true + tplConfig.Cfg.DisableAccessLog = true + tplConfig.Cfg.UpstreamKeepaliveConnections = 0 + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) + + t.Run("it should be able to marshall and unmarshall with server config", func(t *testing.T) { + tplConfig := defaultConfig() + tplConfig.EnableMetrics = true + tplConfig.Cfg.EnableBrotli = true + tplConfig.Cfg.EnableOpentelemetry = true + tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + tplConfig.Cfg.Resolver = resolvers + tplConfig.Cfg.DisableIpv6DNS = true + tplConfig.IsIPV6Enabled = true + tplConfig.Cfg.BindAddressIpv6 = []string{"[::cabe:ca]"} + tplConfig.Cfg.BlockReferers = []string{"testlala.com"} + tplConfig.Cfg.ReusePort = true + tplConfig.BacklogSize = 5 + tplConfig.Cfg.BlockUserAgents = []string{"somebrowser"} + tplConfig.Cfg.UseForwardedHeaders = true + tplConfig.Cfg.LogFormatEscapeNone = true + tplConfig.Cfg.DisableAccessLog = true + tplConfig.Cfg.UpstreamKeepaliveConnections = 0 + tplConfig.Cfg.CustomHTTPErrors = []int{411, 412, 413} // Duplicated on purpose + tplConfig.RedirectServers = []*utilingress.Redirect{ + { + From: "www.xpto123.com", + To: "www.abcdefg.tld", + }, + } + tplConfig.Servers = []*ingress.Server{ + { + Hostname: "_", + }, + { + Hostname: "*.something.com", + Aliases: []string{"abc.com", "def.com"}, + Locations: []*ingress.Location{ + { + Mirror: mirror.Config{ + Source: "/mirror", + Host: "something.com", + Target: "http://www.mymirror.com", + RequestBody: "off", + }, + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + NextUpstream: "10.10.10.10", + }, + }, + { + DefaultBackendUpstreamName: "something", + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + NextUpstream: "10.10.10.10", + }, + CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! + }, + { + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + NextUpstream: "10.10.10.10", + }, + DefaultBackendUpstreamName: "otherthing", + CustomHTTPErrors: []int{403, 404, 403, 409}, // Duplicated on purpose! + }, + { + CorsConfig: cors.Config{ + CorsEnabled: true, + CorsAllowOrigin: []string{"xpto.com", "*.bla.com"}, + CorsAllowMethods: "GET,POST", + CorsAllowHeaders: "XPTO", + CorsMaxAge: 600, + CorsAllowCredentials: true, + CorsExposeHeaders: "XPTO", + }, + Backend: "somebackend", + ClientBodyBufferSize: "512k", + Proxy: proxy.Config{ + ProxyBuffering: "on", + RequestBuffering: "on", + BuffersNumber: 10, + BufferSize: "1024k", + ProxyHTTPVersion: "1.1", + NextUpstream: "10.10.10.10", + }, + ExternalAuth: authreq.Config{ + AuthCacheDuration: []string{"60s"}, + Host: "someauth.com", + URL: "http://someauth.com", + Method: "GET", + ProxySetHeaders: map[string]string{ + "someheader": "something", + }, + AuthCacheKey: "blabla", + SigninURL: "http://externallogin.tld", + }, + Path: "/xpto123", + }, + }, + }, + { + Hostname: "otherthing.com", + Aliases: []string{"abcde.com", "xpto.com"}, + CertificateAuth: authtls.Config{ + MatchCN: "CN=bla; listen xpto\"", + AuthSSLCert: resolver.AuthSSLCert{ + CAFileName: "/something/xpto.crt", + CRLFileName: "/something/xpto.crt", + }, + VerifyClient: "optional", + ValidationDepth: 2, + ErrorPage: "/xpto.html", + }, + ProxySSL: proxyssl.Config{ + AuthSSLCert: resolver.AuthSSLCert{ + CAFileName: "/something/xpto.crt", + PemFileName: "/something/mycert.crt", + }, + Ciphers: "HIGH:!aNULL:!MD5", + Protocols: "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3", + Verify: "on", + VerifyDepth: 2, + ProxySSLName: "xpto.com", + ProxySSLServerName: "on", + }, + SSLCiphers: "HIGH:!aNULL:", + SSLPreferServerCiphers: "on", + }, + } + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) + + t.Run("it should set the right logging configs", func(t *testing.T) { + tplConfig := defaultConfig() + tplConfig.Cfg.DisableAccessLog = false + tplConfig.Cfg.HTTPAccessLogPath = "/lalala.log" + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) + + t.Run("it should be able to marshall and unmarshall the specified configuration", func(t *testing.T) { + tplConfig := defaultConfig() + tplConfig.Cfg.WorkerCPUAffinity = "0001 0010 0100 1000" + tplConfig.Cfg.LuaSharedDicts = map[string]int{ + "configuration_data": 10240, + "certificate_data": 50, + } + tplConfig.Cfg.Resolver = resolvers + tplConfig.Cfg.DisableIpv6DNS = false + + tplConfig.Cfg.UseProxyProtocol = true + tplConfig.Cfg.ProxyRealIPCIDR = []string{"192.168.0.20", "200.200.200.200"} + tplConfig.Cfg.LogFormatEscapeJSON = true + + tplConfig.Cfg.GRPCBufferSizeKb = 10 // default 0 + + tplConfig.Cfg.HTTP2MaxHeaderSize = "10" // default "" + tplConfig.Cfg.HTTP2MaxFieldSize = "10" // default "" + tplConfig.Cfg.HTTP2MaxRequests = 1 // default 0 + + tplConfig.Cfg.UseGzip = true // default false + tplConfig.Cfg.GzipDisable = "enable" + + tplConfig.Cfg.ShowServerTokens = true // default false + + tplConfig.Cfg.DisableAccessLog = false // TODO: test true + tplConfig.Cfg.DisableHTTPAccessLog = false + tplConfig.Cfg.EnableSyslog = true + tplConfig.Cfg.SyslogHost = "localhost" + tplConfig.Cfg.SkipAccessLogURLs = []string{"aaa.a", "bbb.b"} + tplConfig.Cfg.SSLDHParam = "/some/dh.pem" + + // Example: openssl rand 80 | openssl enc -A -base64 + tplConfig.Cfg.SSLSessionTicketKey = "lOj3+7Xe21K9GapKqqPIw/gCQm5S4C2lK8pVne6drEik0QqOQHAw1AaPSMdbAvXx2zZKKPCEG98+g3hzftmrfnePSIvokIIE+hHto3Kj1HQ=" + + tplConfig.Cfg.CustomHTTPErrors = []int{1024, 2048} + + tplConfig.Cfg.AllowBackendServerHeader = true // default false + tplConfig.Cfg.BlockCIDRs = []string{"192.168.0.0/24", " 200.200.0.0/16 "} // default 0 + tplConfig.Cfg.BlockUserAgents = []string{"someuseragent", " another/user-agent "} // default 0 + tplConfig.Cfg.BlockReferers = []string{"someref", " anotherref", "escape\nref"} + + tplConfig.AddHeaders = map[string]string{ + "someheader": "xpto", + "anotherheader": "blabla", + } + + tplConfig.Cfg.EnableBrotli = true + tplConfig.Cfg.BrotliLevel = 7 + tplConfig.Cfg.BrotliMinLength = 2 + tplConfig.Cfg.BrotliTypes = "application/xml+rss application/atom+xml" + + tplConfig.Cfg.HideHeaders = []string{"x-fake-header", "x-another-fake-header"} + tplConfig.Cfg.UpstreamKeepaliveConnections = 15 + + tplConfig.Cfg.UpstreamKeepaliveConnections = 200 + tplConfig.Cfg.UpstreamKeepaliveTime = "60s" + tplConfig.Cfg.UpstreamKeepaliveTimeout = 200 + tplConfig.Cfg.UpstreamKeepaliveRequests = 15 + + tpl, err = crossplane.NewTemplate() + require.NoError(t, err) + + tpl.SetMimeFile(mimeFile.Name()) + content, err := tpl.Write(tplConfig) + require.NoError(t, err) + + tmpFile, err := os.CreateTemp("", "") + require.NoError(t, err) + _, err = tmpFile.Write(content) + require.NoError(t, err) + require.NoError(t, tmpFile.Close()) + + _, err = ngx_crossplane.Parse(tmpFile.Name(), &options) + require.NoError(t, err) + }) +} diff --git a/internal/ingress/controller/template/crossplane/events.go b/internal/ingress/controller/template/crossplane/events.go new file mode 100644 index 0000000000..ba0a76328a --- /dev/null +++ b/internal/ingress/controller/template/crossplane/events.go @@ -0,0 +1,36 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" +) + +func (c *Template) buildEvents() { + events := &ngx_crossplane.Directive{ + Directive: "events", + Block: ngx_crossplane.Directives{ + buildDirective("worker_connections", c.tplConfig.Cfg.MaxWorkerConnections), + buildDirective("use", "epoll"), + buildDirective("multi_accept", c.tplConfig.Cfg.EnableMultiAccept), + }, + } + for k := range c.tplConfig.Cfg.DebugConnections { + events.Block = append(events.Block, buildDirective("debug_connection", c.tplConfig.Cfg.DebugConnections[k])) + } + c.config.Parsed = append(c.config.Parsed, events) +} diff --git a/internal/ingress/controller/template/crossplane/extramodules/README.md b/internal/ingress/controller/template/crossplane/extramodules/README.md new file mode 100644 index 0000000000..b632776873 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/README.md @@ -0,0 +1,10 @@ +# Extra modules +This folder contains the extra modules used by ingress-nginx and not yet +supported by nginx-go-crossplane + +The generation of the files is done using go-crossplane generator + +## Brotli +``` +go run ./cmd/generate/ -src-path=ngx_brotli/ -directive-map-name=brotliDirectives -match-func-name=BrotliMatchFn > ../ingress-nginx/internal/ingress/controller/template/crossplane/extramodules/brotli.go +``` \ No newline at end of file diff --git a/internal/ingress/controller/template/crossplane/extramodules/analyze.go b/internal/ingress/controller/template/crossplane/extramodules/analyze.go new file mode 100644 index 0000000000..0b6d2f335a --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/analyze.go @@ -0,0 +1,78 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * Copyright (c) F5, Inc. + * + * This source code is licensed under the Apache License, Version 2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +// This file is an extraction from https://github.com/nginxinc/nginx-go-crossplane/blob/main/analyze.go +package extramodules + +// bit masks for different directive argument styles. +const ( + ngxConfNoArgs = 0x00000001 // 0 args + ngxConfTake1 = 0x00000002 // 1 args + ngxConfTake2 = 0x00000004 // 2 args + ngxConfTake3 = 0x00000008 // 3 args + ngxConfTake4 = 0x00000010 // 4 args + ngxConfTake5 = 0x00000020 // 5 args + ngxConfTake6 = 0x00000040 // 6 args + // ngxConfTake7 = 0x00000080 // 7 args (currently unused). + ngxConfBlock = 0x00000100 // followed by block + ngxConfExpr = 0x00000200 // directive followed by expression in parentheses `()` + ngxConfFlag = 0x00000400 // 'on' or 'off' + ngxConfAny = 0x00000800 // >=0 args + ngxConf1More = 0x00001000 // >=1 args + ngxConf2More = 0x00002000 // >=2 args + + // some helpful argument style aliases. + ngxConfTake12 = ngxConfTake1 | ngxConfTake2 + ngxConfTake13 = ngxConfTake1 | ngxConfTake3 + ngxConfTake23 = ngxConfTake2 | ngxConfTake3 + ngxConfTake34 = ngxConfTake3 | ngxConfTake4 + ngxConfTake123 = ngxConfTake12 | ngxConfTake3 + ngxConfTake1234 = ngxConfTake123 | ngxConfTake4 + + // bit masks for different directive locations. + ngxDirectConf = 0x00010000 // main file (not used) + ngxMgmtMainConf = 0x00020000 // mgmt // unique bitmask that may not match NGINX source + ngxMainConf = 0x00040000 // main context + ngxEventConf = 0x00080000 // events + ngxMailMainConf = 0x00100000 // mail + ngxMailSrvConf = 0x00200000 // mail > server + ngxStreamMainConf = 0x00400000 // stream + ngxStreamSrvConf = 0x00800000 // stream > server + ngxStreamUpsConf = 0x01000000 // stream > upstream + ngxHTTPMainConf = 0x02000000 // http + ngxHTTPSrvConf = 0x04000000 // http > server + ngxHTTPLocConf = 0x08000000 // http > location + ngxHTTPUpsConf = 0x10000000 // http > upstream + ngxHTTPSifConf = 0x20000000 // http > server > if + ngxHTTPLifConf = 0x40000000 // http > location > if + ngxHTTPLmtConf = 0x80000000 // http > location > limit_except +) + +// helpful directive location alias describing "any" context +// doesn't include ngxHTTPSifConf, ngxHTTPLifConf, ngxHTTPLmtConf, or ngxMgmtMainConf. +// +//nolint:unused // This file is generated +const ngxAnyConf = ngxMainConf | ngxEventConf | ngxMailMainConf | ngxMailSrvConf | + ngxStreamMainConf | ngxStreamSrvConf | ngxStreamUpsConf | + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxHTTPUpsConf | + ngxHTTPSifConf | ngxHTTPLifConf | ngxHTTPLmtConf diff --git a/internal/ingress/controller/template/crossplane/extramodules/brotli.go b/internal/ingress/controller/template/crossplane/extramodules/brotli.go new file mode 100644 index 0000000000..0e2762f985 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/brotli.go @@ -0,0 +1,55 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package extramodules + +var brotliDirectives = map[string][]uint{ + "brotli": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfFlag, + }, + "brotli_buffers": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake2, + }, + "brotli_comp_level": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "brotli_min_length": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "brotli_static": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "brotli_types": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConf1More, + }, + "brotli_window": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, +} + + +func BrotliMatchFn(directive string) ([]uint, bool) { + m, ok := brotliDirectives[directive] + return m, ok +} \ No newline at end of file diff --git a/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go b/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go new file mode 100644 index 0000000000..a0039d6fde --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/opentelemetry.go @@ -0,0 +1,64 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by generator; DO NOT EDIT. +// All the definitions are extracted from the source code +// Each bit mask describes these behaviors: +// - how many arguments the directive can take +// - whether or not it is a block directive +// - whether this is a flag (takes one argument that's either "on" or "off") +// - which contexts it's allowed to be in + +package extramodules + +var opentelemetryDirectives = map[string][]uint{ + "opentelemetry": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_attribute": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake2, + }, + "opentelemetry_capture_headers": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_config": { + ngxHTTPMainConf | ngxConfTake1, + }, + "opentelemetry_ignore_paths": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_operation_name": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_propagate": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfNoArgs | ngxConfTake1, + }, + "opentelemetry_sensitive_header_names": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_sensitive_header_values": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, + "opentelemetry_trust_incoming_spans": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPLocConf | ngxConfTake1, + }, +} + + +func OpentelemetryMatchFn(directive string) ([]uint, bool) { + m, ok := opentelemetryDirectives[directive] + return m, ok +} diff --git a/internal/ingress/controller/template/crossplane/extramodules/setmisc.go b/internal/ingress/controller/template/crossplane/extramodules/setmisc.go new file mode 100644 index 0000000000..fed384d469 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/extramodules/setmisc.go @@ -0,0 +1,31 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// As opposite to the other files, this wasn't auto generated but hand crafted. +// Please do not change it + +package extramodules + +var setMiscDirectives = map[string][]uint{ + "set_escape_uri": { + ngxHTTPMainConf | ngxHTTPSrvConf | ngxHTTPSifConf | ngxHTTPLocConf | ngxHTTPLifConf | ngxConfTake12, + }, +} + +func SetMiscMatchFn(directive string) ([]uint, bool) { + m, ok := setMiscDirectives[directive] + return m, ok +} diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go new file mode 100644 index 0000000000..02ff72c9aa --- /dev/null +++ b/internal/ingress/controller/template/crossplane/http.go @@ -0,0 +1,392 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "fmt" + "strconv" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + utilingress "k8s.io/ingress-nginx/pkg/util/ingress" +) + +func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { + cfg := c.tplConfig.Cfg + httpBlock := ngx_crossplane.Directives{ + buildDirective("lua_package_path", "/etc/nginx/lua/?.lua;;"), + buildDirective("lua_shared_dict", "luaconfig", "5m"), + buildDirective("init_by_lua_file", "/etc/nginx/lua/ngx_conf_init.lua"), + buildDirective("init_worker_by_lua_file", "/etc/nginx/lua/ngx_conf_init_worker.lua"), + buildDirective("include", c.mimeFile), + buildDirective("default_type", cfg.DefaultType), + buildDirective("aio", "threads"), + buildDirective("aio_write", cfg.EnableAioWrite), + buildDirective("server_tokens", cfg.ShowServerTokens), + buildDirective("resolver", buildResolversInternal(cfg.Resolver, cfg.DisableIpv6DNS)), + buildDirective("tcp_nopush", "on"), + buildDirective("tcp_nodelay", "on"), + buildDirective("log_subrequest", "on"), + buildDirective("reset_timedout_connection", "on"), + buildDirective("keepalive_timeout", seconds(cfg.KeepAlive)), + buildDirective("keepalive_requests", cfg.KeepAliveRequests), + buildDirective("client_body_temp_path", "/tmp/nginx/client-body"), + buildDirective("fastcgi_temp_path", "/tmp/nginx/fastcgi-temp"), + buildDirective("proxy_temp_path", "/tmp/nginx/proxy-temp"), + buildDirective("client_header_buffer_size", cfg.ClientHeaderBufferSize), + buildDirective("client_header_timeout", seconds(cfg.ClientHeaderTimeout)), + buildDirective("large_client_header_buffers", strings.Split(cfg.LargeClientHeaderBuffers, " ")), + buildDirective("client_body_buffer_size", cfg.ClientBodyBufferSize), + buildDirective("client_body_timeout", seconds(cfg.ClientBodyTimeout)), + buildDirective("types_hash_max_size", "2048"), + buildDirective("server_names_hash_max_size", cfg.ServerNameHashMaxSize), + buildDirective("server_names_hash_bucket_size", cfg.ServerNameHashBucketSize), + buildDirective("map_hash_bucket_size", cfg.MapHashBucketSize), + buildDirective("proxy_headers_hash_max_size", cfg.ProxyHeadersHashMaxSize), + buildDirective("proxy_headers_hash_bucket_size", cfg.ProxyHeadersHashBucketSize), + buildDirective("variables_hash_bucket_size", cfg.VariablesHashBucketSize), + buildDirective("variables_hash_max_size", cfg.VariablesHashMaxSize), + buildDirective("underscores_in_headers", cfg.EnableUnderscoresInHeaders), + buildDirective("ignore_invalid_headers", cfg.IgnoreInvalidHeaders), + buildDirective("limit_req_status", cfg.LimitReqStatusCode), + buildDirective("limit_conn_status", cfg.LimitConnStatusCode), + buildDirective("uninitialized_variable_warn", "off"), + buildDirective("server_name_in_redirect", "off"), + buildDirective("port_in_redirect", "off"), + buildDirective("http2_max_concurrent_streams", cfg.HTTP2MaxConcurrentStreams), + buildDirective("ssl_protocols", strings.Split(cfg.SSLProtocols, " ")), + buildDirective("ssl_early_data", cfg.SSLEarlyData), + buildDirective("ssl_session_tickets", cfg.SSLSessionTickets), + buildDirective("ssl_buffer_size", cfg.SSLBufferSize), + buildDirective("ssl_ecdh_curve", cfg.SSLECDHCurve), + buildDirective("ssl_certificate", cfg.DefaultSSLCertificate.PemFileName), + buildDirective("ssl_certificate_key", cfg.DefaultSSLCertificate.PemFileName), + buildDirective("proxy_ssl_session_reuse", "on"), + buildDirective("proxy_cache_path", []string{ + "/tmp/nginx/nginx-cache-auth", "levels=1:2", "keys_zone=auth_cache:10m", + "max_size=128m", "inactive=30m", "use_temp_path=off", + }), + } + return httpBlock +} + +//nolint:gocyclo // Function is what it is +func (c *Template) buildHTTP() { + cfg := c.tplConfig.Cfg + httpBlock := c.initHTTPDirectives() + httpBlock = append(httpBlock, buildLuaSharedDictionaries(&cfg)...) + + if c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers) { + httpBlock = append(httpBlock, buildDirective("opentelemetry_config", cfg.OpentelemetryConfig)) + } + // Real IP dealing + if (cfg.UseForwardedHeaders || cfg.UseProxyProtocol) || cfg.EnableRealIP { + if cfg.UseProxyProtocol { + httpBlock = append(httpBlock, buildDirective("real_ip_header", "proxy_protocol")) + } else { + httpBlock = append(httpBlock, buildDirective("real_ip_header", cfg.ForwardedForHeader)) + } + httpBlock = append(httpBlock, buildDirective("real_ip_recursive", "on")) + for k := range cfg.ProxyRealIPCIDR { + httpBlock = append(httpBlock, buildDirective("set_real_ip_from", cfg.ProxyRealIPCIDR[k])) + } + } + + if cfg.GRPCBufferSizeKb > 0 { + httpBlock = append(httpBlock, buildDirective("grpc_buffer_size", strconv.Itoa(cfg.GRPCBufferSizeKb)+"k")) + } + + // HTTP2 Configuration + if cfg.HTTP2MaxHeaderSize != "" && cfg.HTTP2MaxFieldSize != "" { + httpBlock = append(httpBlock, + buildDirective("http2_max_field_size", cfg.HTTP2MaxFieldSize), + buildDirective("http2_max_header_size", cfg.HTTP2MaxHeaderSize), + ) + } + + if cfg.HTTP2MaxRequests > 0 { + httpBlock = append(httpBlock, buildDirective("http2_max_requests", cfg.HTTP2MaxRequests)) + } + + if cfg.UseGzip { + httpBlock = append(httpBlock, + buildDirective("gzip", "on"), + buildDirective("gzip_comp_level", cfg.GzipLevel), + buildDirective("gzip_http_version", "1.1"), + buildDirective("gzip_min_length", cfg.GzipMinLength), + buildDirective("gzip_types", strings.Split(cfg.GzipTypes, " ")), + buildDirective("gzip_proxied", "any"), + buildDirective("gzip_vary", "on"), + ) + + if cfg.GzipDisable != "" { + httpBlock = append(httpBlock, buildDirective("gzip_disable", strings.Split(cfg.GzipDisable, " "))) + } + } + + if cfg.EnableBrotli { + httpBlock = append(httpBlock, buildDirective("brotli", "on"), + buildDirective("brotli_comp_level", cfg.BrotliLevel), + buildDirective("brotli_min_length", cfg.BrotliMinLength), + buildDirective("brotli_types", cfg.BrotliTypes)) + } + + if (c.tplConfig.Cfg.EnableOpentelemetry || shouldLoadOpentelemetryModule(c.tplConfig.Servers)) && + cfg.OpentelemetryOperationName != "" { + httpBlock = append(httpBlock, buildDirective("opentelemetry_operation_name", cfg.OpentelemetryOperationName)) + } + + if !cfg.ShowServerTokens { + httpBlock = append(httpBlock, buildDirective("more_clear_headers", "Server")) + } + + if cfg.UseGeoIP2 && c.tplConfig.MaxmindEditionFiles != nil && len(*c.tplConfig.MaxmindEditionFiles) > 0 { + geoipDirectives := buildGeoIPDirectives(cfg.GeoIP2AutoReloadMinutes, *c.tplConfig.MaxmindEditionFiles) + // We do this to avoid adding empty blocks + if len(geoipDirectives) > 0 { + httpBlock = append(httpBlock, geoipDirectives...) + } + } + + httpBlock = append(httpBlock, buildBlockDirective( + "geo", + []string{"$literal_dollar"}, + ngx_crossplane.Directives{ + buildDirective("default", "$"), + }, + )) + + if len(c.tplConfig.AddHeaders) > 0 { + for headerName, headerValue := range c.tplConfig.AddHeaders { + httpBlock = append(httpBlock, buildDirective("more_set_headers", fmt.Sprintf("%s: %s", headerName, headerValue))) + } + } + + escape := "" + if cfg.LogFormatEscapeNone { + escape = "escape=none" + } else if cfg.LogFormatEscapeJSON { + escape = "escape=json" + } + + httpBlock = append(httpBlock, buildDirective("log_format", "upstreaminfo", escape, cfg.LogFormatUpstream)) + + loggableMap := make(ngx_crossplane.Directives, 0) + for k := range cfg.SkipAccessLogURLs { + loggableMap = append(loggableMap, buildDirective(cfg.SkipAccessLogURLs[k], "0")) + } + loggableMap = append(loggableMap, buildDirective("default", "1")) + httpBlock = append(httpBlock, buildMapDirective("$request_uri", "$loggable", loggableMap)) + + if cfg.DisableAccessLog || cfg.DisableHTTPAccessLog { + httpBlock = append(httpBlock, buildDirective("access_log", "off")) + } else { + logDirectives := []string{"upstreaminfo", "if=$loggable"} + if cfg.EnableSyslog { + httpBlock = append(httpBlock, buildDirective("access_log", fmt.Sprintf("syslog:server%s:%d", cfg.SyslogHost, cfg.SyslogPort), logDirectives)) + } else { + accessLog := cfg.AccessLogPath + if cfg.HTTPAccessLogPath != "" { + accessLog = cfg.HTTPAccessLogPath + } + httpBlock = append(httpBlock, buildDirective("access_log", accessLog, logDirectives)) + } + } + + if cfg.EnableSyslog { + httpBlock = append(httpBlock, buildDirective("error_log", fmt.Sprintf("syslog:server%s:%d", cfg.SyslogHost, cfg.SyslogPort), cfg.ErrorLogLevel)) + } else { + httpBlock = append(httpBlock, buildDirective("error_log", cfg.ErrorLogPath, cfg.ErrorLogLevel)) + } + + if cfg.SSLSessionCache { + httpBlock = append(httpBlock, + buildDirective("ssl_session_cache", fmt.Sprintf("shared:SSL:%s", cfg.SSLSessionCacheSize)), + buildDirective("ssl_session_timeout", cfg.SSLSessionTimeout), + ) + } + + if cfg.SSLSessionTicketKey != "" { + httpBlock = append(httpBlock, buildDirective("ssl_session_ticket_key", "/etc/ingress-controller/tickets.key")) + } + + if cfg.SSLCiphers != "" { + httpBlock = append(httpBlock, + buildDirective("ssl_ciphers", cfg.SSLCiphers), + buildDirective("ssl_prefer_server_ciphers", "on"), + ) + } + + if cfg.SSLDHParam != "" { + httpBlock = append(httpBlock, buildDirective("ssl_dhparam", cfg.SSLDHParam)) + } + + if len(cfg.CustomHTTPErrors) > 0 && !cfg.DisableProxyInterceptErrors { + httpBlock = append(httpBlock, buildDirective("proxy_intercept_errors", "on")) + } + + httpUpgradeMap := ngx_crossplane.Directives{buildDirective("default", "upgrade")} + if cfg.UpstreamKeepaliveConnections < 1 { + httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "close")) + } else { + httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "")) + } + httpBlock = append(httpBlock, buildMapDirective("$http_upgrade", "$connection_upgrade", httpUpgradeMap)) + + reqIDMap := ngx_crossplane.Directives{buildDirective("default", "$http_x_request_id")} + if cfg.GenerateRequestID { + reqIDMap = append(reqIDMap, buildDirective("", "$request_id")) + } + httpBlock = append(httpBlock, buildMapDirective("$http_x_request_id", "$req_id", reqIDMap)) + + if cfg.UseForwardedHeaders && cfg.ComputeFullForwardedFor { + forwardForMap := make(ngx_crossplane.Directives, 0) + if cfg.UseProxyProtocol { + forwardForMap = append(forwardForMap, + buildDirective("default", "$http_x_forwarded_for, $proxy_protocol_addr"), + buildDirective("", "$proxy_protocol_addr"), + ) + } else { + forwardForMap = append(forwardForMap, + buildDirective("default", "$http_x_forwarded_for, $realip_remote_addr"), + buildDirective("", "$realip_remote_addr"), + ) + } + httpBlock = append(httpBlock, buildMapDirective("$http_x_forwarded_for", "$full_x_forwarded_for", forwardForMap)) + } + + if cfg.AllowBackendServerHeader { + httpBlock = append(httpBlock, buildDirective("proxy_pass_header", "Server")) + } + + for k := range cfg.HideHeaders { + httpBlock = append(httpBlock, buildDirective("proxy_hide_header", cfg.HideHeaders[k])) + } + + blockUpstreamDirectives := ngx_crossplane.Directives{ + buildDirective("server", "0.0.0.1"), + buildDirective("balancer_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_balancer.lua"), + } + if c.tplConfig.Cfg.UpstreamKeepaliveConnections > 0 { + blockUpstreamDirectives = append(blockUpstreamDirectives, + buildDirective("keepalive", c.tplConfig.Cfg.UpstreamKeepaliveConnections), + buildDirective("keepalive_time", c.tplConfig.Cfg.UpstreamKeepaliveTime), + buildDirective("keepalive_timeout", seconds(c.tplConfig.Cfg.UpstreamKeepaliveTimeout)), + buildDirective("keepalive_requests", c.tplConfig.Cfg.UpstreamKeepaliveRequests), + ) + } + httpBlock = append(httpBlock, buildBlockDirective("upstream", []string{"upstream_balancer"}, blockUpstreamDirectives)) + + // Adding Rate limit + rl := filterRateLimits(c.tplConfig.Servers) + for i := range rl { + id := fmt.Sprintf("$allowlist_%s", rl[i].ID) + httpBlock = append(httpBlock, buildDirective("#", "Ratelimit", rl[i].Name)) + rlDirectives := ngx_crossplane.Directives{ + buildDirective("default", 0), + } + for _, ip := range rl[i].Allowlist { + rlDirectives = append(rlDirectives, buildDirective(ip, "1")) + } + mapRateLimitDirective := buildMapDirective(id, fmt.Sprintf("$limit_%s", rl[i].ID), ngx_crossplane.Directives{ + buildDirective("0", cfg.LimitConnZoneVariable), + buildDirective("1", ""), + }) + httpBlock = append(httpBlock, buildBlockDirective("geo", []string{"$remote_addr", id}, rlDirectives), mapRateLimitDirective) + } + + zoneRL := buildRateLimitZones(c.tplConfig.Servers) + if len(zoneRL) > 0 { + httpBlock = append(httpBlock, zoneRL...) + } + + // End of Rate limit configs + + for i := range cfg.BlockCIDRs { + httpBlock = append(httpBlock, buildDirective("deny", strings.TrimSpace(cfg.BlockCIDRs[i]))) + } + + if len(cfg.BlockUserAgents) > 0 { + uaDirectives := ngx_crossplane.Directives{buildDirective("default", 0)} + for i := range cfg.BlockUserAgents { + uaDirectives = append(uaDirectives, buildDirective(strings.TrimSpace(cfg.BlockUserAgents[i]), 1)) + } + httpBlock = append(httpBlock, buildMapDirective("$http_user_agent", "$block_ua", uaDirectives)) + } + + if len(cfg.BlockReferers) > 0 { + refDirectives := ngx_crossplane.Directives{buildDirective("default", 0)} + for i := range cfg.BlockReferers { + refDirectives = append(refDirectives, buildDirective(strings.TrimSpace(cfg.BlockReferers[i]), 1)) + } + httpBlock = append(httpBlock, buildMapDirective("$http_referer", "$block_ref", refDirectives)) + } + + for _, v := range cfg.CustomHTTPErrors { + httpBlock = append(httpBlock, buildDirective("error_page", v, "=", + fmt.Sprintf("@custom_upstream-default-backend_%d", v))) + } + + if redirectServers, ok := c.tplConfig.RedirectServers.([]*utilingress.Redirect); ok { + for _, server := range redirectServers { + httpBlock = append(httpBlock, + buildStartServer(server.From), + c.buildRedirectServer(server), + buildEndServer(server.From), + ) + } + } + + for _, server := range c.tplConfig.Servers { + for _, location := range server.Locations { + if shouldApplyAuthUpstream(location, &cfg) && !shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL) { + authUpstreamBlock := buildBlockDirective("upstream", + []string{buildAuthUpstreamName(location, server.Hostname)}, ngx_crossplane.Directives{ + buildDirective("server", extractHostPort(location.ExternalAuth.URL)), + buildDirective("keepalive", location.ExternalAuth.KeepaliveConnections), + buildDirective("keepalive_requests", location.ExternalAuth.KeepaliveRequests), + buildDirective("keepalive_timeout", seconds(location.ExternalAuth.KeepaliveTimeout)), + }, + ) + httpBlock = append(httpBlock, + buildStartAuthUpstream(server.Hostname, location.Path), + authUpstreamBlock, + buildEndAuthUpstream(server.Hostname, location.Path), + ) + } + } + } + + for _, server := range c.tplConfig.Servers { + httpBlock = append(httpBlock, + buildStartServer(server.Hostname), + c.buildServerDirective(server), + buildEndServer(server.Hostname), + ) + } + + httpBlock = append(httpBlock, + c.buildDefaultBackend(), + c.buildHealthAndStatsServer(), + ) + + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ + Directive: "http", + Block: httpBlock, + }) +} diff --git a/internal/ingress/controller/template/crossplane/location.go b/internal/ingress/controller/template/crossplane/location.go new file mode 100644 index 0000000000..bc7c5685ed --- /dev/null +++ b/internal/ingress/controller/template/crossplane/location.go @@ -0,0 +1,733 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "fmt" + "sort" + "strconv" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + "k8s.io/apimachinery/pkg/util/sets" + + "k8s.io/ingress-nginx/internal/ingress/controller/config" + "k8s.io/ingress-nginx/pkg/apis/ingress" +) + +func buildMirrorLocationDirective(locs []*ingress.Location) ngx_crossplane.Directives { + mirrorDirectives := make(ngx_crossplane.Directives, 0) + + mapped := sets.Set[string]{} + + for _, loc := range locs { + if loc.Mirror.Source == "" || loc.Mirror.Target == "" || loc.Mirror.Host == "" { + continue + } + + if mapped.Has(loc.Mirror.Source) { + continue + } + + mapped.Insert(loc.Mirror.Source) + mirrorDirectives = append(mirrorDirectives, buildBlockDirective("location", + []string{"=", loc.Mirror.Source}, + ngx_crossplane.Directives{ + buildDirective("internal"), + buildDirective("proxy_set_header", "Host", loc.Mirror.Host), + buildDirective("proxy_pass", loc.Mirror.Target), + })) + } + return mirrorDirectives +} + +// buildCustomErrorLocationsPerServer is a utility function which will collect all +// custom error codes for all locations of a server block, deduplicates them, +// and returns a set which is unique by default-upstream and error code. It returns an array +// of errorLocations, each of which contain the upstream name and a list of +// error codes for that given upstream, so that sufficiently unique +// @custom error location blocks can be created in the template +func buildCustomErrorLocationsPerServer(server *ingress.Server, enableMetrics bool) ngx_crossplane.Directives { + type errorLocation struct { + UpstreamName string + Codes []int + } + + codesMap := make(map[string]map[int]bool) + for _, loc := range server.Locations { + backendUpstream := loc.DefaultBackendUpstreamName + + var dedupedCodes map[int]bool + if existingMap, ok := codesMap[backendUpstream]; ok { + dedupedCodes = existingMap + } else { + dedupedCodes = make(map[int]bool) + } + + for _, code := range loc.CustomHTTPErrors { + dedupedCodes[code] = true + } + codesMap[backendUpstream] = dedupedCodes + } + + errorLocations := []errorLocation{} + + for upstream, dedupedCodes := range codesMap { + codesForUpstream := []int{} + for code := range dedupedCodes { + codesForUpstream = append(codesForUpstream, code) + } + sort.Ints(codesForUpstream) + errorLocations = append(errorLocations, errorLocation{ + UpstreamName: upstream, + Codes: codesForUpstream, + }) + } + + sort.Slice(errorLocations, func(i, j int) bool { + return errorLocations[i].UpstreamName < errorLocations[j].UpstreamName + }) + + errorLocationsDirectives := make(ngx_crossplane.Directives, 0) + for i := range errorLocations { + errorLocationsDirectives = append(errorLocationsDirectives, buildCustomErrorLocation(errorLocations[i].UpstreamName, errorLocations[i].Codes, enableMetrics)...) + } + return errorLocationsDirectives +} + +func buildCustomErrorLocation(upstreamName string, errorCodes []int, enableMetrics bool) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, len(errorCodes)) + for i := range errorCodes { + locationDirectives := ngx_crossplane.Directives{ + buildDirective("internal"), + buildDirective("proxy_intercept_errors", "off"), + buildDirective("proxy_set_header", "X-Code", errorCodes[i]), + buildDirective("proxy_set_header", "X-Format", "$http_accept"), + buildDirective("proxy_set_header", "X-Original-URI", "$request_uri"), + buildDirective("proxy_set_header", "X-Namespace", "$namespace"), + buildDirective("proxy_set_header", "X-Ingress-Name", "$ingress_name"), + buildDirective("proxy_set_header", "X-Service-Name", "$service_name"), + buildDirective("proxy_set_header", "X-Service-Port", "$service_port"), + buildDirective("proxy_set_header", "X-Request-ID", "$req_id"), + buildDirective("proxy_set_header", "X-Forwarded-For", "$remote_addr"), + buildDirective("proxy_set_header", "Host", "$best_http_host"), + buildDirective("set", "$proxy_upstream_name", upstreamName), + buildDirective("rewrite", "(.*)", "/", "break"), + buildDirective("proxy_pass", "http://upstream_balancer"), + } + + if enableMetrics { + locationDirectives = append(locationDirectives, buildDirective("log_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_log.lua")) + } + locationName := fmt.Sprintf("@custom_%s_%d", upstreamName, errorCodes[i]) + directives[i] = buildBlockDirective("location", []string{locationName}, locationDirectives) + } + + return directives +} + +type locationCfg struct { + pathLocation []string + authPath string + externalAuth *externalAuth + proxySetHeader string + applyGlobalAuth bool + applyAuthUpstream bool +} + +func (c *Template) buildServerLocations(server *ingress.Server, locations []*ingress.Location) ngx_crossplane.Directives { + serverLocations := make(ngx_crossplane.Directives, 0) + + cfg := c.tplConfig.Cfg + enforceRegexModifier := false + needsRewrite := func(loc *ingress.Location) bool { + return loc.Rewrite.Target != "" && + loc.Rewrite.Target != loc.Path + } + + for _, location := range locations { + if needsRewrite(location) || location.Rewrite.UseRegex { + enforceRegexModifier = true + break + } + } + + for _, location := range locations { + locationConfig := locationCfg{ + pathLocation: buildLocation(location, enforceRegexModifier), + proxySetHeader: getProxySetHeader(location), + authPath: buildAuthLocation(location, cfg.GlobalExternalAuth.URL), + applyGlobalAuth: shouldApplyGlobalAuth(location, cfg.GlobalExternalAuth.URL), + applyAuthUpstream: shouldApplyAuthUpstream(location, &cfg), + externalAuth: &externalAuth{}, + } + + if location.Rewrite.AppRoot != "" { + serverLocations = append(serverLocations, + buildBlockDirective("if", []string{"$uri", "=", "/"}, + ngx_crossplane.Directives{ + buildDirective("return", "302", fmt.Sprintf("$scheme://$http_host%s", location.Rewrite.AppRoot)), + })) + } + + if locationConfig.applyGlobalAuth { + locationConfig.externalAuth = buildExternalAuth(cfg.GlobalExternalAuth) + } else { + locationConfig.externalAuth = buildExternalAuth(location.ExternalAuth) + } + if locationConfig.authPath != "" { + serverLocations = append(serverLocations, c.buildAuthLocation(server, location, locationConfig)) + } + if location.Denied == nil && locationConfig.externalAuth != nil && locationConfig.externalAuth.SigninURL != "" { + directives := ngx_crossplane.Directives{ + buildDirective("internal"), + buildDirective("add_header", "Set-Cookie", "$auth_cookie"), + } + if location.CorsConfig.CorsEnabled { + directives = append(directives, buildCorsDirectives(&location.CorsConfig)...) + } + directives = append(directives, + buildDirective("return", + "302", + buildAuthSignURL(locationConfig.externalAuth.SigninURL, locationConfig.externalAuth.SigninURLRedirectParam))) + + serverLocations = append(serverLocations, buildBlockDirective("location", + []string{buildAuthSignURLLocation(location.Path, locationConfig.externalAuth.SigninURL)}, directives)) + } + serverLocations = append(serverLocations, c.buildLocation(server, location, locationConfig)) + } + return serverLocations +} + +func (c *Template) buildLocation(server *ingress.Server, + location *ingress.Location, locationConfig locationCfg, +) *ngx_crossplane.Directive { + ing := getIngressInformation(location.Ingress, server.Hostname, location.IngressPath) + cfg := c.tplConfig + locationDirectives := ngx_crossplane.Directives{ + buildDirective("set", "$namespace", ing.Namespace), + buildDirective("set", "$ingress_name", ing.Rule), + buildDirective("set", "$service_name", ing.Service), + buildDirective("set", "$service_port", ing.ServicePort), + buildDirective("set", "$balancer_ewma_score", "-1"), + buildDirective("set", "$proxy_upstream_name", location.Backend), + buildDirective("set", "$proxy_host", "$proxy_upstream_name"), + buildDirective("set", "$pass_access_scheme", "$scheme"), + buildDirective("set", "$best_http_host", "$http_host"), + buildDirective("set", "$pass_port", "$pass_server_port"), + buildDirective("set", "$proxy_alternative_upstream_name", ""), + buildDirective("set", "$location_path", strings.ReplaceAll(ing.Path, `$`, `${literal_dollar}`)), + } + + locationDirectives = append(locationDirectives, locationConfigForLua(location, c.tplConfig)...) + locationDirectives = append(locationDirectives, buildCertificateDirectives(location)...) + + if cfg.Cfg.UseProxyProtocol { + locationDirectives = append(locationDirectives, + buildDirective("set", "$pass_server_port", "$proxy_protocol_server_port")) + } else { + locationDirectives = append(locationDirectives, + buildDirective("set", "$pass_server_port", "$server_port")) + } + + locationDirectives = append(locationDirectives, + buildOpentelemetryForLocationDirectives(cfg.Cfg.EnableOpentelemetry, cfg.Cfg.OpentelemetryTrustIncomingSpan, location)...) + + locationDirectives = append(locationDirectives, + buildDirective("rewrite_by_lua_file", "/etc/nginx/lua/nginx/ngx_rewrite.lua"), + buildDirective("header_filter_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_srv_hdr_filter.lua"), + buildDirective("log_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_log_block.lua"), + buildDirective("rewrite_log", location.Logs.Rewrite), + // buildDirective("http2_push_preload", location.HTTP2PushPreload), // This directive is deprecated, keeping out of new crossplane + buildDirective("port_in_redirect", location.UsePortInRedirects)) + + if location.Mirror.Source != "" { + locationDirectives = append(locationDirectives, + buildDirective("mirror", location.Mirror.Source), + buildDirective("mirror_request_body", location.Mirror.RequestBody), + ) + } + + if !location.Logs.Access { + locationDirectives = append(locationDirectives, + buildDirective("access_log", "off"), + ) + } + if location.Denied != nil { + locationDirectives = append(locationDirectives, + buildDirectiveWithComment("return", fmt.Sprintf("Location denied. Reason: %s", *location.Denied), "503")) + } else { + locationDirectives = append(locationDirectives, c.buildAllowedLocation(server, location, locationConfig)...) + } + + return buildBlockDirective("location", locationConfig.pathLocation, locationDirectives) +} + +func (c *Template) buildAllowedLocation(server *ingress.Server, location *ingress.Location, locationConfig locationCfg) ngx_crossplane.Directives { + dir := make(ngx_crossplane.Directives, 0) + proxySetHeader := locationConfig.proxySetHeader + for _, ip := range location.Denylist.CIDR { + dir = append(dir, buildDirective("deny", ip)) + } + if len(location.Allowlist.CIDR) > 0 { + for _, ip := range location.Allowlist.CIDR { + dir = append(dir, buildDirective("allow", ip)) + } + dir = append(dir, buildDirective("deny", "all")) + } + + if location.CorsConfig.CorsEnabled { + dir = append(dir, buildCorsDirectives(&location.CorsConfig)...) + } + + if !isLocationInLocationList(location, c.tplConfig.Cfg.NoAuthLocations) { + dir = append(dir, buildAuthLocationConfig(location, locationConfig)...) + } + + dir = append(dir, buildRateLimit(location)...) + + if isValidByteSize(location.Proxy.BodySize, true) { + dir = append(dir, buildDirective("client_max_body_size", location.Proxy.BodySize)) + } + if isValidByteSize(location.ClientBodyBufferSize, false) { + dir = append(dir, buildDirective("client_body_buffer_size", location.ClientBodyBufferSize)) + } + + if location.UpstreamVhost != "" { + dir = append(dir, buildDirective(proxySetHeader, "Host", location.UpstreamVhost)) + } else { + dir = append(dir, buildDirective(proxySetHeader, "Host", "$best_http_host")) + } + + if server.CertificateAuth.CAFileName != "" { + dir = append(dir, + buildDirective(proxySetHeader, "ssl-client-verify", "$ssl_client_verify"), + buildDirective(proxySetHeader, "ssl-client-subject-dn", "$ssl_client_s_dn"), + buildDirective(proxySetHeader, "ssl-client-issuer-dn", "$ssl_client_i_dn"), + ) + + if server.CertificateAuth.PassCertToUpstream { + dir = append(dir, buildDirective(proxySetHeader, "ssl-client-cert", "$ssl_client_escaped_cert")) + } + } + + dir = append(dir, + buildDirective(proxySetHeader, "Upgrade", "$http_upgrade"), + buildDirective(proxySetHeader, "X-Request-ID", "$req_id"), + buildDirective(proxySetHeader, "X-Real-IP", "$remote_addr"), + buildDirective(proxySetHeader, "X-Forwarded-Host", "$best_http_host"), + buildDirective(proxySetHeader, "X-Forwarded-Port", "$pass_port"), + buildDirective(proxySetHeader, "X-Forwarded-Proto", "$pass_access_scheme"), + buildDirective(proxySetHeader, "X-Forwarded-Scheme", "$pass_access_scheme"), + buildDirective(proxySetHeader, "X-Real-IP", "$remote_addr"), + buildDirective(proxySetHeader, "X-Scheme", "$pass_access_scheme"), + buildDirective(proxySetHeader, "X-Original-Forwarded-For", + fmt.Sprintf("$http_%s", strings.ToLower(strings.ReplaceAll(c.tplConfig.Cfg.ForwardedForHeader, "-", "_")))), + buildDirectiveWithComment(proxySetHeader, + "mitigate HTTProxy Vulnerability - https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/", "Proxy", ""), + buildDirective("proxy_connect_timeout", seconds(location.Proxy.ConnectTimeout)), + buildDirective("proxy_read_timeout", seconds(location.Proxy.ReadTimeout)), + buildDirective("proxy_send_timeout", seconds(location.Proxy.SendTimeout)), + buildDirective("proxy_buffering", location.Proxy.ProxyBuffering), + buildDirective("proxy_buffer_size", location.Proxy.BufferSize), + buildDirective("proxy_buffers", location.Proxy.BuffersNumber, location.Proxy.BufferSize), + buildDirective("proxy_request_buffering", location.Proxy.RequestBuffering), + buildDirective("proxy_http_version", location.Proxy.ProxyHTTPVersion), + buildDirective("proxy_cookie_domain", strings.Split(location.Proxy.CookieDomain, " ")), + buildDirective("proxy_cookie_path", strings.Split(location.Proxy.CookiePath, " ")), + buildDirective("proxy_next_upstream_timeout", location.Proxy.NextUpstreamTimeout), + buildDirective("proxy_next_upstream_tries", location.Proxy.NextUpstreamTries), + buildDirective("proxy_next_upstream", buildNextUpstream(location.Proxy.NextUpstream, c.tplConfig.Cfg.RetryNonIdempotent)), + ) + + if isValidByteSize(location.Proxy.ProxyMaxTempFileSize, true) { + dir = append(dir, buildDirective("proxy_max_temp_file_size", location.Proxy.ProxyMaxTempFileSize)) + } + + if c.tplConfig.Cfg.UseForwardedHeaders && c.tplConfig.Cfg.ComputeFullForwardedFor { + dir = append(dir, buildDirective(proxySetHeader, "X-Forwarded-For", "$full_x_forwarded_for")) + } else { + dir = append(dir, buildDirective(proxySetHeader, "X-Forwarded-For", "$remote_addr")) + } + + if c.tplConfig.Cfg.ProxyAddOriginalURIHeader { + dir = append(dir, buildDirective(proxySetHeader, "X-Original-URI", "$request_uri")) + } + + if location.Connection.Enabled { + dir = append(dir, buildDirective(proxySetHeader, "Connection", location.Connection.Header)) + } else { + dir = append(dir, buildDirective(proxySetHeader, "Connection", "$connection_upgrade")) + } + + for k, v := range c.tplConfig.ProxySetHeaders { + dir = append(dir, buildDirective(proxySetHeader, k, v)) + } + + for k, v := range location.CustomHeaders.Headers { + dir = append(dir, buildDirective("more_set_headers", fmt.Sprintf("%s: %s", k, strings.ReplaceAll(v, `$`, `${literal_dollar}`)))) + } + + if strings.HasPrefix(location.Backend, "custom-default-backend-") { + dir = append(dir, + buildDirective("proxy_set_header", "X-Code", "503"), + buildDirective("proxy_set_header", "X-Format", "$http_accept"), + buildDirective("proxy_set_header", "X-Namespace", "$namespace"), + buildDirective("proxy_set_header", "X-Ingress-Name", "$ingress_name"), + buildDirective("proxy_set_header", "X-Service-Name", "$service_name"), + buildDirective("proxy_set_header", "X-Service-Port", "$service_port"), + buildDirective("proxy_set_header", "X-Request-ID", "$req_id"), + ) + } + + if location.Satisfy != "" { + dir = append(dir, buildDirective("satisfy", location.Satisfy)) + } + + if len(location.CustomHTTPErrors) > 0 && !location.DisableProxyInterceptErrors { + dir = append(dir, buildDirective("proxy_intercept_errors", "on")) + } + + for _, errorcode := range location.CustomHTTPErrors { + dir = append(dir, buildDirective( + "error_page", + errorcode, "=", + fmt.Sprintf("@custom_%s_%d", location.DefaultBackendUpstreamName, errorcode)), + ) + } + + switch location.BackendProtocol { + case "GRPC", "GRPCS": + dir = append(dir, + buildDirective("grpc_connect_timeout", seconds(location.Proxy.ConnectTimeout)), + buildDirective("grpc_send_timeout", seconds(location.Proxy.SendTimeout)), + buildDirective("grpc_read_timeout", seconds(location.Proxy.ReadTimeout)), + ) + case "FCGI": + dir = append(dir, buildDirective("include", "/etc/nginx/fastcgi_params")) + if location.FastCGI.Index != "" { + dir = append(dir, buildDirective("fastcgi_index", location.FastCGI.Index)) + } + for k, v := range location.FastCGI.Params { + dir = append(dir, buildDirective("fastcgi_param", k, v)) + } + } + + if location.Redirect.URL != "" { + dir = append(dir, buildDirective("return", location.Redirect.Code, location.Redirect.URL)) + } + + dir = append(dir, buildProxyPass(c.tplConfig.Backends, location)...) + + if location.Proxy.ProxyRedirectFrom == "default" || location.Proxy.ProxyRedirectFrom == "off" { + dir = append(dir, buildDirective("proxy_redirect", location.Proxy.ProxyRedirectFrom)) + } else if location.Proxy.ProxyRedirectTo != "off" { + dir = append(dir, buildDirective("proxy_redirect", location.Proxy.ProxyRedirectFrom, location.Proxy.ProxyRedirectTo)) + } + + return dir +} + +func buildCertificateDirectives(location *ingress.Location) ngx_crossplane.Directives { + cert := make(ngx_crossplane.Directives, 0) + if location.ProxySSL.CAFileName != "" { + cert = append(cert, + buildDirectiveWithComment( + "proxy_ssl_trusted_certificate", + fmt.Sprintf("#PEM sha: %s", location.ProxySSL.CASHA), + location.ProxySSL.CAFileName, + ), + buildDirective("proxy_ssl_ciphers", location.ProxySSL.Ciphers), + buildDirective("proxy_ssl_protocols", strings.Split(location.ProxySSL.Protocols, " ")), + buildDirective("proxy_ssl_verify", location.ProxySSL.Verify), + buildDirective("proxy_ssl_verify_depth", location.ProxySSL.VerifyDepth), + ) + } + if location.ProxySSL.ProxySSLName != "" { + cert = append(cert, buildDirective("proxy_ssl_name", location.ProxySSL.ProxySSLName)) + } + if location.ProxySSL.ProxySSLServerName != "" { + cert = append(cert, buildDirective("proxy_ssl_server_name", location.ProxySSL.ProxySSLServerName)) + } + if location.ProxySSL.PemFileName != "" { + cert = append(cert, + buildDirective("proxy_ssl_certificate", location.ProxySSL.PemFileName), + buildDirective("proxy_ssl_certificate_key", location.ProxySSL.PemFileName), + ) + } + return cert +} + +type ingressInformation struct { + Namespace string + Path string + Rule string + Service string + ServicePort string + Annotations map[string]string +} + +func getIngressInformation(ing *ingress.Ingress, hostname, ingressPath string) *ingressInformation { + if ing == nil { + return &ingressInformation{} + } + + info := &ingressInformation{ + Namespace: ing.GetNamespace(), + Rule: ing.GetName(), + Annotations: ing.Annotations, + Path: ingressPath, + } + + if ingressPath == "" { + ingressPath = "/" + info.Path = "/" + } + + if ing.Spec.DefaultBackend != nil && ing.Spec.DefaultBackend.Service != nil { + info.Service = ing.Spec.DefaultBackend.Service.Name + if ing.Spec.DefaultBackend.Service.Port.Number > 0 { + info.ServicePort = strconv.Itoa(int(ing.Spec.DefaultBackend.Service.Port.Number)) + } else { + info.ServicePort = ing.Spec.DefaultBackend.Service.Port.Name + } + } + + for _, rule := range ing.Spec.Rules { + if rule.HTTP == nil { + continue + } + + if hostname != "_" && rule.Host == "" { + continue + } + + host := "_" + if rule.Host != "" { + host = rule.Host + } + + if hostname != host { + continue + } + + for _, rPath := range rule.HTTP.Paths { + if ingressPath != rPath.Path { + continue + } + + if rPath.Backend.Service == nil { + continue + } + + if info.Service != "" && rPath.Backend.Service.Name == "" { + // empty rule. Only contains a Path and PathType + return info + } + + info.Service = rPath.Backend.Service.Name + if rPath.Backend.Service.Port.Number > 0 { + info.ServicePort = strconv.Itoa(int(rPath.Backend.Service.Port.Number)) + } else { + info.ServicePort = rPath.Backend.Service.Port.Name + } + + return info + } + } + + return info +} + +func buildOpentelemetryForLocationDirectives(isOTEnabled, isOTTrustSet bool, location *ingress.Location) ngx_crossplane.Directives { + isOTEnabledInLoc := location.Opentelemetry.Enabled + isOTSetInLoc := location.Opentelemetry.Set + directives := make(ngx_crossplane.Directives, 0) + if isOTEnabled { + if isOTSetInLoc && !isOTEnabledInLoc { + return ngx_crossplane.Directives{ + buildDirective("opentelemetry", "off"), + } + } + } else if !isOTSetInLoc || !isOTEnabledInLoc { + return directives + } + + if location != nil { + directives = append(directives, + buildDirective("opentelemetry", "on"), + buildDirective("opentelemetry_propagate"), + ) + if location.Opentelemetry.OperationName != "" { + directives = append(directives, + buildDirective("opentelemetry_operation_name", location.Opentelemetry.OperationName)) + } + + if (!isOTTrustSet && !location.Opentelemetry.TrustSet) || + (location.Opentelemetry.TrustSet && !location.Opentelemetry.TrustEnabled) { + directives = append(directives, + buildDirective("opentelemetry_trust_incoming_spans", "off"), + ) + } else { + directives = append(directives, + buildDirective("opentelemetry_trust_incoming_spans", "on"), + ) + } + } + + return directives +} + +// buildRateLimit produces an array of limit_req to be used inside the Path of +// Ingress rules. The order: connections by IP first, then RPS, and RPM last. +func buildRateLimit(loc *ingress.Location) ngx_crossplane.Directives { + limits := make(ngx_crossplane.Directives, 0) + + if loc.RateLimit.Connections.Limit > 0 { + limits = append(limits, buildDirective("limit_conn", loc.RateLimit.Connections.Name, loc.RateLimit.Connections.Limit)) + } + + if loc.RateLimit.RPS.Limit > 0 { + limits = append(limits, + buildDirective( + "limit_req", + fmt.Sprintf("zone=%s", loc.RateLimit.RPS.Name), + fmt.Sprintf("burst=%d", loc.RateLimit.RPS.Burst), + "nodelay", + ), + ) + } + + if loc.RateLimit.RPM.Limit > 0 { + limits = append(limits, + buildDirective( + "limit_req", + fmt.Sprintf("zone=%s", loc.RateLimit.RPM.Name), + fmt.Sprintf("burst=%d", loc.RateLimit.RPM.Burst), + "nodelay", + ), + ) + } + + if loc.RateLimit.LimitRateAfter > 0 { + limits = append(limits, + buildDirective( + "limit_rate_after", + fmt.Sprintf("%dk", loc.RateLimit.LimitRateAfter), + ), + ) + } + + if loc.RateLimit.LimitRate > 0 { + limits = append(limits, + buildDirective( + "limit_rate", + fmt.Sprintf("%dk", loc.RateLimit.LimitRate), + ), + ) + } + + return limits +} + +// locationConfigForLua formats some location specific configuration into Lua table represented as string +func locationConfigForLua(location *ingress.Location, all *config.TemplateConfig) ngx_crossplane.Directives { + /* Lua expects the following vars + force_ssl_redirect = string_to_bool(ngx.var.force_ssl_redirect), + ssl_redirect = string_to_bool(ngx.var.ssl_redirect), + force_no_ssl_redirect = string_to_bool(ngx.var.force_no_ssl_redirect), + preserve_trailing_slash = string_to_bool(ngx.var.preserve_trailing_slash), + use_port_in_redirects = string_to_bool(ngx.var.use_port_in_redirects), + */ + + return ngx_crossplane.Directives{ + buildDirective("set", "$force_ssl_redirect", strconv.FormatBool(location.Rewrite.ForceSSLRedirect)), + buildDirective("set", "$ssl_redirect", strconv.FormatBool(location.Rewrite.SSLRedirect)), + buildDirective("set", "$force_no_ssl_redirect", strconv.FormatBool(isLocationInLocationList(location, all.Cfg.NoTLSRedirectLocations))), + buildDirective("set", "$preserve_trailing_slash", strconv.FormatBool(location.Rewrite.PreserveTrailingSlash)), + buildDirective("set", "$use_port_in_redirects", strconv.FormatBool(location.UsePortInRedirects)), + } +} + +func isLocationInLocationList(loc *ingress.Location, rawLocationList string) bool { + locationList := strings.Split(rawLocationList, ",") + + for _, locationListItem := range locationList { + locationListItem = strings.Trim(locationListItem, " ") + if locationListItem == "" { + continue + } + if strings.HasPrefix(loc.Path, locationListItem) { + return true + } + } + + return false +} + +func buildAuthLocationConfig(location *ingress.Location, locationConfig locationCfg) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, 0) + if locationConfig.authPath != "" { + if locationConfig.applyAuthUpstream && !locationConfig.applyGlobalAuth { + directives = append(directives, buildDirective("set", "$auth_cookie", ""), + buildDirective("add_header", "Set-Cookie", "$auth_cookie")) + directives = append(directives, buildAuthResponseHeaders(locationConfig.proxySetHeader, locationConfig.externalAuth.ResponseHeaders, true)...) + if len(locationConfig.externalAuth.ResponseHeaders) > 0 { + directives = append(directives, buildDirective("set", "$auth_response_headers", strings.Join(locationConfig.externalAuth.ResponseHeaders, ","))) + } + directives = append(directives, + buildDirective("set", "$auth_path", locationConfig.authPath), + buildDirective("set", "$auth_keepalive_share_vars", strconv.FormatBool(locationConfig.externalAuth.KeepaliveShareVars)), + buildDirective("access_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_external_auth.lua"), + ) + } else { + directives = append(directives, + buildDirective("auth_request", locationConfig.authPath), + buildDirective("auth_request_set", "$auth_cookie", "$upstream_http_set_cookie"), + ) + cookieDirective := buildDirective("add_header", "Set-Cookie", "$auth_cookie") + if locationConfig.externalAuth.AlwaysSetCookie { + cookieDirective.Args = append(cookieDirective.Args, "always") + } + directives = append(directives, cookieDirective) + directives = append(directives, buildAuthResponseHeaders(locationConfig.proxySetHeader, locationConfig.externalAuth.ResponseHeaders, false)...) + } + } + + if locationConfig.externalAuth.SigninURL != "" { + directives = append(directives, + buildDirective("set_escape_uri", "$escaped_request_uri", "$request_uri"), + buildDirective("error_page", "401", "=", buildAuthSignURLLocation(location.Path, locationConfig.externalAuth.SigninURL)), + ) + } + if location.BasicDigestAuth.Secured { + var authDirective, authFileDirective string + if location.BasicDigestAuth.Type == "basic" { + authDirective, authFileDirective = "auth_basic", "auth_basic_user_file" + } else { + authDirective, authFileDirective = "auth_digest", "auth_digest_user_file" + } + + directives = append(directives, + buildDirective(authDirective, location.BasicDigestAuth.Realm), + buildDirective(authFileDirective, location.BasicDigestAuth.File), + buildDirective(locationConfig.proxySetHeader, "Authorization", ""), + ) + } + + return directives +} diff --git a/internal/ingress/controller/template/crossplane/server.go b/internal/ingress/controller/template/crossplane/server.go new file mode 100644 index 0000000000..a78a5a351a --- /dev/null +++ b/internal/ingress/controller/template/crossplane/server.go @@ -0,0 +1,270 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "fmt" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + + "k8s.io/ingress-nginx/pkg/apis/ingress" + utilingress "k8s.io/ingress-nginx/pkg/util/ingress" + "k8s.io/utils/ptr" +) + +func (c *Template) buildServerDirective(server *ingress.Server) *ngx_crossplane.Directive { + cfg := c.tplConfig.Cfg + serverName := buildServerName(server.Hostname) + serverBlock := ngx_crossplane.Directives{ + buildDirective("server_name", serverName, server.Aliases), + buildDirective("http2", cfg.UseHTTP2), + buildDirective("set", "$proxy_upstream_name", "-"), + buildDirective("ssl_certificate_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_certificate.lua"), + } + + serverBlock = append(serverBlock, buildListener(c.tplConfig, server.Hostname)...) + serverBlock = append(serverBlock, c.buildBlockers()...) + + if server.Hostname == "_" { + serverBlock = append(serverBlock, buildDirective("ssl_reject_handshake", cfg.SSLRejectHandshake)) + } + + if server.CertificateAuth.MatchCN != "" { + matchCNBlock := buildBlockDirective("if", + []string{"$ssl_client_s_dn", "!~", server.CertificateAuth.MatchCN}, + ngx_crossplane.Directives{ + buildDirective("return", "403", "client certificate unauthorized"), + }) + serverBlock = append(serverBlock, matchCNBlock) + } + + if server.AuthTLSError != "" { + serverBlock = append(serverBlock, buildDirective("return", 403)) + } else { + serverBlock = append(serverBlock, c.buildCertificateDirectives(server)...) + serverBlock = append(serverBlock, buildCustomErrorLocationsPerServer(server, c.tplConfig.EnableMetrics)...) + serverBlock = append(serverBlock, buildMirrorLocationDirective(server.Locations)...) + + // The other locations should come here! + serverBlock = append(serverBlock, c.buildServerLocations(server, server.Locations)...) + } + + // "/healthz" location + if server.Hostname == "_" { + dirs := ngx_crossplane.Directives{ + buildDirective("access_log", "off"), + buildDirective("return", "200"), + } + if cfg.EnableOpentelemetry { + dirs = append(dirs, buildDirective("opentelemetry", "off")) + } + healthLocation := buildBlockDirective("location", + []string{c.tplConfig.HealthzURI}, dirs) + serverBlock = append(serverBlock, healthLocation) + + // "/nginx_status" location + statusLocationDirs := ngx_crossplane.Directives{} + if cfg.EnableOpentelemetry { + statusLocationDirs = append(statusLocationDirs, buildDirective("opentelemetry", "off")) + } + + for _, v := range c.tplConfig.NginxStatusIpv4Whitelist { + statusLocationDirs = append(statusLocationDirs, buildDirective("allow", v)) + } + + if c.tplConfig.IsIPV6Enabled { + for _, v := range c.tplConfig.NginxStatusIpv6Whitelist { + statusLocationDirs = append(statusLocationDirs, buildDirective("allow", v)) + } + } + statusLocationDirs = append(statusLocationDirs, + buildDirective("deny", "all"), + buildDirective("access_log", "off"), + buildDirective("stub_status", "on")) + + // End of "nginx_status" location + + serverBlock = append(serverBlock, buildBlockDirective("location", []string{"/nginx_status"}, statusLocationDirs)) + } + + // DO NOT MOVE! THIS IS THE END DIRECTIVE OF SERVERS + serverBlock = append(serverBlock, buildCustomErrorLocation("upstream-default-backend", cfg.CustomHTTPErrors, c.tplConfig.EnableMetrics)...) + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +func (c *Template) buildCertificateDirectives(server *ingress.Server) ngx_crossplane.Directives { + certDirectives := make(ngx_crossplane.Directives, 0) + + if server.CertificateAuth.CAFileName != "" { + certAuth := server.CertificateAuth + certDirectives = append(certDirectives, + buildDirective("ssl_client_certificate", certAuth.CAFileName), + buildDirective("ssl_verify_client", certAuth.VerifyClient), + buildDirective("ssl_verify_depth", certAuth.ValidationDepth)) + if certAuth.CRLFileName != "" { + certDirectives = append(certDirectives, buildDirective("ssl_crl", certAuth.CRLFileName)) + } + if certAuth.ErrorPage != "" { + certDirectives = append(certDirectives, buildDirective("error_page", "495", "496", "=", certAuth.ErrorPage)) + } + } + + prxSSL := server.ProxySSL + if prxSSL.CAFileName != "" { + certDirectives = append(certDirectives, buildDirective("proxy_ssl_trusted_certificate", prxSSL.CAFileName), + buildDirective("proxy_ssl_ciphers", prxSSL.Ciphers), + buildDirective("proxy_ssl_protocols", strings.Split(prxSSL.Protocols, " ")), + buildDirective("proxy_ssl_verify", prxSSL.Verify), + buildDirective("proxy_ssl_verify_depth", prxSSL.VerifyDepth), + ) + if prxSSL.ProxySSLName != "" { + certDirectives = append(certDirectives, + buildDirective("proxy_ssl_name", prxSSL.ProxySSLName), + buildDirective("proxy_ssl_server_name", prxSSL.ProxySSLServerName)) + } + } + if prxSSL.PemFileName != "" { + certDirectives = append(certDirectives, + buildDirective("proxy_ssl_certificate", prxSSL.PemFileName), + buildDirective("proxy_ssl_certificate_key", prxSSL.PemFileName)) + } + if server.SSLCiphers != "" { + certDirectives = append(certDirectives, buildDirective("ssl_ciphers", server.SSLCiphers)) + } + + if server.SSLPreferServerCiphers != "" { + certDirectives = append(certDirectives, buildDirective("ssl_prefer_server_ciphers", server.SSLPreferServerCiphers)) + } + + return certDirectives +} + +// buildRedirectServer builds the server blocks for redirections +func (c *Template) buildRedirectServer(server *utilingress.Redirect) *ngx_crossplane.Directive { + serverBlock := ngx_crossplane.Directives{ + buildDirective("server_name", server.From), + buildDirective("ssl_certificate_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_certificate.lua"), + buildDirective("set_by_lua_file", "$redirect_to", "/etc/nginx/lua/nginx/ngx_srv_redirect.lua", server.To), + } + serverBlock = append(serverBlock, buildListener(c.tplConfig, server.From)...) + serverBlock = append(serverBlock, c.buildBlockers()...) + serverBlock = append(serverBlock, buildDirective("return", c.tplConfig.Cfg.HTTPRedirectCode, "$redirect_to")) + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +// buildDefaultBackend builds the default catch all server +func (c *Template) buildDefaultBackend() *ngx_crossplane.Directive { + var reusePort *string + if c.tplConfig.Cfg.ReusePort { + reusePort = ptr.To("reuseport") + } + serverBlock := ngx_crossplane.Directives{ + buildDirective("listen", c.tplConfig.ListenPorts.Default, "default_server", reusePort, fmt.Sprintf("backlog=%d", c.tplConfig.BacklogSize)), + } + if c.tplConfig.IsIPV6Enabled { + serverBlock = append(serverBlock, buildDirective( + "listen", + fmt.Sprintf("[::]:%d", c.tplConfig.ListenPorts.Default), + "default_server", reusePort, + fmt.Sprintf("backlog=%d", c.tplConfig.BacklogSize), + )) + } + serverBlock = append(serverBlock, + buildDirective("set", "$proxy_upstream_name", "internal"), + buildDirective("access_log", "off"), + buildBlockDirective("location", []string{"/"}, ngx_crossplane.Directives{ + buildDirective("return", "404"), + })) + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +func (c *Template) buildHealthAndStatsServer() *ngx_crossplane.Directive { + serverBlock := ngx_crossplane.Directives{ + buildDirective("listen", fmt.Sprintf("127.0.0.1:%d", c.tplConfig.StatusPort)), + buildDirective("set", "$proxy_upstream_name", "internal"), + buildDirective("keepalive_timeout", "0"), + buildDirective("gzip", "off"), + buildDirective("access_log", "off"), + buildBlockDirective( + "location", + []string{c.tplConfig.HealthzURI}, ngx_crossplane.Directives{ + buildDirective("return", "200"), + }), + buildBlockDirective( + "location", + []string{"/is-dynamic-lb-initialized"}, ngx_crossplane.Directives{ + buildDirective("content_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_is_dynamic_lb_initialized.lua"), + }), + buildBlockDirective( + "location", + []string{c.tplConfig.StatusPath}, ngx_crossplane.Directives{ + buildDirective("stub_status", "on"), + }), + buildBlockDirective( + "location", + []string{"/configuration"}, ngx_crossplane.Directives{ + buildDirective("client_max_body_size", luaConfigurationRequestBodySize(&c.tplConfig.Cfg)), + buildDirective("client_body_buffer_size", luaConfigurationRequestBodySize(&c.tplConfig.Cfg)), + buildDirective("proxy_buffering", "off"), + buildDirective("content_by_lua_file", "/etc/nginx/lua/nginx/ngx_conf_configuration.lua"), + }), + buildBlockDirective( + "location", + []string{"/"}, ngx_crossplane.Directives{ + buildDirective("return", "404"), + }), + } + if c.tplConfig.Cfg.EnableOpentelemetry { + serverBlock = append(serverBlock, buildDirective("opentelemetry", "off")) + } + + return &ngx_crossplane.Directive{ + Directive: "server", + Block: serverBlock, + } +} + +func (c *Template) buildBlockers() ngx_crossplane.Directives { + blockers := make(ngx_crossplane.Directives, 0) + if len(c.tplConfig.Cfg.BlockUserAgents) > 0 { + uaDirectives := buildBlockDirective("if", []string{"$block_ua"}, ngx_crossplane.Directives{ + buildDirective("return", "403"), + }) + blockers = append(blockers, uaDirectives) + } + + if len(c.tplConfig.Cfg.BlockReferers) > 0 { + refDirectives := buildBlockDirective("if", []string{"$block_ref"}, ngx_crossplane.Directives{ + buildDirective("return", "403"), + }) + blockers = append(blockers, refDirectives) + } + return blockers +} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go new file mode 100644 index 0000000000..eef9b87ca2 --- /dev/null +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -0,0 +1,739 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crossplane + +import ( + "crypto/sha1" //nolint:gosec // We cannot move away from sha1 + "encoding/base64" + "encoding/hex" + "fmt" + "net" + "net/url" + "regexp" + "strconv" + "strings" + + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/annotations/ratelimit" + "k8s.io/ingress-nginx/internal/ingress/controller/config" + ing_net "k8s.io/ingress-nginx/internal/net" + "k8s.io/ingress-nginx/pkg/apis/ingress" +) + +const ( + slash = "/" + nonIdempotent = "non_idempotent" + defBufferSize = 65535 + writeIndentOnEmptyLines = true // backward-compatibility + httpProtocol = "HTTP" + autoHTTPProtocol = "AUTO_HTTP" + httpsProtocol = "HTTPS" + grpcProtocol = "GRPC" + grpcsProtocol = "GRPCS" + fcgiProtocol = "FCGI" +) + +var ( + nginxSizeRegex = regexp.MustCompile(`^\d+[kKmM]?$`) + nginxOffsetRegex = regexp.MustCompile(`^\d+[kKmMgG]?$`) + defaultGlobalAuthRedirectParam = "rd" +) + +type ( + seconds int + minutes int +) + +func buildDirectiveWithComment(directive, comment string, args ...any) *ngx_crossplane.Directive { + dir := buildDirective(directive, args...) + dir.Comment = ptr.To(comment) + return dir +} + +func buildStartServer(name string) *ngx_crossplane.Directive { + return buildDirective("##", "start", "server", name) +} + +func buildEndServer(name string) *ngx_crossplane.Directive { + return buildDirective("##", "end", "server", name) +} + +func buildStartAuthUpstream(name, location string) *ngx_crossplane.Directive { + return buildDirective("##", "start", "auth", "upstream", name, location) +} + +func buildEndAuthUpstream(name, location string) *ngx_crossplane.Directive { + return buildDirective("##", "end", "auth", "upstream", name, location) +} + +func buildDirective(directive string, args ...any) *ngx_crossplane.Directive { + argsVal := make([]string, 0) + for k := range args { + switch v := args[k].(type) { + case string: + argsVal = append(argsVal, v) + case *string: + if v != nil { + argsVal = append(argsVal, *v) + } + case []string: + argsVal = append(argsVal, v...) + case int: + argsVal = append(argsVal, strconv.Itoa(v)) + case bool: + argsVal = append(argsVal, boolToStr(v)) + case seconds: + argsVal = append(argsVal, strconv.Itoa(int(v))+"s") + case minutes: + argsVal = append(argsVal, strconv.Itoa(int(v))+"m") + } + } + return &ngx_crossplane.Directive{ + Directive: directive, + Args: argsVal, + } +} + +func buildLuaSharedDictionaries(cfg *config.Configuration) []*ngx_crossplane.Directive { + out := make([]*ngx_crossplane.Directive, 0, len(cfg.LuaSharedDicts)) + for name, size := range cfg.LuaSharedDicts { + sizeStr := dictKbToStr(size) + out = append(out, buildDirective("lua_shared_dict", name, sizeStr)) + } + + return out +} + +// TODO: The utils below should be moved to a level where they can be consumed by any template writer + +// buildResolvers returns the resolvers reading the /etc/resolv.conf file +func buildResolversInternal(res []net.IP, disableIpv6 bool) []string { + r := make([]string, 0) + for _, ns := range res { + if ing_net.IsIPV6(ns) { + if disableIpv6 { + continue + } + r = append(r, fmt.Sprintf("[%s]", ns)) + } else { + r = append(r, ns.String()) + } + } + r = append(r, "valid=30s") + + if disableIpv6 { + r = append(r, "ipv6=off") + } + + return r +} + +// buildBlockDirective is used to build a block directive +func buildBlockDirective(blockName string, args []string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { + return &ngx_crossplane.Directive{ + Directive: blockName, + Args: args, + Block: block, + } +} + +// buildMapDirective is used to build a map directive +func buildMapDirective(name, variable string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { + return buildBlockDirective("map", []string{name, variable}, block) +} + +func boolToStr(b bool) string { + if b { + return "on" + } + return "off" +} + +func dictKbToStr(size int) string { + if size%1024 == 0 { + return fmt.Sprintf("%dM", size/1024) + } + return fmt.Sprintf("%dK", size) +} + +func shouldLoadAuthDigestModule(servers []*ingress.Server) bool { + for _, server := range servers { + for _, location := range server.Locations { + if !location.BasicDigestAuth.Secured { + continue + } + + if location.BasicDigestAuth.Type == "digest" { + return true + } + } + } + return false +} + +// shouldLoadOpentelemetryModule determines whether or not the Opentelemetry module needs to be loaded. +// It checks if `enable-opentelemetry` is set in the ConfigMap. +func shouldLoadOpentelemetryModule(servers []*ingress.Server) bool { + for _, server := range servers { + for _, location := range server.Locations { + if location.Opentelemetry.Enabled { + return true + } + } + } + return false +} + +func buildServerName(hostname string) string { + if !strings.HasPrefix(hostname, "*") { + return hostname + } + + hostname = strings.Replace(hostname, "*.", "", 1) + parts := strings.Split(hostname, ".") + + return `~^(?[\w-]+)\.` + strings.Join(parts, "\\.") + `$` +} + +func buildListener(tc *config.TemplateConfig, hostname string) ngx_crossplane.Directives { + listenDirectives := make(ngx_crossplane.Directives, 0) + + co := commonListenOptions(tc, hostname) + + addrV4 := []string{""} + if len(tc.Cfg.BindAddressIpv4) > 0 { + addrV4 = tc.Cfg.BindAddressIpv4 + } + listenDirectives = append(listenDirectives, httpListener(addrV4, co, tc, false)...) + listenDirectives = append(listenDirectives, httpListener(addrV4, co, tc, true)...) + + if tc.IsIPV6Enabled { + addrV6 := []string{"[::]"} + if len(tc.Cfg.BindAddressIpv6) > 0 { + addrV6 = tc.Cfg.BindAddressIpv6 + } + listenDirectives = append(listenDirectives, httpListener(addrV6, co, tc, false)...) + listenDirectives = append(listenDirectives, httpListener(addrV6, co, tc, true)...) + } + + return listenDirectives +} + +// commonListenOptions defines the common directives that should be added to NGINX listeners +func commonListenOptions(template *config.TemplateConfig, hostname string) []string { + var out []string + + if template.Cfg.UseProxyProtocol { + out = append(out, "proxy_protocol") + } + + if hostname != "_" { + return out + } + + out = append(out, "default_server") + + if template.Cfg.ReusePort { + out = append(out, "reuseport") + } + out = append(out, fmt.Sprintf("backlog=%d", template.BacklogSize)) + return out +} + +func httpListener(addresses, co []string, tc *config.TemplateConfig, ssl bool) ngx_crossplane.Directives { + listeners := make(ngx_crossplane.Directives, 0) + port := tc.ListenPorts.HTTP + isTLSProxy := tc.IsSSLPassthroughEnabled + // If this is a SSL listener we should mutate the port properly + if ssl { + port = tc.ListenPorts.HTTPS + if isTLSProxy { + port = tc.ListenPorts.SSLProxy + } + } + for _, address := range addresses { + var listenAddress string + if address == "" { + listenAddress = fmt.Sprintf("%d", port) + } else { + listenAddress = fmt.Sprintf("%s:%d", address, port) + } + if ssl { + if isTLSProxy { + co = append(co, "proxy_protocol") + } + co = append(co, "ssl") + } + listenDirective := buildDirective("listen", listenAddress, co) + listeners = append(listeners, listenDirective) + } + + return listeners +} + +func luaConfigurationRequestBodySize(cfg *config.Configuration) string { + size := cfg.LuaSharedDicts["configuration_data"] + if size < cfg.LuaSharedDicts["certificate_data"] { + size = cfg.LuaSharedDicts["certificate_data"] + } + size += 1024 + + return dictKbToStr(size) +} + +func buildLocation(location *ingress.Location, enforceRegex bool) []string { + path := location.Path + if enforceRegex { + return []string{"~*", fmt.Sprintf("^%s", path)} + } + + if location.PathType != nil && *location.PathType == networkingv1.PathTypeExact { + return []string{"=", path} + } + + return []string{path} +} + +func getProxySetHeader(location *ingress.Location) string { + if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol { + return "grpc_set_header" + } + + return "proxy_set_header" +} + +func buildAuthLocation(location *ingress.Location, globalExternalAuthURL string) string { + if (location.ExternalAuth.URL == "") && (!shouldApplyGlobalAuth(location, globalExternalAuthURL)) { + return "" + } + + str := base64.URLEncoding.EncodeToString([]byte(location.Path)) + // removes "=" after encoding + str = strings.ReplaceAll(str, "=", "") + + pathType := "default" + if location.PathType != nil { + pathType = string(*location.PathType) + } + + return fmt.Sprintf("/_external-auth-%v-%v", str, pathType) +} + +// shouldApplyGlobalAuth returns true only in case when ExternalAuth.URL is not set and +// GlobalExternalAuth is set and enabled +func shouldApplyGlobalAuth(location *ingress.Location, globalExternalAuthURL string) bool { + return location.ExternalAuth.URL == "" && + globalExternalAuthURL != "" && + location.EnableGlobalAuth +} + +// shouldApplyAuthUpstream returns true only in case when ExternalAuth.URL and +// ExternalAuth.KeepaliveConnections are all set +func shouldApplyAuthUpstream(location *ingress.Location, cfg *config.Configuration) bool { + if location.ExternalAuth.URL == "" || location.ExternalAuth.KeepaliveConnections == 0 { + return false + } + + // Unfortunately, `auth_request` module ignores keepalive in upstream block: https://trac.nginx.org/nginx/ticket/1579 + // The workaround is to use `ngx.location.capture` Lua subrequests but it is not supported with HTTP/2 + if cfg.UseHTTP2 { + return false + } + return true +} + +func isValidByteSize(s string, isOffset bool) bool { + s = strings.TrimSpace(s) + if s == "" { + return false + } + + if isOffset { + return nginxOffsetRegex.MatchString(s) + } + + return nginxSizeRegex.MatchString(s) +} + +func buildAuthUpstreamName(input *ingress.Location, host string) string { + authPath := buildAuthLocation(input, "") + if authPath == "" || host == "" { + return "" + } + + return fmt.Sprintf("%s-%s", host, authPath[2:]) +} + +// changeHostPort will change the host:port part of the url to value +func changeHostPort(newURL, value string) string { + if newURL == "" { + return "" + } + + authURL, err := parser.StringToURL(newURL) + if err != nil { + klog.Errorf("expected a valid URL but %s was returned", newURL) + return "" + } + + authURL.Host = value + + return authURL.String() +} + +func buildAuthSignURLLocation(location, authSignURL string) string { + hasher := sha1.New() //nolint:gosec // We cannot move away from sha1 + hasher.Write([]byte(location)) + hasher.Write([]byte(authSignURL)) + return "@" + hex.EncodeToString(hasher.Sum(nil)) +} + +func buildAuthSignURL(authSignURL, authRedirectParam string) string { + u, err := url.Parse(authSignURL) + if err != nil { + klog.Errorf("error parsing authSignURL: %v", err) + return "" + } + q := u.Query() + if authRedirectParam == "" { + authRedirectParam = defaultGlobalAuthRedirectParam + } + if len(q) == 0 { + return fmt.Sprintf("%v?%v=$pass_access_scheme://$http_host$escaped_request_uri", authSignURL, authRedirectParam) + } + + if q.Get(authRedirectParam) != "" { + return authSignURL + } + + return fmt.Sprintf("%v&%v=$pass_access_scheme://$http_host$escaped_request_uri", authSignURL, authRedirectParam) +} + +func buildCorsOriginRegex(corsOrigins []string) ngx_crossplane.Directives { + if len(corsOrigins) == 1 && corsOrigins[0] == "*" { + return ngx_crossplane.Directives{ + buildDirective("set", "$http_origin", "*"), + buildDirective("set", "$cors", "true"), + } + } + + originsArray := []string{"("} + for i, origin := range corsOrigins { + originTrimmed := strings.TrimSpace(origin) + if originTrimmed != "" { + originsArray = append(originsArray, buildOriginRegex(originTrimmed)) + } + if i != len(corsOrigins)-1 { + originsArray = append(originsArray, "|") + } + } + originsArray = append(originsArray, ")$") + + // originsArray should be converted to a single string, as it is a single directive for if. + origins := strings.Join(originsArray, "") + return ngx_crossplane.Directives{ + buildBlockDirective("if", []string{"$http_origin", "~*", origins}, ngx_crossplane.Directives{ + buildDirective("set", "$cors", "true"), + }), + } +} + +func buildOriginRegex(origin string) string { + origin = regexp.QuoteMeta(origin) + origin = strings.Replace(origin, "\\*", `[A-Za-z0-9\-]+`, 1) + return fmt.Sprintf("(%s)", origin) +} + +func buildNextUpstream(nextUpstream string, retryNonIdempotent bool) []string { + parts := strings.Split(nextUpstream, " ") + + nextUpstreamCodes := make([]string, 0, len(parts)) + for _, v := range parts { + if v != "" && v != nonIdempotent { + nextUpstreamCodes = append(nextUpstreamCodes, v) + } + + if v == nonIdempotent { + retryNonIdempotent = true + } + } + + if retryNonIdempotent { + nextUpstreamCodes = append(nextUpstreamCodes, nonIdempotent) + } + + return nextUpstreamCodes +} + +func buildProxyPass(backends []*ingress.Backend, location *ingress.Location) ngx_crossplane.Directives { + path := location.Path + proto := "http://" + proxyPass := "proxy_pass" + + switch strings.ToUpper(location.BackendProtocol) { + case autoHTTPProtocol: + proto = "$scheme://" + case httpsProtocol: + proto = "https://" + case grpcProtocol: + proto = "grpc://" + proxyPass = "grpc_pass" + case grpcsProtocol: + proto = "grpcs://" + proxyPass = "grpc_pass" + case fcgiProtocol: + proto = "" + proxyPass = "fastcgi_pass" + } + + upstreamName := "upstream_balancer" + + for _, backend := range backends { + if backend.Name == location.Backend { + if backend.SSLPassthrough { + proto = "https://" + + if location.BackendProtocol == grpcsProtocol { + proto = "grpcs://" + } + } + + break + } + } + + if location.Backend == "upstream-default-backend" { + proto = "http://" + proxyPass = "proxy_pass" + } + + // defProxyPass returns the default proxy_pass, just the name of the upstream + defProxyPass := buildDirective(proxyPass, fmt.Sprintf("%s%s", proto, upstreamName)) + + // if the path in the ingress rule is equals to the target: no special rewrite + if path == location.Rewrite.Target { + return ngx_crossplane.Directives{defProxyPass} + } + + if location.Rewrite.Target != "" { + proxySetHeader := "proxy_set_header" + dir := make(ngx_crossplane.Directives, 0) + if location.BackendProtocol == grpcProtocol || location.BackendProtocol == grpcsProtocol { + proxySetHeader = "grpc_set_header" + } + + if location.XForwardedPrefix != "" { + dir = append(dir, + buildDirective(proxySetHeader, "X-Forwarded-Prefix", location.XForwardedPrefix), + ) + } + + dir = append(dir, + buildDirective("rewrite", fmt.Sprintf("(?i)%s", path), location.Rewrite.Target, "break"), + buildDirective(proxyPass, fmt.Sprintf("%s%s", proto, upstreamName)), + ) + return dir + } + + // default proxy_pass + return ngx_crossplane.Directives{defProxyPass} +} + +func buildGeoIPDirectives(reloadTime int, files []string) ngx_crossplane.Directives { + directives := make(ngx_crossplane.Directives, 0) + buildGeoIPBlock := func(file string, directives ngx_crossplane.Directives) *ngx_crossplane.Directive { + if reloadTime > 0 && file != "GeoIP2-Connection-Type.mmdb" { + directives = append(directives, buildDirective("auto_reload", minutes(reloadTime))) + } + fileName := fmt.Sprintf("/etc/ingress-controller/geoip/%s", file) + return buildBlockDirective("geoip2", []string{fileName}, directives) + } + + for _, file := range files { + if file == "GeoLite2-Country.mmdb" || file == "GeoIP2-Country.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_country_code", "source=$remote_addr", "country", "iso_code"), + buildDirective("$geoip2_country_name", "source=$remote_addr", "country", "names", "en"), + buildDirective("$geoip2_country_geoname_id", "source=$remote_addr", "country", "geoname_id"), + buildDirective("$geoip2_continent_code", "source=$remote_addr", "continent", "code"), + buildDirective("$geoip2_continent_name", "source=$remote_addr", "continent", "names", "en"), + buildDirective("$geoip2_continent_geoname_id", "source=$remote_addr", "continent", "geoname_id"), + })) + } + if file == "GeoLite2-City.mmdb" || file == "GeoIP2-City.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_city_country_code", "source=$remote_addr", "country", "iso_code"), + buildDirective("$geoip2_city_country_name", "source=$remote_addr", "country", "names", "en"), + buildDirective("$geoip2_city_country_geoname_id", "source=$remote_addr", "country", "geoname_id"), + buildDirective("$geoip2_city_continent_code", "source=$remote_addr", "continent", "code"), + buildDirective("$geoip2_city_continent_name", "source=$remote_addr", "continent", "names", "en"), + buildDirective("$geoip2_city", "source=$remote_addr", "city", "names", "en"), + buildDirective("$geoip2_city_geoname_id", "source=$remote_addr", "city", "geoname_id"), + buildDirective("$geoip2_postal_code", "source=$remote_addr", "postal", "code"), + buildDirective("$geoip2_dma_code", "source=$remote_addr", "location", "metro_code"), + buildDirective("$geoip2_latitude", "source=$remote_addr", "location", "latitude"), + buildDirective("$geoip2_longitude", "source=$remote_addr", "location", "longitude"), + buildDirective("$geoip2_time_zone", "source=$remote_addr", "location", "time_zone"), + buildDirective("$geoip2_region_code", "source=$remote_addr", "subdivisions", "0", "iso_code"), + buildDirective("$geoip2_region_name", "source=$remote_addr", "subdivisions", "0", "names", "en"), + buildDirective("$geoip2_region_geoname_id", "source=$remote_addr", "subdivisions", "0", "geoname_id"), + buildDirective("$geoip2_subregion_code", "source=$remote_addr", "subdivisions", "1", "iso_code"), + buildDirective("$geoip2_subregion_name", "source=$remote_addr", "subdivisions", "1", "names", "en"), + buildDirective("$geoip2_subregion_geoname_id", "source=$remote_addr", "subdivisions", "1", "geoname_id"), + })) + } + if file == "GeoLite2-ASN.mmdb" || file == "GeoIP2-ASN.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_asn", "source=$remote_addr", "autonomous_system_number"), + buildDirective("$geoip2_org", "source=$remote_addr", "autonomous_system_organization"), + })) + } + if file == "GeoIP2-ISP.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_isp", "source=$remote_addr", "isp"), + buildDirective("$geoip2_isp_org", "source=$remote_addr", "organization"), + buildDirective("$geoip2_asn", "source=$remote_addr", "autonomous_system_number"), + })) + } + if file == "GeoIP2-Anonymous-IP.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_is_anon", "source=$remote_addr", "is_anonymous"), + buildDirective("$geoip2_is_anonymous", "source=$remote_addr", "default=0", "is_anonymous"), + buildDirective("$geoip2_is_anonymous_vpn", "source=$remote_addr", "default=0", "is_anonymous_vpn"), + buildDirective("$geoip2_is_hosting_provider", "source=$remote_addr", "default=0", "is_hosting_provider"), + buildDirective("$geoip2_is_public_proxy", "source=$remote_addr", "default=0", "is_public_proxy"), + buildDirective("$geoip2_is_tor_exit_node", "source=$remote_addr", "default=0", "is_tor_exit_node"), + })) + } + if file == "GeoIP2-Connection-Type.mmdb" { + directives = append(directives, buildGeoIPBlock(file, ngx_crossplane.Directives{ + buildDirective("$geoip2_connection_type", "connection_type"), + })) + } + } + return directives +} + +func filterRateLimits(servers []*ingress.Server) []ratelimit.Config { + ratelimits := []ratelimit.Config{} + found := sets.Set[string]{} + + for _, server := range servers { + for _, loc := range server.Locations { + if loc.RateLimit.ID != "" && !found.Has(loc.RateLimit.ID) { + found.Insert(loc.RateLimit.ID) + ratelimits = append(ratelimits, loc.RateLimit) + } + } + } + return ratelimits +} + +// buildRateLimitZones produces an array of limit_conn_zone in order to allow +// rate limiting of request. Each Ingress rule could have up to three zones, one +// for connection limit by IP address, one for limiting requests per minute, and +// one for limiting requests per second. +func buildRateLimitZones(servers []*ingress.Server) ngx_crossplane.Directives { + zones := make(map[string]bool) + directives := make(ngx_crossplane.Directives, 0) + for _, server := range servers { + for _, loc := range server.Locations { + zoneID := fmt.Sprintf("$limit_%s", loc.RateLimit.ID) + if loc.RateLimit.Connections.Limit > 0 { + zoneArg := fmt.Sprintf("zone=%s:%dm", loc.RateLimit.Connections.Name, loc.RateLimit.Connections.SharedSize) + zone := fmt.Sprintf("limit_conn_zone %s %s", zoneID, zoneArg) + if _, ok := zones[zone]; !ok { + zones[zone] = true + directives = append(directives, buildDirective("limit_conn_zone", zoneID, zoneArg)) + } + } + + if loc.RateLimit.RPM.Limit > 0 { + zoneArg := fmt.Sprintf("zone=%s:%dm", loc.RateLimit.RPM.Name, loc.RateLimit.RPM.SharedSize) + zoneRate := fmt.Sprintf("rate=%dr/m", loc.RateLimit.RPM.Limit) + zone := fmt.Sprintf("limit_req_zone %s %s %s", zoneID, zoneArg, zoneRate) + if _, ok := zones[zone]; !ok { + zones[zone] = true + directives = append(directives, buildDirective("limit_req_zone", zoneID, zoneArg, zoneRate)) + } + } + + if loc.RateLimit.RPS.Limit > 0 { + zoneArg := fmt.Sprintf("zone=%s:%dm", loc.RateLimit.RPS.Name, loc.RateLimit.RPS.SharedSize) + zoneRate := fmt.Sprintf("rate=%dr/s", loc.RateLimit.RPS.Limit) + zone := fmt.Sprintf("limit_req_zone %s %s %s", zoneID, zoneArg, zoneRate) + if _, ok := zones[zone]; !ok { + zones[zone] = true + directives = append(directives, buildDirective("limit_req_zone", zoneID, zoneArg, zoneRate)) + } + } + } + } + return directives +} + +// buildAuthResponseHeaders sets HTTP response headers when `auth-url` is used. +// Based on `auth-keepalive` value we use auth_request_set Nginx directives, or +// we use Lua and Nginx variables instead. +// +// NOTE: Unfortunately auth_request module ignores the keepalive directive (see: +// https://trac.nginx.org/nginx/ticket/1579), that is why we mimic the same +// functionality with access_by_lua_block. +// TODO: This function is duplicated with the non-crossplane and we should consolidate +func buildAuthResponseHeaders(proxySetHeader string, headers []string, lua bool) ngx_crossplane.Directives { + res := make(ngx_crossplane.Directives, 0) + + if len(headers) == 0 { + return res + } + + for i, h := range headers { + authHeader := fmt.Sprintf("$authHeader%d", i) + if lua { + res = append(res, buildDirective("set", authHeader, "")) + } else { + hvar := strings.ToLower(h) + hvar = strings.NewReplacer("-", "_").Replace(hvar) + res = append(res, buildDirective("auth_request_set", + authHeader, + fmt.Sprintf("$upstream_http_%s", hvar))) + } + res = append(res, buildDirective(proxySetHeader, h, authHeader)) + } + return res +} + +// extractHostPort will extract the host:port part from the URL specified by url +func extractHostPort(newURL string) string { + if newURL == "" { + return "" + } + + authURL, err := parser.StringToURL(newURL) + if err != nil { + klog.Errorf("expected a valid URL but %s was returned", newURL) + return "" + } + + return authURL.Host +} diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index ed052e4ecf..b976ec142a 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -71,6 +71,10 @@ type Writer interface { // NOTE: Implementors must ensure that the content of the returned slice is not modified by the implementation // after the return of this function. Write(conf *config.TemplateConfig) ([]byte, error) + // Validate is a function that can be called, containing the file name to be tested + // This function should be used just by specific cases like crossplane, otherwise it can return + // null error + Validate(filename string) error } // Template ingress template @@ -229,6 +233,11 @@ type LuaListenPorts struct { SSLProxyPort string `json:"ssl_proxy"` } +// Validate is no-op at go-template +func (t *Template) Validate(_ string) error { + return nil +} + // Write populates a buffer using a template with NGINX configuration // and the servers and upstreams created by Ingress rules func (t *Template) Write(conf *config.TemplateConfig) ([]byte, error) { diff --git a/magefiles/go.mod b/magefiles/go.mod index 64f18e43d2..0a43081049 100644 --- a/magefiles/go.mod +++ b/magefiles/go.mod @@ -8,31 +8,30 @@ require ( github.com/helm/helm v2.17.0+incompatible github.com/magefile/mage v1.15.0 github.com/vmware-labs/yaml-jsonpath v0.3.2 - golang.org/x/oauth2 v0.18.0 + golang.org/x/oauth2 v0.23.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.30.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/onsi/gomega v1.34.2 // indirect github.com/sergi/go-diff v1.3.1 // indirect - github.com/stretchr/testify v1.9.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.26.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apimachinery v0.29.3 // indirect + k8s.io/apimachinery v0.31.2 // indirect k8s.io/helm v2.17.0+incompatible // indirect ) diff --git a/magefiles/go.sum b/magefiles/go.sum index 16bc8460fa..17ba71e9ff 100644 --- a/magefiles/go.sum +++ b/magefiles/go.sum @@ -5,17 +5,15 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 h1:aRd8M7HJVZOqn/vhOzrGcQH0lNAMkqMn+pXUYkatmcA= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -28,15 +26,12 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v48 v48.2.0 h1:68puzySE6WqUY9KWmpOsDEQfDZsso98rT6pZcz9HqcE= @@ -67,12 +62,10 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= @@ -84,30 +77,21 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -116,41 +100,25 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -168,7 +136,6 @@ gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go index ce24160fdc..ff8fa3d737 100644 --- a/pkg/flags/flags.go +++ b/pkg/flags/flags.go @@ -233,6 +233,8 @@ Takes the form ":port". If not provided, no admission controller is starte disableSyncEvents = flags.Bool("disable-sync-events", false, "Disables the creation of 'Sync' event resources") enableTopologyAwareRouting = flags.Bool("enable-topology-aware-routing", false, "Enable topology aware routing feature, needs service object annotation service.kubernetes.io/topology-mode sets to auto.") + + configurationTemplateEngine = flags.String("configuration-template-engine", "go-template", "Defines what configuration template engine should be used. Can be 'go-template' or 'crossplane'. ") ) flags.StringVar(&nginx.MaxmindMirror, "maxmind-mirror", "", `Maxmind mirror url (example: http://geoip.local/databases.`) @@ -303,6 +305,10 @@ https://blog.maxmind.com/2019/12/significant-changes-to-accessing-and-using-geol return false, nil, fmt.Errorf("flags --publish-service and --publish-status-address are mutually exclusive") } + if *enableSSLPassthrough && *configurationTemplateEngine != "go-template" { + return false, nil, fmt.Errorf("SSL Passthrough can only be enabled with 'go-template' configuration engine") + } + nginx.HealthPath = *defHealthzURL if *defHealthCheckTimeout > 0 { @@ -390,12 +396,13 @@ https://blog.maxmind.com/2019/12/significant-changes-to-accessing-and-using-geol WatchWithoutClass: *watchWithoutClass, IngressClassByName: *ingressClassByName, }, - DisableCatchAll: *disableCatchAll, - ValidationWebhook: *validationWebhook, - ValidationWebhookCertPath: *validationWebhookCert, - ValidationWebhookKeyPath: *validationWebhookKey, - InternalLoggerAddress: *internalLoggerAddress, - DisableSyncEvents: *disableSyncEvents, + DisableCatchAll: *disableCatchAll, + ValidationWebhook: *validationWebhook, + ValidationWebhookCertPath: *validationWebhookCert, + ValidationWebhookKeyPath: *validationWebhookKey, + InternalLoggerAddress: *internalLoggerAddress, + DisableSyncEvents: *disableSyncEvents, + ConfigurationTemplateEngine: *configurationTemplateEngine, } if *apiserverHost != "" { diff --git a/test/e2e-image/e2e.sh b/test/e2e-image/e2e.sh index f8ecd5337d..f793c76819 100755 --- a/test/e2e-image/e2e.sh +++ b/test/e2e-image/e2e.sh @@ -24,7 +24,7 @@ E2E_CHECK_LEAKS=${E2E_CHECK_LEAKS:-""} reportFile="report-e2e-test-suite.xml" ginkgo_args=( - "--fail-fast" + # "--fail-fast" "--flake-attempts=2" "--junit-report=${reportFile}" "--nodes=${E2E_NODES}" diff --git a/test/e2e/admission/admission.go b/test/e2e/admission/admission.go index 873e6719da..870df21b27 100644 --- a/test/e2e/admission/admission.go +++ b/test/e2e/admission/admission.go @@ -100,6 +100,9 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if there is an error validating the ingress definition", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -208,6 +211,9 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should return an error if the Ingress V1 definition contains invalid annotations", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crissplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -222,6 +228,9 @@ var _ = framework.IngressNginxDescribeSerial("[Admission] admission controller", }) ginkgo.It("should not return an error for an invalid Ingress when it has unknown class", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() out, err := createIngress(f.Namespace, invalidV1IngressWithOtherClass) diff --git a/test/e2e/annotations/affinity.go b/test/e2e/annotations/affinity.go index b64581ef62..629277ddf1 100644 --- a/test/e2e/annotations/affinity.go +++ b/test/e2e/annotations/affinity.go @@ -58,7 +58,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -80,7 +81,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -115,7 +117,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -181,7 +184,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -212,7 +216,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) local, err := time.LoadLocation("GMT") @@ -243,7 +248,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -265,7 +271,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -289,7 +296,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -312,7 +320,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -393,7 +402,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { // server alias sort by sort.Strings(), see: internal/ingress/annotations/alias/main.go:60 - return strings.Contains(server, fmt.Sprintf("server_name %s %s %s ;", host, alias1, alias2)) + return strings.Contains(server, fmt.Sprintf("server_name %s %s %s ;", host, alias1, alias2)) || + strings.Contains(server, fmt.Sprintf("server_name %s %s %s;", host, alias1, alias2)) }) f.HTTPTestClient(). @@ -430,7 +440,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -453,7 +464,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.HTTPTestClient(). @@ -475,7 +487,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && + return (strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host))) && strings.Contains(server, "listen 443") }) diff --git a/test/e2e/annotations/affinitymode.go b/test/e2e/annotations/affinitymode.go index e6253b6ffa..7b5f47c565 100644 --- a/test/e2e/annotations/affinitymode.go +++ b/test/e2e/annotations/affinitymode.go @@ -56,7 +56,8 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) // Check configuration @@ -89,7 +90,8 @@ var _ = framework.DescribeAnnotation("affinitymode", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) // Check configuration diff --git a/test/e2e/annotations/auth.go b/test/e2e/annotations/auth.go index ddda1dce5f..5ecff23b43 100644 --- a/test/e2e/annotations/auth.go +++ b/test/e2e/annotations/auth.go @@ -270,6 +270,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should set snippet "proxy_set_header My-Custom-Header 42;" when external auth is configured`, func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not support snippets") + } host := authHost annotations := map[string]string{ @@ -290,6 +293,9 @@ var _ = framework.DescribeAnnotation("auth-*", func() { }) ginkgo.It(`should not set snippet "proxy_set_header My-Custom-Header 42;" when external auth is not configured`, func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not support snippets") + } host := authHost disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -325,7 +331,8 @@ var _ = framework.DescribeAnnotation("auth-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `proxy_set_header 'My-Custom-Header' '42';`) + return strings.Contains(server, `proxy_set_header 'My-Custom-Header' '42';`) || + strings.Contains(server, `proxy_set_header My-Custom-Header 42;`) }) }) @@ -412,7 +419,6 @@ http { f.EnsureIngress(ing2) f.WaitForNginxServer(host, func(server string) bool { - //nolint:goconst //server_name is a constant return strings.Contains(server, "server_name "+host) }) }) @@ -531,7 +537,8 @@ http { f.UpdateIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("proxy_set_header '%s' $authHeader0;", rewriteHeader)) + return strings.Contains(server, fmt.Sprintf("proxy_set_header '%s' $authHeader0;", rewriteHeader)) || + strings.Contains(server, fmt.Sprintf("proxy_set_header %s $authHeader0;", rewriteHeader)) }) f.HTTPTestClient(). @@ -893,6 +900,9 @@ http { }) ginkgo.It("should add error to the config", func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not allows injecting invalid configuration") + } f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, "could not parse auth-url annotation: invalid url host") }) diff --git a/test/e2e/annotations/canary.go b/test/e2e/annotations/canary.go index ea733dbf43..3632ed6967 100644 --- a/test/e2e/annotations/canary.go +++ b/test/e2e/annotations/canary.go @@ -1091,13 +1091,21 @@ var _ = framework.DescribeAnnotation("canary-*", func() { f.WaitForNginxServer("_", func(server string) bool { - upstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, framework.HTTPBunService, "80") - canaryUpstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-%s";`, f.Namespace, canaryService, "80") + upstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-80";`, f.Namespace, framework.HTTPBunService) + upstreamNameCrossplane := fmt.Sprintf(`set $proxy_upstream_name %s-%s-80;`, f.Namespace, framework.HTTPBunService) - return strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, host)) && + canaryUpstreamName := fmt.Sprintf(`set $proxy_upstream_name "%s-%s-80";`, f.Namespace, canaryService) + canaryUpstreamNameCrossplane := fmt.Sprintf(`set $proxy_upstream_name %s-%s-80;`, f.Namespace, canaryService) + + return (strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, host)) && !strings.Contains(server, `set $proxy_upstream_name "upstream-default-backend";`) && !strings.Contains(server, canaryUpstreamName) && - strings.Contains(server, upstreamName) + strings.Contains(server, upstreamName)) || + // Crossplane assertion + (strings.Contains(server, fmt.Sprintf(`set $ingress_name %s;`, host)) && + !strings.Contains(server, `set $proxy_upstream_name "pstream-default-backend;`) && + !strings.Contains(server, canaryUpstreamNameCrossplane) && + strings.Contains(server, upstreamNameCrossplane)) }) }) diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go index 58f4445f70..57206b2e67 100644 --- a/test/e2e/annotations/cors.go +++ b/test/e2e/annotations/cors.go @@ -48,13 +48,21 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';") && + return (strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS';") && strings.Contains(server, "more_set_headers 'Access-Control-Allow-Origin: $http_origin';") && strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';") && strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 1728000';") && strings.Contains(server, "more_set_headers 'Access-Control-Allow-Credentials: true';") && strings.Contains(server, "set $http_origin *;") && - strings.Contains(server, "$cors 'true';") + strings.Contains(server, "$cors 'true';")) || + // Assertions for crossplane mode + (strings.Contains(server, `more_set_headers "Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS";`) && + strings.Contains(server, `more_set_headers "Access-Control-Allow-Origin: $http_origin";`) && + strings.Contains(server, `more_set_headers "Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";`) && + strings.Contains(server, `more_set_headers "Access-Control-Max-Age: 1728000";`) && + strings.Contains(server, `more_set_headers "Access-Control-Allow-Credentials: true";`) && + strings.Contains(server, "set $http_origin *;") && + strings.Contains(server, "$cors true;")) }) f.HTTPTestClient(). @@ -76,7 +84,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: POST, GET';") + return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Methods: POST, GET';") || + strings.Contains(server, `more_set_headers "Access-Control-Allow-Methods: POST, GET";`) }) }) @@ -92,7 +101,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 200';") + return strings.Contains(server, "more_set_headers 'Access-Control-Max-Age: 200';") || + strings.Contains(server, `more_set_headers "Access-Control-Max-Age: 200";`) }) }) @@ -151,7 +161,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT, User-Agent';") + return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Headers: DNT, User-Agent';") || + strings.Contains(server, `more_set_headers "Access-Control-Allow-Headers: DNT, User-Agent";`) }) }) @@ -167,7 +178,8 @@ var _ = framework.DescribeAnnotation("cors-*", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "more_set_headers 'Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader';") + return strings.Contains(server, "more_set_headers 'Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader';") || + strings.Contains(server, `more_set_headers "Access-Control-Expose-Headers: X-CustomResponseHeader, X-CustomSecondHeader";`) }) }) diff --git a/test/e2e/annotations/fastcgi.go b/test/e2e/annotations/fastcgi.go index bcf1c3487b..b4684faefc 100644 --- a/test/e2e/annotations/fastcgi.go +++ b/test/e2e/annotations/fastcgi.go @@ -64,7 +64,8 @@ var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "fastcgi_index \"index.php\";") + return strings.Contains(server, "fastcgi_index \"index.php\";") || + strings.Contains(server, "fastcgi_index index.php;") }) }) @@ -94,8 +95,10 @@ var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"$fastcgi_script_name\";") && - strings.Contains(server, "fastcgi_param REDIRECT_STATUS \"200\";") + return (strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"$fastcgi_script_name\";") && + strings.Contains(server, "fastcgi_param REDIRECT_STATUS \"200\";")) || + (strings.Contains(server, "fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;") && + strings.Contains(server, "fastcgi_param REDIRECT_STATUS 200;")) }) }) diff --git a/test/e2e/annotations/fromtowwwredirect.go b/test/e2e/annotations/fromtowwwredirect.go index a3fb3b9b5d..2f56c23150 100644 --- a/test/e2e/annotations/fromtowwwredirect.go +++ b/test/e2e/annotations/fromtowwwredirect.go @@ -62,17 +62,20 @@ var _ = framework.DescribeAnnotation("from-to-www-redirect", func() { }) ginkgo.It("should redirect from www HTTPS to HTTPS", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() - ginkgo.By("setting up server for redirect from www") + h := make(map[string]string) + h["ExpectedHost"] = "$http_host" + cfgMap := "add-headers-configmap" + + f.CreateConfigMap(cfgMap, h) + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%s/%s", f.Namespace, cfgMap)) + fromHost := fmt.Sprintf("%s.nip.io", f.GetNginxIP()) toHost := fmt.Sprintf("www.%s", fromHost) annotations := map[string]string{ - "nginx.ingress.kubernetes.io/from-to-www-redirect": "true", - "nginx.ingress.kubernetes.io/configuration-snippet": "more_set_headers \"ExpectedHost: $http_host\";", + "nginx.ingress.kubernetes.io/from-to-www-redirect": "true", } ing := framework.NewSingleIngressWithTLS(fromHost, "/", fromHost, []string{fromHost, toHost}, f.Namespace, framework.EchoService, 80, annotations) diff --git a/test/e2e/annotations/grpc.go b/test/e2e/annotations/grpc.go index 2a9c5a9835..049cf931d2 100644 --- a/test/e2e/annotations/grpc.go +++ b/test/e2e/annotations/grpc.go @@ -106,7 +106,6 @@ var _ = framework.DescribeAnnotation("backend-protocol - GRPC", func() { return strings.Contains(server, "grpc_pass grpc://upstream_balancer;") }) - //nolint:goconst //string interpolation conn, err := grpc.NewClient(f.GetNginxIP()+":443", grpc.WithTransportCredentials( credentials.NewTLS(&tls.Config{ diff --git a/test/e2e/annotations/http2pushpreload.go b/test/e2e/annotations/http2pushpreload.go index b15d2df179..2c317eef8d 100644 --- a/test/e2e/annotations/http2pushpreload.go +++ b/test/e2e/annotations/http2pushpreload.go @@ -25,6 +25,10 @@ import ( ) var _ = framework.DescribeAnnotation("http2-push-preload", func() { + if framework.IsCrossplane() { + // Http2 Push preload is removed from crossplane as it is deprecated + return + } f := framework.NewDefaultFramework("http2pushpreload") ginkgo.BeforeEach(func() { diff --git a/test/e2e/annotations/limitconnections.go b/test/e2e/annotations/limitconnections.go index 7d00b6df06..e660a233a8 100644 --- a/test/e2e/annotations/limitconnections.go +++ b/test/e2e/annotations/limitconnections.go @@ -41,7 +41,8 @@ var _ = framework.DescribeAnnotation("Annotation - limit-connections", func() { ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.SlowEchoService, 80, nil) f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) // limit connections diff --git a/test/e2e/annotations/mirror.go b/test/e2e/annotations/mirror.go index 787cbfa3bc..c6cd028c71 100644 --- a/test/e2e/annotations/mirror.go +++ b/test/e2e/annotations/mirror.go @@ -60,7 +60,8 @@ var _ = framework.DescribeAnnotation("mirror-*", func() { func(server string) bool { return strings.Contains(server, fmt.Sprintf("mirror /_mirror-%v;", ing.UID)) && strings.Contains(server, "mirror_request_body on;") && - strings.Contains(server, `proxy_pass "https://test.env.com/$request_uri";`) + (strings.Contains(server, `proxy_pass "https://test.env.com/$request_uri";`) || + strings.Contains(server, `proxy_pass https://test.env.com/$request_uri;`)) }) }) diff --git a/test/e2e/annotations/modsecurity/modsecurity.go b/test/e2e/annotations/modsecurity/modsecurity.go index 730fc76e78..606a50ede2 100644 --- a/test/e2e/annotations/modsecurity/modsecurity.go +++ b/test/e2e/annotations/modsecurity/modsecurity.go @@ -38,6 +38,9 @@ const ( var _ = framework.DescribeAnnotation("modsecurity owasp", func() { f := framework.NewDefaultFramework("modsecuritylocation") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go index 173df29f00..416e0190e6 100644 --- a/test/e2e/annotations/rewrite.go +++ b/test/e2e/annotations/rewrite.go @@ -97,8 +97,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/" {`) && - strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) + return (strings.Contains(server, `location ~* "^/" {`) || strings.Contains(server, `location ~* ^/ {`)) && + (strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) || strings.Contains(server, `location ~* ^/.well-known/acme/challenge {`)) }) ginkgo.By("making a second request to the non-rewritten location") @@ -132,8 +132,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo" {`) && - strings.Contains(server, `location ~* "^/foo.+" {`) + return (strings.Contains(server, `location ~* "^/foo" {`) || strings.Contains(server, `location ~* ^/foo {`)) && + (strings.Contains(server, `location ~* "^/foo.+" {`) || strings.Contains(server, `location ~* ^/foo.+ {`)) }) ginkgo.By("ensuring '/foo' matches '~* ^/foo'") @@ -174,7 +174,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo/bar/bar" {`) && + return (strings.Contains(server, `location ~* "^/foo/bar/bar" {`) || + strings.Contains(server, `location ~* ^/foo/bar/bar {`)) && strings.Contains(server, `location ~* "^/foo/bar/[a-z]{3}" {`) }) @@ -202,7 +203,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo/bar/(.+)" {`) + return strings.Contains(server, `location ~* "^/foo/bar/(.+)" {`) || strings.Contains(server, `location ~* ^/foo/bar/(.+) {`) }) ginkgo.By("check that '/foo/bar/bar' redirects to custom rewrite") diff --git a/test/e2e/annotations/serversnippet.go b/test/e2e/annotations/serversnippet.go index c94960a3da..0a6c091048 100644 --- a/test/e2e/annotations/serversnippet.go +++ b/test/e2e/annotations/serversnippet.go @@ -27,6 +27,9 @@ import ( var _ = framework.DescribeAnnotation("server-snippet", func() { f := framework.NewDefaultFramework("serversnippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/annotations/snippet.go b/test/e2e/annotations/snippet.go index 9e3160dcc4..178db4202d 100644 --- a/test/e2e/annotations/snippet.go +++ b/test/e2e/annotations/snippet.go @@ -30,6 +30,9 @@ var _ = framework.DescribeAnnotation("configuration-snippet", func() { "configurationsnippet", framework.WithHTTPBunEnabled(), ) + if framework.IsCrossplane() { + return + } ginkgo.It("set snippet more_set_headers in all locations", func() { host := "configurationsnippet.foo.com" diff --git a/test/e2e/annotations/streamsnippet.go b/test/e2e/annotations/streamsnippet.go index f91cdc34e3..1a5f7fd5de 100644 --- a/test/e2e/annotations/streamsnippet.go +++ b/test/e2e/annotations/streamsnippet.go @@ -33,6 +33,9 @@ import ( var _ = framework.DescribeSetting("stream-snippet", func() { f := framework.NewDefaultFramework("stream-snippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/annotations/upstreamhashby.go b/test/e2e/annotations/upstreamhashby.go index 1b81066627..43670c7174 100644 --- a/test/e2e/annotations/upstreamhashby.go +++ b/test/e2e/annotations/upstreamhashby.go @@ -36,7 +36,8 @@ func startIngress(f *framework.Framework, annotations map[string]string) map[str f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) //nolint:staticcheck // TODO: will replace it since wait.Poll is deprecated diff --git a/test/e2e/annotations/upstreamvhost.go b/test/e2e/annotations/upstreamvhost.go index e091e7c9fd..fb763ba1d5 100644 --- a/test/e2e/annotations/upstreamvhost.go +++ b/test/e2e/annotations/upstreamvhost.go @@ -42,7 +42,8 @@ var _ = framework.DescribeAnnotation("upstream-vhost", func() { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `proxy_set_header Host "upstreamvhost.bar.com";`) + return strings.Contains(server, `proxy_set_header Host "upstreamvhost.bar.com";`) || + strings.Contains(server, `proxy_set_header Host upstreamvhost.bar.com;`) }) }) }) diff --git a/test/e2e/annotations/xforwardedprefix.go b/test/e2e/annotations/xforwardedprefix.go index 35f2eb4262..3ca4497570 100644 --- a/test/e2e/annotations/xforwardedprefix.go +++ b/test/e2e/annotations/xforwardedprefix.go @@ -43,7 +43,8 @@ var _ = framework.DescribeAnnotation("x-forwarded-prefix", func() { f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, host) && - strings.Contains(server, "proxy_set_header X-Forwarded-Prefix \"/test/value\";") + (strings.Contains(server, "proxy_set_header X-Forwarded-Prefix \"/test/value\";") || + strings.Contains(server, "proxy_set_header X-Forwarded-Prefix /test/value;")) }) f.HTTPTestClient(). diff --git a/test/e2e/defaultbackend/custom_default_backend.go b/test/e2e/defaultbackend/custom_default_backend.go index 1e30b9269f..37b8c8ab26 100644 --- a/test/e2e/defaultbackend/custom_default_backend.go +++ b/test/e2e/defaultbackend/custom_default_backend.go @@ -47,7 +47,8 @@ var _ = framework.IngressNginxDescribe("[Default Backend] custom service", func( f.WaitForNginxServer("_", func(server string) bool { - return strings.Contains(server, `set $proxy_upstream_name "upstream-default-backend"`) + return strings.Contains(server, `set $proxy_upstream_name "upstream-default-backend"`) || + strings.Contains(server, `set $proxy_upstream_name upstream-default-backend`) }) f.HTTPTestClient(). diff --git a/test/e2e/framework/exec.go b/test/e2e/framework/exec.go index 8d528c37ae..ea38b8bef8 100644 --- a/test/e2e/framework/exec.go +++ b/test/e2e/framework/exec.go @@ -117,7 +117,12 @@ func (f *Framework) newIngressController(namespace, namespaceOverlay string) err isChroot = "false" } - cmd := exec.Command("./wait-for-nginx.sh", namespace, namespaceOverlay, isChroot) + isCrossplane, ok := os.LookupEnv("IS_CROSSPLANE") + if !ok { + isCrossplane = "false" + } + + cmd := exec.Command("./wait-for-nginx.sh", namespace, namespaceOverlay, isChroot, isCrossplane) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("unexpected error waiting for ingress controller deployment: %v.\nLogs:\n%v", err, string(out)) diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 204da7df05..b3eec60e76 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -20,6 +20,7 @@ import ( "fmt" "net" "net/http" + "os" "strings" "time" @@ -119,6 +120,11 @@ func NewSimpleFramework(baseName string, opts ...func(*Framework)) *Framework { return f } +func IsCrossplane() bool { + isCrossplane, ok := os.LookupEnv("IS_CROSSPLANE") + return ok && isCrossplane == "true" +} + func (f *Framework) CreateEnvironment() { var err error @@ -315,7 +321,7 @@ func (f *Framework) matchNginxConditions(name string, matcher func(cfg string) b if name == "" { cmd = "cat /etc/nginx/nginx.conf" } else { - cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %v/,/## end server %v/'", name, name) + cmd = fmt.Sprintf("cat /etc/nginx/nginx.conf | awk '/## start server %s/,/## end server %s/'", name, name) } o, err := f.ExecCommand(f.pod, cmd) diff --git a/test/e2e/framework/httpexpect/response.go b/test/e2e/framework/httpexpect/response.go index e324e94ffa..1c7624752c 100644 --- a/test/e2e/framework/httpexpect/response.go +++ b/test/e2e/framework/httpexpect/response.go @@ -234,7 +234,6 @@ func (r *HTTPResponse) checkContentType(expectedType string, expectedCharset ... } if mediaType != expectedType { - //nolint:goconst //string interpolation r.chain.fail("\nexpected \"Content-Type\" header with %q media type,"+ "\nbut got %q", expectedType, mediaType) return false diff --git a/test/e2e/ingress/multiple_rules.go b/test/e2e/ingress/multiple_rules.go index 9247dc1d3f..22ca08a96f 100644 --- a/test/e2e/ingress/multiple_rules.go +++ b/test/e2e/ingress/multiple_rules.go @@ -17,11 +17,11 @@ limitations under the License. package ingress import ( + "fmt" "net/http" "strings" "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -36,14 +36,19 @@ var _ = framework.IngressNginxDescribe("single ingress - multiple hosts", func() }) ginkgo.It("should set the correct $service_name NGINX variable", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() + customHeader := "Service-Name" + customHeaderValue := "$service_name" - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "service-name: $service_name";`, - } + h := make(map[string]string) + h[customHeader] = customHeaderValue - ing := framework.NewSingleIngress("simh", "/", "first.host", f.Namespace, "first-service", 80, annotations) + cfgMap := "custom-headers" + + f.CreateConfigMap(cfgMap, h) + + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%v/%v", f.Namespace, cfgMap)) + + ing := framework.NewSingleIngress("simh", "/", "first.host", f.Namespace, "first-service", 80, nil) ing.Spec.Rules = append(ing.Spec.Rules, networking.IngressRule{ Host: "second.host", @@ -79,26 +84,19 @@ var _ = framework.IngressNginxDescribe("single ingress - multiple hosts", func() return strings.Contains(server, "second.host") }) - body := f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact"). WithHeader("Host", "first.host"). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "service-name=first-service") - assert.NotContains(ginkgo.GinkgoT(), body, "service-name=second-service") + Headers().ValueEqual("Service-Name", []string{"first-service"}) - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact"). WithHeader("Host", "second.host"). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Service-Name", []string{"second-service"}) - assert.NotContains(ginkgo.GinkgoT(), body, "service-name=first-service") - assert.Contains(ginkgo.GinkgoT(), body, "service-name=second-service") }) }) diff --git a/test/e2e/ingress/pathtype_exact.go b/test/e2e/ingress/pathtype_exact.go index 2660e32a45..060ea9a318 100644 --- a/test/e2e/ingress/pathtype_exact.go +++ b/test/e2e/ingress/pathtype_exact.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -35,24 +34,31 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { }) ginkgo.It("should choose exact location for /exact", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() - host := "exact.path" + f.UpdateNginxConfigMapData("global-allowed-response-headers", "Pathtype,duplicated") annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-exact", } + f.CreateConfigMap("custom-headers-exact", map[string]string{ + "Pathtype": "exact", + }) + + host := "exact.path" exactPathType := networking.PathTypeExact ing := framework.NewSingleIngress("exact", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-prefix", } + f.CreateConfigMap("custom-headers-prefix", map[string]string{ + "Pathtype": "prefix", + }) + ing = framework.NewSingleIngress("exact-suffix", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -63,34 +69,29 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { strings.Contains(server, "location /exact/") }) - body := f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Pathtype", []string{"exact"}) - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") - - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact/suffix"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") + Headers().ValueEqual("Pathtype", []string{"prefix"}) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": ` - more_set_input_headers "pathType: prefix"; - more_set_input_headers "duplicated: true"; - `, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-duplicated", } + f.CreateConfigMap("custom-headers-duplicated", map[string]string{ + "Pathtype": "prefix", + "duplicated": "true", + }) + ing = framework.NewSingleIngress("duplicated-prefix", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -101,16 +102,12 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { strings.Contains(server, "location /exact/") }) - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/exact/suffix"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Pathtype", []string{"prefix"}).NotContainsKey("duplicated") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.NotContains(ginkgo.GinkgoT(), body, "duplicated=true") }) }) diff --git a/test/e2e/ingress/pathtype_mixed.go b/test/e2e/ingress/pathtype_mixed.go index 3212089c90..0573cc0f58 100644 --- a/test/e2e/ingress/pathtype_mixed.go +++ b/test/e2e/ingress/pathtype_mixed.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/onsi/ginkgo/v2" - "github.com/stretchr/testify/assert" networking "k8s.io/api/networking/v1" "k8s.io/ingress-nginx/test/e2e/framework" @@ -37,21 +36,32 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi exactPathType := networking.PathTypeExact ginkgo.It("should choose the correct location", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() - host := "mixed.path" + f.UpdateNginxConfigMapData("global-allowed-response-headers", "Pathtype,Pathheader") + annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";more_set_input_headers "pathheader: /";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-exact", } + + f.CreateConfigMap("custom-headers-exact", map[string]string{ + "Pathtype": "exact", + "Pathheader": "/", + }) + ing := framework.NewSingleIngress("exact-root", "/", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";more_set_input_headers "pathheader: /";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-prefix", } + + f.CreateConfigMap("custom-headers-prefix", map[string]string{ + "Pathtype": "prefix", + "Pathheader": "/", + }) + ing = framework.NewSingleIngress("prefix-root", "/", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -63,41 +73,42 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi }) ginkgo.By("Checking exact request to /") - body := f.HTTPTestClient(). + f.HTTPTestClient(). GET("/"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/") + Headers().ValueEqual("Pathtype", []string{"exact"}).ValueEqual("Pathheader", []string{"/"}) ginkgo.By("Checking prefix request to /bar") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/bar"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/") + Headers().ValueEqual("Pathtype", []string{"prefix"}).ValueEqual("Pathheader", []string{"/"}) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";more_set_input_headers "pathheader: /foo";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-ex-foo", } + + f.CreateConfigMap("custom-headers-ex-foo", map[string]string{ + "Pathtype": "exact", + "Pathheader": "/foo", + }) ing = framework.NewSingleIngress("exact-foo", "/foo", host, f.Namespace, framework.EchoService, 80, annotations) ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType f.EnsureIngress(ing) annotations = map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: prefix";more_set_input_headers "pathheader: /foo";`, + "nginx.ingress.kubernetes.io/custom-headers": f.Namespace + "/custom-headers-pr-foo", } + + f.CreateConfigMap("custom-headers-pr-foo", map[string]string{ + "Pathtype": "prefix", + "Pathheader": "/foo", + }) + ing = framework.NewSingleIngress("prefix-foo", "/foo", host, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) @@ -109,40 +120,28 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] mix Exact and Prefi }) ginkgo.By("Checking exact request to /foo") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/foo"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/foo") + Headers().ValueEqual("Pathtype", []string{"exact"}).ValueEqual("Pathheader", []string{"/foo"}) ginkgo.By("Checking prefix request to /foo/bar") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/foo/bar"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() - - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/foo") + Headers().ValueEqual("Pathtype", []string{"prefix"}).ValueEqual("Pathheader", []string{"/foo"}) ginkgo.By("Checking prefix request to /foobar") - body = f.HTTPTestClient(). + f.HTTPTestClient(). GET("/foobar"). WithHeader("Host", host). Expect(). Status(http.StatusOK). - Body(). - Raw() + Headers().ValueEqual("Pathtype", []string{"prefix"}).ValueEqual("Pathheader", []string{"/"}) - assert.Contains(ginkgo.GinkgoT(), body, "pathtype=prefix") - assert.Contains(ginkgo.GinkgoT(), body, "pathheader=/") }) }) diff --git a/test/e2e/ingress/without_host.go b/test/e2e/ingress/without_host.go index 38f89beda1..24192a0b7d 100644 --- a/test/e2e/ingress/without_host.go +++ b/test/e2e/ingress/without_host.go @@ -39,11 +39,17 @@ var _ = framework.IngressNginxDescribe("[Ingress] definition without host", func f.WaitForNginxServer("_", func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && + return (strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, ing.Name)) && strings.Contains(server, fmt.Sprintf(`set $service_name "%v";`, framework.EchoService)) && strings.Contains(server, `set $service_port "80";`) && - strings.Contains(server, `set $location_path "/";`) + strings.Contains(server, `set $location_path "/";`)) || + // Crossplane assertions + (strings.Contains(server, fmt.Sprintf(`set $namespace %s;`, f.Namespace)) && + strings.Contains(server, fmt.Sprintf(`set $ingress_name %s;`, ing.Name)) && + strings.Contains(server, fmt.Sprintf(`set $service_name %s;`, framework.EchoService)) && + strings.Contains(server, `set $service_port 80;`) && + strings.Contains(server, `set $location_path /;`)) }) f.HTTPTestClient(). @@ -81,11 +87,17 @@ var _ = framework.IngressNginxDescribe("[Ingress] definition without host", func f.WaitForNginxServer("only-backend", func(server string) bool { - return strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && + return (strings.Contains(server, fmt.Sprintf(`set $namespace "%v";`, f.Namespace)) && strings.Contains(server, fmt.Sprintf(`set $ingress_name "%v";`, ing.Name)) && strings.Contains(server, fmt.Sprintf(`set $service_name "%v";`, framework.EchoService)) && strings.Contains(server, `set $service_port "80";`) && - strings.Contains(server, `set $location_path "/";`) + strings.Contains(server, `set $location_path "/";`)) || + // Crossplane assertions + (strings.Contains(server, fmt.Sprintf(`set $namespace %s;`, f.Namespace)) && + strings.Contains(server, fmt.Sprintf(`set $ingress_name %s;`, ing.Name)) && + strings.Contains(server, fmt.Sprintf(`set $service_name %s;`, framework.EchoService)) && + strings.Contains(server, `set $service_port 80;`) && + strings.Contains(server, `set $location_path /;`)) }) f.HTTPTestClient(). diff --git a/test/e2e/lua/dynamic_configuration.go b/test/e2e/lua/dynamic_configuration.go index a5e2196cef..a63a7a37d9 100644 --- a/test/e2e/lua/dynamic_configuration.go +++ b/test/e2e/lua/dynamic_configuration.go @@ -212,7 +212,8 @@ func createIngress(f *framework.Framework, host, deploymentName string) { f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && + return (strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host))) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) } diff --git a/test/e2e/metrics/metrics.go b/test/e2e/metrics/metrics.go index bec09bb37c..2100eefd2a 100644 --- a/test/e2e/metrics/metrics.go +++ b/test/e2e/metrics/metrics.go @@ -43,7 +43,8 @@ var _ = framework.IngressNginxDescribe("[metrics] exported prometheus metrics", f.EnsureIngress(framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) && + return (strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host))) && strings.Contains(server, "proxy_pass http://upstream_balancer;") }) }) diff --git a/test/e2e/run-e2e-suite.sh b/test/e2e/run-e2e-suite.sh index 909368e96b..621313610a 100755 --- a/test/e2e/run-e2e-suite.sh +++ b/test/e2e/run-e2e-suite.sh @@ -78,6 +78,7 @@ kubectl run --rm \ --env="E2E_NODES=${E2E_NODES}" \ --env="FOCUS=${FOCUS}" \ --env="IS_CHROOT=${IS_CHROOT:-false}"\ + --env="IS_CROSSPLANE=${IS_CROSSPLANE:-false}"\ --env="SKIP_OPENTELEMETRY_TESTS=${SKIP_OPENTELEMETRY_TESTS:-false}"\ --env="E2E_CHECK_LEAKS=${E2E_CHECK_LEAKS}" \ --env="NGINX_BASE_IMAGE=${NGINX_BASE_IMAGE}" \ diff --git a/test/e2e/run-kind-e2e.sh b/test/e2e/run-kind-e2e.sh index fed9ff256a..cf0925a076 100755 --- a/test/e2e/run-kind-e2e.sh +++ b/test/e2e/run-kind-e2e.sh @@ -39,6 +39,8 @@ fi KIND_LOG_LEVEL="1" IS_CHROOT="${IS_CHROOT:-false}" +IS_CROSSPLANE="${IS_CROSSPLANE:-false}" + export KIND_CLUSTER_NAME=${KIND_CLUSTER_NAME:-ingress-nginx-dev} DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Use 1.0.0-dev to make sure we use the latest configuration in the helm template diff --git a/test/e2e/security/request_smuggling.go b/test/e2e/security/request_smuggling.go index 5ede02d4b5..d96ba4b4c0 100644 --- a/test/e2e/security/request_smuggling.go +++ b/test/e2e/security/request_smuggling.go @@ -37,6 +37,9 @@ var _ = framework.IngressNginxDescribe("[Security] request smuggling", func() { }) ginkgo.It("should not return body content from error_page", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") // TODO: Re-add this test when we enable admin defined snippets + } host := "foo.bar.com" snippet := ` diff --git a/test/e2e/settings/access_log.go b/test/e2e/settings/access_log.go index 65b9dfda4a..33466f29f5 100644 --- a/test/e2e/settings/access_log.go +++ b/test/e2e/settings/access_log.go @@ -31,6 +31,10 @@ var _ = framework.DescribeSetting("access-log", func() { ginkgo.It("use the default configuration", func() { f.WaitForNginxConfiguration( func(cfg string) bool { + if framework.IsCrossplane() { + return strings.Contains(cfg, "access_log /var/log/nginx/access.log upstreaminfo") || + strings.Contains(cfg, "access_log syslog:server=127.0.0.1:11514 upstreaminfo") + } return (strings.Contains(cfg, "access_log /var/log/nginx/access.log upstreaminfo") && strings.Contains(cfg, "access_log /var/log/nginx/access.log log_stream")) || (strings.Contains(cfg, "access_log syslog:server=127.0.0.1:11514 upstreaminfo") && @@ -42,6 +46,9 @@ var _ = framework.DescribeSetting("access-log", func() { f.UpdateNginxConfigMapData("access-log-path", "/tmp/nginx/access.log") f.WaitForNginxConfiguration( func(cfg string) bool { + if framework.IsCrossplane() { + return strings.Contains(cfg, "access_log /tmp/nginx/access.log upstreaminfo") + } return strings.Contains(cfg, "access_log /tmp/nginx/access.log upstreaminfo") && strings.Contains(cfg, "access_log /tmp/nginx/access.log log_stream") }) @@ -53,6 +60,9 @@ var _ = framework.DescribeSetting("access-log", func() { f.UpdateNginxConfigMapData("http-access-log-path", "/tmp/nginx/http-access.log") f.WaitForNginxConfiguration( func(cfg string) bool { + if framework.IsCrossplane() { + return strings.Contains(cfg, "access_log /tmp/nginx/http-access.log upstreaminfo") + } return strings.Contains(cfg, "access_log /tmp/nginx/http-access.log upstreaminfo") && (strings.Contains(cfg, "access_log /var/log/nginx/access.log log_stream") || strings.Contains(cfg, "access_log syslog:server=127.0.0.1:11514 log_stream")) @@ -62,6 +72,9 @@ var _ = framework.DescribeSetting("access-log", func() { ginkgo.Context("stream-access-log-path", func() { ginkgo.It("use the specified configuration", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support stream") + } f.UpdateNginxConfigMapData("stream-access-log-path", "/tmp/nginx/stream-access.log") f.WaitForNginxConfiguration( func(cfg string) bool { @@ -74,6 +87,9 @@ var _ = framework.DescribeSetting("access-log", func() { ginkgo.Context("http-access-log-path & stream-access-log-path", func() { ginkgo.It("use the specified configuration", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support stream") + } f.SetNginxConfigMapData(map[string]string{ "http-access-log-path": "/tmp/nginx/http-access.log", "stream-access-log-path": "/tmp/nginx/stream-access.log", diff --git a/test/e2e/settings/badannotationvalues.go b/test/e2e/settings/badannotationvalues.go index aa9906909c..4459a8da8b 100644 --- a/test/e2e/settings/badannotationvalues.go +++ b/test/e2e/settings/badannotationvalues.go @@ -50,7 +50,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.WaitForNginxServer(host, @@ -87,7 +88,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.WaitForNginxServer(host, @@ -103,6 +105,9 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { }) ginkgo.It("[BAD_ANNOTATIONS] should allow an ingress if there is a default blocklist config in place", func() { + if framework.IsCrossplane() { + ginkgo.Skip("Crossplane does not support snippets") + } disableSnippet := f.AllowSnippetConfiguration() defer disableSnippet() @@ -120,7 +125,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(hostValid, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", hostValid)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", hostValid)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", hostValid)) }) f.WaitForNginxServer(hostValid, @@ -153,7 +159,8 @@ var _ = framework.DescribeAnnotation("Bad annotation values", func() { f.WaitForNginxServer(host, func(server string) bool { - return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return !strings.Contains(server, fmt.Sprintf("server_name %s;", host)) && + !strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) f.WaitForNginxServer(host, diff --git a/test/e2e/settings/configmap_change.go b/test/e2e/settings/configmap_change.go index 3e37b62cd6..3b18780519 100644 --- a/test/e2e/settings/configmap_change.go +++ b/test/e2e/settings/configmap_change.go @@ -17,7 +17,6 @@ limitations under the License. package settings import ( - "regexp" "strings" "github.com/onsi/ginkgo/v2" @@ -43,36 +42,20 @@ var _ = framework.DescribeSetting("Configmap change", func() { f.UpdateNginxConfigMapData("whitelist-source-range", "1.1.1.1") - checksumRegex := regexp.MustCompile(`Configuration checksum:\s+(\d+)`) - checksum := "" - f.WaitForNginxConfiguration( func(cfg string) bool { - // before returning, extract the current checksum - match := checksumRegex.FindStringSubmatch(cfg) - if len(match) > 0 { - checksum = match[1] - } - return strings.Contains(cfg, "allow 1.1.1.1;") }) - assert.NotEmpty(ginkgo.GinkgoT(), checksum) ginkgo.By("changing error-log-level") f.UpdateNginxConfigMapData("error-log-level", "debug") - newChecksum := "" f.WaitForNginxConfiguration( func(cfg string) bool { - match := checksumRegex.FindStringSubmatch(cfg) - if len(match) > 0 { - newChecksum = match[1] - } - - return strings.ContainsAny(cfg, "error_log /var/log/nginx/error.log debug;") + return strings.ContainsAny(cfg, "error_log /var/log/nginx/error.log debug;") || + strings.ContainsAny(cfg, "error_log /var/log/nginx/error.log debug;") }) - assert.NotEqual(ginkgo.GinkgoT(), checksum, newChecksum) logs, err := f.NginxLogs() assert.Nil(ginkgo.GinkgoT(), err, "obtaining nginx logs") diff --git a/test/e2e/settings/custom_header.go b/test/e2e/settings/custom_header.go index 8341f7ef02..b2c8aac8d6 100644 --- a/test/e2e/settings/custom_header.go +++ b/test/e2e/settings/custom_header.go @@ -79,8 +79,10 @@ var _ = framework.DescribeSetting("add-headers", func() { f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%v/%v", f.Namespace, cfgMap)) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, fmt.Sprintf("more_set_headers \"%s: %s\";", firstCustomHeader, firstCustomHeaderValue)) && - strings.Contains(server, fmt.Sprintf("more_set_headers \"%s: %s\";", secondCustomHeader, secondCustomHeaderValue)) + return (strings.Contains(server, fmt.Sprintf(`more_set_headers "%s: %s";`, firstCustomHeader, firstCustomHeaderValue)) && + strings.Contains(server, fmt.Sprintf(`more_set_headers "%s: %s";`, secondCustomHeader, secondCustomHeaderValue))) || + (strings.Contains(server, fmt.Sprintf("more_set_headers %s: %s;", firstCustomHeader, firstCustomHeaderValue)) && + strings.Contains(server, fmt.Sprintf("more_set_headers %s: %s;", secondCustomHeader, secondCustomHeaderValue))) }) resp := f.HTTPTestClient(). diff --git a/test/e2e/settings/default_ssl_certificate.go b/test/e2e/settings/default_ssl_certificate.go index c48a1e87f7..e8e2195199 100644 --- a/test/e2e/settings/default_ssl_certificate.go +++ b/test/e2e/settings/default_ssl_certificate.go @@ -69,8 +69,10 @@ var _ = framework.IngressNginxDescribe("[SSL] [Flag] default-ssl-certificate", f ginkgo.By("making sure new ingress is deployed") expectedConfig := fmt.Sprintf(`set $proxy_upstream_name "%v-%v-%v";`, f.Namespace, service, port) + expectedConfigCrossPlane := fmt.Sprintf(`set $proxy_upstream_name %v-%v-%v;`, f.Namespace, service, port) + f.WaitForNginxServer("_", func(cfg string) bool { - return strings.Contains(cfg, expectedConfig) + return strings.Contains(cfg, expectedConfig) || strings.Contains(cfg, expectedConfigCrossPlane) }) ginkgo.By("making sure new ingress is responding") @@ -91,8 +93,9 @@ var _ = framework.IngressNginxDescribe("[SSL] [Flag] default-ssl-certificate", f ginkgo.By("making sure new ingress is deployed") expectedConfig := fmt.Sprintf(`set $proxy_upstream_name "%v-%v-%v";`, f.Namespace, service, port) + expectedConfigCrossPlane := fmt.Sprintf(`set $proxy_upstream_name %v-%v-%v;`, f.Namespace, service, port) f.WaitForNginxServer(host, func(cfg string) bool { - return strings.Contains(cfg, expectedConfig) + return strings.Contains(cfg, expectedConfig) || strings.Contains(cfg, expectedConfigCrossPlane) }) ginkgo.By("making sure the configured default ssl certificate is being used") diff --git a/test/e2e/settings/disable_catch_all.go b/test/e2e/settings/disable_catch_all.go index 4e7a16f4de..54fbe9d8bd 100644 --- a/test/e2e/settings/disable_catch_all.go +++ b/test/e2e/settings/disable_catch_all.go @@ -62,7 +62,8 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { f.WaitForNginxServer("_", func(cfg string) bool { return strings.Contains(cfg, `set $ingress_name ""`) && - strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) + (strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) || + strings.Contains(cfg, `set $proxy_upstream_name upstream-default-backend`)) }) }) @@ -74,7 +75,8 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-catch-all", func() { f.WaitForNginxServer("_", func(cfg string) bool { return strings.Contains(cfg, `set $ingress_name ""`) && - strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) + (strings.Contains(cfg, `set $proxy_upstream_name "upstream-default-backend"`) || + strings.Contains(cfg, `set $proxy_upstream_name upstream-default-backend`)) }) }) diff --git a/test/e2e/settings/disable_sync_events.go b/test/e2e/settings/disable_sync_events.go index 0d55c96e48..033fd9194a 100644 --- a/test/e2e/settings/disable_sync_events.go +++ b/test/e2e/settings/disable_sync_events.go @@ -44,7 +44,6 @@ var _ = framework.IngressNginxDescribe("[Flag] disable-sync-events", func() { return strings.Contains(server, fmt.Sprintf("server_name %v", host)) }) - //nolint:goconst //string interpolation events, err := f.KubeClientSet.CoreV1().Events(ing.Namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: "reason=Sync,involvedObject.name=" + host}) assert.Nil(ginkgo.GinkgoT(), err, "listing events") diff --git a/test/e2e/settings/enable_real_ip.go b/test/e2e/settings/enable_real_ip.go index bf16e1ea04..778011b9fa 100644 --- a/test/e2e/settings/enable_real_ip.go +++ b/test/e2e/settings/enable_real_ip.go @@ -47,7 +47,6 @@ var _ = framework.DescribeSetting("enable-real-ip", func() { f.WaitForNginxServer(host, func(server string) bool { - //nolint:goconst //already a const return strings.Contains(server, "server_name "+host) && !strings.Contains(server, "proxy_set_header X-Forwarded-Proto $full_x_forwarded_proto;") }) diff --git a/test/e2e/settings/global_external_auth.go b/test/e2e/settings/global_external_auth.go index f589a63e94..1721c54a57 100644 --- a/test/e2e/settings/global_external_auth.go +++ b/test/e2e/settings/global_external_auth.go @@ -246,6 +246,9 @@ var _ = framework.DescribeSetting("[Security] global-auth-url", func() { }) ginkgo.It(`should set snippet when global external auth is configured`, func() { + if framework.IsCrossplane() { + ginkgo.Skip("crossplane does not support snippets") + } globalExternalAuthSnippetSetting := "global-auth-snippet" globalExternalAuthSnippet := "proxy_set_header My-Custom-Header 42;" diff --git a/test/e2e/settings/gzip.go b/test/e2e/settings/gzip.go index c7e580e079..b2972e69c3 100644 --- a/test/e2e/settings/gzip.go +++ b/test/e2e/settings/gzip.go @@ -106,7 +106,7 @@ var _ = framework.DescribeSetting("gzip", func() { f.WaitForNginxConfiguration( func(cfg string) bool { return strings.Contains(cfg, "gzip on;") && - strings.Contains(cfg, `gzip_disable "msie6";`) + (strings.Contains(cfg, `gzip_disable "msie6";`) || strings.Contains(cfg, `gzip_disable msie6;`)) }, ) diff --git a/test/e2e/settings/limit_rate.go b/test/e2e/settings/limit_rate.go index 9d79dc3582..41c54da852 100644 --- a/test/e2e/settings/limit_rate.go +++ b/test/e2e/settings/limit_rate.go @@ -41,7 +41,8 @@ var _ = framework.DescribeSetting("Configmap - limit-rate", func() { f.EnsureIngress(ing) f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) + return strings.Contains(server, fmt.Sprintf("server_name %s;", host)) || + strings.Contains(server, fmt.Sprintf("server_name %s ;", host)) }) wlKey := "limit-rate" diff --git a/test/e2e/settings/main_snippet.go b/test/e2e/settings/main_snippet.go index ff14bab9d2..50bf96caed 100644 --- a/test/e2e/settings/main_snippet.go +++ b/test/e2e/settings/main_snippet.go @@ -26,6 +26,9 @@ import ( var _ = framework.DescribeSetting("main-snippet", func() { f := framework.NewDefaultFramework("main-snippet") + if framework.IsCrossplane() { + return + } mainSnippet := "main-snippet" ginkgo.It("should add value of main-snippet setting to nginx config", func() { diff --git a/test/e2e/settings/modsecurity/modsecurity_snippet.go b/test/e2e/settings/modsecurity/modsecurity_snippet.go index 2dd92ced26..5e82ccf8e7 100644 --- a/test/e2e/settings/modsecurity/modsecurity_snippet.go +++ b/test/e2e/settings/modsecurity/modsecurity_snippet.go @@ -26,6 +26,9 @@ import ( var _ = framework.DescribeSetting("[Security] modsecurity-snippet", func() { f := framework.NewDefaultFramework("modsecurity-snippet") + if framework.IsCrossplane() { + return + } ginkgo.It("should add value of modsecurity-snippet setting to nginx config", func() { expectedComment := "# modsecurity snippet" diff --git a/test/e2e/settings/no_tls_redirect_locations.go b/test/e2e/settings/no_tls_redirect_locations.go index 18fd09e267..b9ac1bb971 100644 --- a/test/e2e/settings/no_tls_redirect_locations.go +++ b/test/e2e/settings/no_tls_redirect_locations.go @@ -33,7 +33,7 @@ var _ = framework.DescribeSetting("Add no tls redirect locations", func() { f.EnsureIngress(ing) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, "set $force_no_ssl_redirect \"false\"") + return strings.Contains(server, "set $force_no_ssl_redirect \"false\"") || strings.Contains(server, "set $force_no_ssl_redirect false") }) wlKey := "no-tls-redirect-locations" @@ -42,7 +42,7 @@ var _ = framework.DescribeSetting("Add no tls redirect locations", func() { f.UpdateNginxConfigMapData(wlKey, wlValue) f.WaitForNginxConfiguration(func(server string) bool { - return strings.Contains(server, "set $force_no_ssl_redirect \"true\"") + return strings.Contains(server, "set $force_no_ssl_redirect \"true\"") || strings.Contains(server, "set $force_no_ssl_redirect true") }) }) }) diff --git a/test/e2e/settings/proxy_host.go b/test/e2e/settings/proxy_host.go index bb5dc9c012..afb37c372c 100644 --- a/test/e2e/settings/proxy_host.go +++ b/test/e2e/settings/proxy_host.go @@ -34,14 +34,15 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() + h := make(map[string]string) + h["Custom-Header"] = "$proxy_host" + cfgMap := "add-headers-configmap" + + f.CreateConfigMap(cfgMap, h) + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%s/%s", f.Namespace, cfgMap)) upstreamName := fmt.Sprintf("%v-%v-80", f.Namespace, framework.EchoService) - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Custom-Header: $proxy_host"`, - } - f.EnsureIngress(framework.NewSingleIngress(test, "/", test, f.Namespace, framework.EchoService, 80, annotations)) + f.EnsureIngress(framework.NewSingleIngress(test, "/", test, f.Namespace, framework.EchoService, 80, nil)) f.WaitForNginxConfiguration( func(server string) bool { @@ -58,15 +59,19 @@ var _ = framework.IngressNginxDescribe("Dynamic $proxy_host", func() { }) ginkgo.It("should exist a proxy_host using the upstream-vhost annotation value", func() { - disableSnippet := f.AllowSnippetConfiguration() - defer disableSnippet() + h := make(map[string]string) + h["Custom-Header"] = "$proxy_host" + cfgMap := "add-headers-configmap" + + f.CreateConfigMap(cfgMap, h) + f.UpdateNginxConfigMapData("add-headers", fmt.Sprintf("%s/%s", f.Namespace, cfgMap)) upstreamName := fmt.Sprintf("%v-%v-80", f.Namespace, framework.EchoService) upstreamVHost := "different.host" annotations := map[string]string{ - "nginx.ingress.kubernetes.io/upstream-vhost": upstreamVHost, - "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_headers "Custom-Header: $proxy_host"`, + "nginx.ingress.kubernetes.io/upstream-vhost": upstreamVHost, } + f.EnsureIngress(framework.NewSingleIngress(test, "/", test, f.Namespace, framework.EchoService, 80, annotations)) f.WaitForNginxConfiguration( diff --git a/test/e2e/settings/proxy_protocol.go b/test/e2e/settings/proxy_protocol.go index cfce68bf8a..4c2e2a1aaf 100644 --- a/test/e2e/settings/proxy_protocol.go +++ b/test/e2e/settings/proxy_protocol.go @@ -162,6 +162,9 @@ var _ = framework.DescribeSetting("use-proxy-protocol", func() { }) ginkgo.It("should enable PROXY Protocol for TCP", func() { + if framework.IsCrossplane() { + return + } cmapData := map[string]string{} cmapData[setting] = "true" cmapData["enable-real-ip"] = "true" diff --git a/test/e2e/settings/server_snippet.go b/test/e2e/settings/server_snippet.go index 1e2084bd84..e162630db7 100644 --- a/test/e2e/settings/server_snippet.go +++ b/test/e2e/settings/server_snippet.go @@ -28,6 +28,9 @@ import ( var _ = framework.DescribeSetting("configmap server-snippet", func() { f := framework.NewDefaultFramework("cm-server-snippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() }) diff --git a/test/e2e/settings/ssl_ciphers.go b/test/e2e/settings/ssl_ciphers.go index 241dfc92b2..f80bf41a27 100644 --- a/test/e2e/settings/ssl_ciphers.go +++ b/test/e2e/settings/ssl_ciphers.go @@ -35,7 +35,7 @@ var _ = framework.DescribeSetting("ssl-ciphers", func() { f.UpdateNginxConfigMapData(wlKey, wlValue) f.WaitForNginxConfiguration(func(cfg string) bool { - return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", wlValue)) + return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", wlValue)) || strings.Contains(cfg, fmt.Sprintf("ssl_ciphers %s;", wlValue)) }) }) }) diff --git a/test/e2e/settings/ssl_passthrough.go b/test/e2e/settings/ssl_passthrough.go index b10511bde4..111c0c776d 100644 --- a/test/e2e/settings/ssl_passthrough.go +++ b/test/e2e/settings/ssl_passthrough.go @@ -35,6 +35,9 @@ import ( var _ = framework.IngressNginxDescribe("[Flag] enable-ssl-passthrough", func() { f := framework.NewDefaultFramework("ssl-passthrough", framework.WithHTTPBunEnabled()) + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { err := f.UpdateIngressControllerDeployment(func(deployment *appsv1.Deployment) error { diff --git a/test/e2e/settings/stream_snippet.go b/test/e2e/settings/stream_snippet.go index e1552afd36..4d1757a086 100644 --- a/test/e2e/settings/stream_snippet.go +++ b/test/e2e/settings/stream_snippet.go @@ -34,6 +34,9 @@ import ( var _ = framework.DescribeSetting("configmap stream-snippet", func() { f := framework.NewDefaultFramework("cm-stream-snippet") + if framework.IsCrossplane() { + return + } ginkgo.BeforeEach(func() { f.NewEchoDeployment() diff --git a/test/e2e/settings/tls.go b/test/e2e/settings/tls.go index 1ebf358c18..2191116642 100644 --- a/test/e2e/settings/tls.go +++ b/test/e2e/settings/tls.go @@ -71,7 +71,7 @@ var _ = framework.DescribeSetting("[SSL] TLS protocols, ciphers and headers", fu f.WaitForNginxConfiguration( func(cfg string) bool { - return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", testCiphers)) + return strings.Contains(cfg, fmt.Sprintf("ssl_ciphers '%s';", testCiphers)) || strings.Contains(cfg, fmt.Sprintf("ssl_ciphers %s;", testCiphers)) }) resp := f.HTTPTestClientWithTLSConfig(tlsConfig). diff --git a/test/e2e/tcpudp/tcp.go b/test/e2e/tcpudp/tcp.go index 856184d18b..de45b00351 100644 --- a/test/e2e/tcpudp/tcp.go +++ b/test/e2e/tcpudp/tcp.go @@ -37,6 +37,9 @@ import ( var _ = framework.IngressNginxDescribe("[TCP] tcp-services", func() { f := framework.NewDefaultFramework("tcp") + if framework.IsCrossplane() { + return + } var ip string ginkgo.BeforeEach(func() { diff --git a/test/e2e/wait-for-nginx.sh b/test/e2e/wait-for-nginx.sh index 73023aba15..8eb4368d0c 100755 --- a/test/e2e/wait-for-nginx.sh +++ b/test/e2e/wait-for-nginx.sh @@ -24,6 +24,12 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export NAMESPACE=$1 export NAMESPACE_OVERLAY=$2 export IS_CHROOT=$3 +export IS_CROSSPLANE=$4 + +TPL_ENGINE="go-template" +if [ "$IS_CROSSPLANE" == "true" ]; then + TPL_ENGINE="crossplane" +fi echo "deploying NGINX Ingress controller in namespace $NAMESPACE" @@ -52,12 +58,14 @@ if [[ ! -z "$NAMESPACE_OVERLAY" && -d "$DIR/namespace-overlays/$NAMESPACE_OVERLA echo "Namespace overlay $NAMESPACE_OVERLAY is being used for namespace $NAMESPACE" helm install nginx-ingress ${DIR}/charts/ingress-nginx \ --namespace=$NAMESPACE \ - --values "$DIR/namespace-overlays/$NAMESPACE_OVERLAY/values.yaml" + --values "$DIR/namespace-overlays/$NAMESPACE_OVERLAY/values.yaml" \ + --set controller.templateEngine=${TPL_ENGINE} else cat << EOF | helm install nginx-ingress ${DIR}/charts/ingress-nginx --namespace=$NAMESPACE --values - # TODO: remove the need to use fullnameOverride fullnameOverride: nginx-ingress controller: + templateEngine: ${TPL_ENGINE} image: repository: ingress-controller/controller chroot: ${IS_CHROOT}