From 1a1d1eb186e495b7e67f9b10d278b11652d44f89 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 18 Feb 2022 12:01:41 +0100 Subject: [PATCH 0001/1047] Use testcontainers to run `redis-server` (#1960) Mitigates build failures caused by package manager redirects during `redis-server` installation. Note that change adds many dependencies but they are only used for tests and do not end up in the build. Signed-off-by: Alexander Yastrebov --- .github/workflows/master.yaml | 1 - .github/workflows/pr.yaml | 2 - delivery.yaml | 2 - go.mod | 29 ++- go.sum | 340 ++++++++++++++++++++++++++++++++++ net/redistest/redistest.go | 60 +++--- 6 files changed, 402 insertions(+), 32 deletions(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index c345846fd5..42cc7b1be6 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,7 +13,6 @@ jobs: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.17' - run: go version - - run: sudo apt-get install redis-server - run: make deps - run: make check-fmt - run: make build diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4f27d785f7..e450d47126 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -10,7 +10,6 @@ jobs: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.17' - run: go version - - run: sudo apt-get install redis-server - run: make deps - run: make check-race tests: @@ -22,7 +21,6 @@ jobs: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.17' - run: go version - - run: sudo apt-get install redis-server - run: make deps - run: make check-fmt - run: make vet diff --git a/delivery.yaml b/delivery.yaml index 2f80b7b466..0fdfadecb5 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -8,8 +8,6 @@ pipeline: commands: - desc: build-push cmd: | - apt-get update -o Acquire::http::AllowRedirect=false - apt-get install -o Acquire::http::AllowRedirect=false -y bzr redis-server jq if [[ $CDP_TARGET_BRANCH == master && ! $CDP_PULL_REQUEST_NUMBER ]]; then LATEST_VERSION=$(git describe --tags --always | awk -F \- '{print $1}') CUR_PART=$(echo $LATEST_VERSION | awk -F . '{print $1"."$2}') diff --git a/go.mod b/go.mod index 677fe0714d..0d64b6a2ac 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect + golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d golang.org/x/tools v0.1.8 // indirect google.golang.org/grpc v1.43.0 // indirect @@ -65,18 +65,29 @@ require ( require ( cloud.google.com/go v0.65.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 // indirect + github.com/Microsoft/hcsshim v0.8.16 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 // indirect + github.com/containerd/containerd v1.5.0-beta.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker v20.10.11+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.0 // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-immutable-radix v1.0.0 // indirect github.com/hashicorp/go-msgpack v0.5.3 // indirect @@ -85,24 +96,34 @@ require ( github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/json-iterator/go v1.1.11 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/magiconair/properties v1.8.5 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/moby/sys/mount v0.2.0 // indirect + github.com/moby/sys/mountinfo v0.5.0 // indirect + github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/runc v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/shirou/gopsutil/v3 v3.21.2 // indirect + github.com/testcontainers/testcontainers-go v0.12.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/numcpus v0.3.0 // indirect + go.opencensus.io v0.22.4 // indirect golang.org/x/mod v0.5.1 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect - google.golang.org/protobuf v1.26.0 // indirect + google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect + google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/klog/v2 v2.8.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect diff --git a/go.sum b/go.sum index 1af4cc9c03..0b50c3028c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -32,16 +33,22 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= @@ -49,10 +56,28 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Flaque/filet v0.0.0-20201012163910-45f684403088/go.mod h1:TK+jB3mBs+8ZMWhU5BqZKnZWJ1MrLo8etNVg51ueTBo= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/MicahParks/keyfunc v1.0.1 h1:BIKfiyELXV0A8SRCHQaByJW0s71xXpArhzfTS3uf26c= github.com/MicahParks/keyfunc v1.0.1/go.mod h1:R8RZa27qn+5cHTfYLJ9/+7aSb5JIdz7cl0XFo0o4muo= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 h1:mw6pDQqv38/WGF1cO/jF5t/jyAJ2yi7CmtFLLO5tGFI= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -60,6 +85,7 @@ github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= @@ -73,6 +99,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -84,12 +111,24 @@ github.com/aryszka/jobqueue v0.0.2 h1:LYPhzklo0XFpVF+QtzfP9XRQPEsbJ2EW5Pur6pxxaS github.com/aryszka/jobqueue v0.0.2/go.mod h1:SdxqI6HZ4E1Lss94tey5OfjcAu3bdCDWS1AQzzIN4m4= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -98,9 +137,15 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 h1:rdWOzitWlNYeUsXmz+IQfa9NkGEq3gA/qQ3mOEqBU6o= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9/go.mod h1:X97UjDTXp+7bayQSFZk2hPvCTmTZIicUjZQRtkwgAKY= github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 h1:cJ1E8ZwZLfercTX3dywnCAQDilbbi+m2cw3+8tCFpRo= @@ -113,29 +158,109 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 h1:hkGVFjz+plgr5UfxZUTPFbUFIF/Km6/s+RVRIRHLrrY= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4 h1:zjz4MOAOFgdBlwid2nNUlJ3YLpVi/97L36lfMYJex60= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= 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.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dchest/siphash v1.2.2 h1:9DFz8tQwl9pTVt5iok/9zKyzA1Q6bRGiF3HPiEEVr9I= github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 h1:0wH6nO9QEa02Qx8sIQGw6ieKdz+BXjpccSOo9vXNl4U= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA= @@ -146,9 +271,24 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +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/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -167,9 +307,12 @@ github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -178,6 +321,7 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -234,14 +378,25 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-restit/lzjson v0.0.0-20161206095556-efe3c53acc68/go.mod h1:7vXSKQt83WmbPeyVjCfNT9YDJ5BUFmcwFsEjI9SCvYM= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -254,6 +409,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -290,6 +446,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/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.4/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.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= @@ -310,6 +467,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -318,6 +477,8 @@ github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3i github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -328,12 +489,14 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= @@ -349,11 +512,17 @@ github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/instana/go-sensor v1.38.3 h1:/PdHEDveLmUCvK+O6REvSv8kKljX8vaj9JMjMeCHAWk= github.com/instana/go-sensor v1.38.3/go.mod h1:E42MelHWFz11qqaLwvgt0j98v2s2O/bq22UDkGaG0Gg= github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65 h1:T25FL3WEzgmKB0m6XCJNZ65nw09/QIp3T1yXr487D+A= github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65/go.mod h1:nYhEREG/B7HUY7P+LKOrqy53TpIqmJ9JyUShcaEKtGw= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -372,11 +541,14 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -390,36 +562,59 @@ github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/u github.com/looplab/fsm v0.3.0 h1:kIgNS3Yyud1tyxhG8kDqh853B7QqwnlWdgL3TD2s3Sw= github.com/looplab/fsm v0.3.0/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.45 h1:g5fRIhm9nx7g8osrAvgb16QJfmyMsyOCb+J7LSv+Qzk= github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= +github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= +github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -428,13 +623,17 @@ github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -443,6 +642,32 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0= github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -455,6 +680,7 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -464,27 +690,38 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= 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/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= @@ -495,23 +732,31 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sanity-io/litter v1.5.2 h1:AnC8s9BMORWH5a4atZ4D6FPVvKGzHcnc5/IVTa87myw= github.com/sanity-io/litter v1.5.2/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= github.com/sarslanhan/cronmask v0.0.0-20190709075623-766eca24d011 h1:S5j3KTsiGwmQSEJJBp0iIG87CDBCGCwbYLmVv8L/nuE= github.com/sarslanhan/cronmask v0.0.0-20190709075623-766eca24d011/go.mod h1:NmI1tg7wwsf1hF6G5EtyGCrtNKsH2RIdYYoJa7GsnP8= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.21.2 h1:fIOk3hyqV1oGKogfGNjUZa0lUbtlkx3+ZT0IoJth2uM= github.com/shirou/gopsutil/v3 v3.21.2/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= @@ -520,22 +765,27 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -543,10 +793,16 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/szuecs/rate-limit-buffer v0.7.1 h1:kpVLwDvpCTFQi8uhiXQrhAKWzNUaEKhArFdjb4GQ8F4= github.com/szuecs/rate-limit-buffer v0.7.1/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/szuecs/routegroup-client v0.21.0 h1:XWLxK2OCs4lZeIIHOJVVaFpe7nFmJjM3r+fOsZ2eyWw= github.com/szuecs/routegroup-client v0.21.0/go.mod h1:GbR5pqdcJdiFx9aL2hoq1ghAMZ9E1qFKBf88w7vqyK0= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/testcontainers/testcontainers-go v0.12.0 h1:SK0NryGHIx7aifF6YqReORL18aGAA4bsDPtikDVCEyg= +github.com/testcontainers/testcontainers-go v0.12.0/go.mod h1:SIndOQXZng0IW8iWU1Js0ynrfZ8xcxrTtDfF6rD2pxs= github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -568,8 +824,20 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yookoala/gofast v0.6.0 h1:E5x2acfUD7GkzCf8bmIMwnV10VxDy5tUCHc5LGhluwc= @@ -582,16 +850,23 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -600,7 +875,9 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -609,10 +886,12 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= @@ -660,6 +939,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -672,6 +952,7 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -696,11 +977,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -740,24 +1024,35 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -769,14 +1064,25 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -787,6 +1093,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 h1:T6tyxxvHMj2L1R2kZg0uNMpS8ZhB9lRa9XRGTCSA65w= +golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= @@ -805,6 +1114,7 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -826,6 +1136,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -876,6 +1187,7 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -899,11 +1211,13 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -913,6 +1227,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -931,12 +1246,16 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -946,6 +1265,7 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= @@ -962,9 +1282,13 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= @@ -972,12 +1296,15 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 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= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -997,6 +1324,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1005,18 +1334,25 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.21.4 h1:WtDkzTAuI31WZKDPeIYpEUA+WeUfXAmA7gwj6nzFfbc= k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.21.4 h1:KDq0lWZVslHkuE5I7iGAQHwpK0aDTlar1E7IWEc4CNw= k8s.io/apimachinery v0.21.4/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.21.4/go.mod h1:t0/eMKyUAq/DoQ7vW8NVVA00/nomlwC+eInsS8PxSew= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.21.4/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1027,10 +1363,13 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0= @@ -1040,6 +1379,7 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-tools v0.4.0/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= diff --git a/net/redistest/redistest.go b/net/redistest/redistest.go index f7066b032c..3f1456eae7 100644 --- a/net/redistest/redistest.go +++ b/net/redistest/redistest.go @@ -2,13 +2,12 @@ package redistest import ( "context" - "net" - "os/exec" - "strconv" "testing" "time" "github.com/go-redis/redis/v8" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" ) func NewTestRedis(t *testing.T) (address string, done func()) { @@ -16,43 +15,58 @@ func NewTestRedis(t *testing.T) (address string, done func()) { } func NewTestRedisWithPassword(t *testing.T, password string) (address string, done func()) { - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - address = l.Addr().String() - port := strconv.Itoa(l.Addr().(*net.TCPAddr).Port) - l.Close() - - args := []string{"--port", port} + var args []string if password != "" { args = append(args, "--requirepass", password) } - ctx, stop := context.WithCancel(context.Background()) + start := time.Now() + + // first testcontainer start takes longer than subsequent + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() - cmd := exec.CommandContext(ctx, "redis-server", args...) // #nosec - if err := cmd.Start(); err != nil { + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "redis:6-alpine", + Cmd: args, + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForLog("* Ready to accept connections"), + }, + Started: true, + }) + if err != nil { t.Fatalf("Failed to start redis server: %v", err) } - if err := ping(address, password); err != nil { + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + address, err = container.Endpoint(ctx, "") + if err != nil { + t.Fatalf("Failed to get redis address: %v", err) + } + + t.Logf("Started redis server at %s in %v", address, time.Since(start)) + + if err := ping(ctx, address, password); err != nil { t.Fatalf("Failed to ping redis server: %v", err) } - t.Logf("Started redis server at %s", address) done = func() { t.Logf("Stopping redis server at %s", address) - stop() - cmd.Wait() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + if err := container.Terminate(ctx); err != nil { + t.Fatalf("Failed to stop redis: %v", err) + } } return } -func ping(address, password string) error { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - +func ping(ctx context.Context, address, password string) error { rdb := redis.NewClient(&redis.Options{Addr: address, Password: password}) defer rdb.Close() From 196a5a542396f3b246c40f6e4ce052178ae95f8e Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 21 Feb 2022 12:00:24 +0100 Subject: [PATCH 0002/1047] scheduler: delays closing of deleted lifo queues (#1953) Closing of a lifo queue on route deletion while in-flight requests try to get a slot from it results in `Unknown error for route based LIFO: queue closed for host` error and 500 response to the client. To mitigate the problem this change puts deleted queues aside and closes them after one minute. The proper fix would be to close queue when all route's in-flight requests complete, see #202. Updates #1851 Signed-off-by: Alexander Yastrebov --- scheduler/export_test.go | 5 ++ scheduler/scheduler.go | 148 ++++++++++++++++++++++-------------- scheduler/scheduler_test.go | 62 ++++++++++----- 3 files changed, 138 insertions(+), 77 deletions(-) create mode 100644 scheduler/export_test.go diff --git a/scheduler/export_test.go b/scheduler/export_test.go new file mode 100644 index 0000000000..2e50cd3b5c --- /dev/null +++ b/scheduler/export_test.go @@ -0,0 +1,5 @@ +package scheduler + +var ( + ExportQueueCloseDelay = &queueCloseDelay +) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 78ad184d83..62e1eb6517 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -72,7 +72,7 @@ type Queue struct { // Options provides options for the registry. type Options struct { - // MetricsUpdateTimeout defines the frequence of how often the LIFO metrics + // MetricsUpdateTimeout defines the frequency of how often the LIFO metrics // are updated when they are enabled. Defaults to 1s. MetricsUpdateTimeout time.Duration @@ -95,11 +95,22 @@ type Options struct { // type Registry struct { options Options - queues *sync.Map measuring bool quit chan struct{} + + mu sync.Mutex + queues map[queueId]*Queue + deleted map[*Queue]time.Time +} + +type queueId struct { + name string + grouped bool } +// Amount of time to wait before closing the deleted queues +var queueCloseDelay = 1 * time.Minute + // LIFOFilter is the interface that needs to be implemented by the filters that // use a LIFO queue maintained by the registry. type LIFOFilter interface { @@ -184,8 +195,9 @@ func RegistryWith(o Options) *Registry { return &Registry{ options: o, - queues: new(sync.Map), quit: make(chan struct{}), + queues: make(map[queueId]*Queue), + deleted: make(map[*Queue]time.Time), } } @@ -194,6 +206,23 @@ func NewRegistry() *Registry { return RegistryWith(Options{}) } +func (r *Registry) getQueue(id queueId, c Config) *Queue { + r.mu.Lock() + defer r.mu.Unlock() + + q, ok := r.queues[id] + if ok { + if q.config != c { + q.config = c + q.reconfigure() + } + } else { + q = r.newQueue(id.name, c) + r.queues[id] = q + } + return q +} + func (r *Registry) newQueue(name string, c Config) *Queue { q := &Queue{ config: c, @@ -222,6 +251,28 @@ func (r *Registry) newQueue(name string, c Config) *Queue { return q } +func (r *Registry) deleteUnused(inUse map[queueId]struct{}) { + r.mu.Lock() + defer r.mu.Unlock() + + now := time.Now() + closeCutoff := now.Add(-queueCloseDelay) + + for q, deleted := range r.deleted { + if deleted.Before(closeCutoff) { + delete(r.deleted, q) + q.close() + } + } + + for id, q := range r.queues { + if _, ok := inUse[id]; !ok { + delete(r.queues, id) + r.deleted[q] = now + } + } +} + // Returns routing.PreProcessor that ensures single lifo filter instance per route // // Registry can not implement routing.PreProcessor directly due to unfortunate method name clash with routing.PostProcessor @@ -261,7 +312,7 @@ func (registryPreProcessor) Do(routes []*eskip.Route) []*eskip.Route { // It preserves the existing queue when available. func (r *Registry) Do(routes []*routing.Route) []*routing.Route { rr := make([]*routing.Route, len(routes)) - existingKeys := make(map[string]bool) + inUse := make(map[queueId]struct{}) groups := make(map[string][]GroupedLIFOFilter) for i, ri := range routes { @@ -280,24 +331,11 @@ func (r *Registry) Do(routes []*routing.Route) []*routing.Route { } lifoCount++ - var q *Queue - key := fmt.Sprintf("lifo::%s", ri.Id) - existingKeys[key] = true - c := lf.Config() - qi, ok := r.queues.Load(key) - if ok { - // Will not reach here if routes were pre-processed - // because key is derived from the unique route id and - // pre-processor ensures single lifo filter instance per route - q = qi.(*Queue) - if q.config != c { - q.config = c - q.reconfigure() - } - } else { - q = r.newQueue(ri.Id, c) - r.queues.Store(key, q) - } + + id := queueId{ri.Id, false} + inUse[id] = struct{}{} + + q := r.getQueue(id, lf.Config()) lf.SetQueue(q) } @@ -327,34 +365,17 @@ func (r *Registry) Do(routes []*routing.Route) []*routing.Route { foundConfig = true } - var q *Queue - key := fmt.Sprintf("group-lifo::%s", name) - existingKeys[key] = true - qi, ok := r.queues.Load(key) - if ok { - q = qi.(*Queue) - if q.config != c { - q.config = c - q.reconfigure() - } - } else { - q = r.newQueue(name, c) - r.queues.Store(key, q) - } + id := queueId{name, true} + inUse[id] = struct{}{} + + q := r.getQueue(id, c) for _, glf := range group { glf.SetQueue(q) } } - r.queues.Range(func(key, qi interface{}) bool { - if !existingKeys[key.(string)] { - qi.(*Queue).close() - r.queues.Delete(key) - } - - return true - }) + r.deleteUnused(inUse) return rr } @@ -367,16 +388,9 @@ func (r *Registry) measure() { r.measuring = true go func() { for { - r.queues.Range(func(_, value interface{}) bool { - q := value.(*Queue) - s := q.Status() - r.options.Metrics.UpdateGauge(q.activeRequestsMetricsKey, float64(s.ActiveRequests)) - r.options.Metrics.UpdateGauge(q.queuedRequestsMetricsKey, float64(s.QueuedRequests)) - return true - }) - select { case <-time.After(r.options.MetricsUpdateTimeout): + r.updateMetrics() case <-r.quit: return } @@ -384,13 +398,31 @@ func (r *Registry) measure() { }() } -// Close closes the registry, including gracefull tearing down the stored -// queues. +func (r *Registry) updateMetrics() { + r.mu.Lock() + defer r.mu.Unlock() + + for _, q := range r.queues { + s := q.Status() + r.options.Metrics.UpdateGauge(q.activeRequestsMetricsKey, float64(s.ActiveRequests)) + r.options.Metrics.UpdateGauge(q.queuedRequestsMetricsKey, float64(s.QueuedRequests)) + } +} + +// Close closes the registry, including graceful tearing down the stored queues. func (r *Registry) Close() { - r.queues.Range(func(_, value interface{}) bool { - value.(*Queue).close() - return true - }) + r.mu.Lock() + defer r.mu.Unlock() + + for q := range r.deleted { + delete(r.deleted, q) + q.close() + } + + for id, q := range r.queues { + delete(r.queues, id) + q.close() + } close(r.quit) } diff --git a/scheduler/scheduler_test.go b/scheduler/scheduler_test.go index bb9a68e71d..cfba7a298a 100644 --- a/scheduler/scheduler_test.go +++ b/scheduler/scheduler_test.go @@ -182,6 +182,7 @@ func TestScheduler(t *testing.T) { func TestConfig(t *testing.T) { waitForStatus := func(t *testing.T, q *scheduler.Queue, s scheduler.QueueStatus) { + t.Helper() timeout := time.After(120 * time.Millisecond) for { if q.Status() == s { @@ -196,6 +197,9 @@ func TestConfig(t *testing.T) { } } + const testQueueCloseDelay = 1 * time.Second + *scheduler.ExportQueueCloseDelay = testQueueCloseDelay + initTest := func(doc string) (*routing.Routing, *testdataclient.Client, func()) { cli, err := testdataclient.NewDoc(doc) if err != nil { @@ -220,6 +224,21 @@ func TestConfig(t *testing.T) { } } + updateDoc := func(t *testing.T, dc *testdataclient.Client, upsertDoc string, deletedIDs []string) { + t.Helper() + if err := dc.UpdateDoc(upsertDoc, deletedIDs); err != nil { + t.Fatal(err) + } + time.Sleep(120 * time.Millisecond) + } + + getQueue := func(path string, rt *routing.Routing) *scheduler.Queue { + req := &http.Request{URL: &url.URL{Path: path}} + r, _ := rt.Route(req) + f := r.Filters[0] + return f.Filter.(scheduler.LIFOFilter).GetQueue() + } + t.Run("group config applied", func(t *testing.T) { const doc = ` g1: Path("/one") -> lifoGroup("g", 2, 2) -> ; @@ -273,10 +292,7 @@ func TestConfig(t *testing.T) { waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) // change the configuration, should decrease the queue size: - const updateDoc = `route: * -> lifo(2, 1) -> ` - if err := dc.UpdateDoc(updateDoc, nil); err != nil { - t.Fatal(err) - } + updateDoc(t, dc, `route: * -> lifo(2, 1) -> `, nil) waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) }) @@ -309,19 +325,15 @@ func TestConfig(t *testing.T) { waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) // change the configuration, should decrease the queue size: - const updateDoc = ` + updateDoc(t, dc, ` g1: Path("/one") -> lifoGroup("g", 2, 1) -> ; g2: Path("/two") -> lifoGroup("g") -> ; - ` - - if err := dc.UpdateDoc(updateDoc, nil); err != nil { - t.Fatal(err) - } + `, nil) waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) }) - t.Run("queue gets closed when removed", func(t *testing.T) { + t.Run("queue gets closed when removed after delay", func(t *testing.T) { const doc = ` g1: Path("/one") -> lifo(2, 2) -> ; g2: Path("/two") -> lifo(2, 2) -> ; @@ -330,16 +342,28 @@ func TestConfig(t *testing.T) { rt, dc, close := initTest(doc) defer close() - req := &http.Request{URL: &url.URL{Path: "/one"}} - r, _ := rt.Route(req) - f := r.Filters[0] - q := f.Filter.(scheduler.LIFOFilter).GetQueue() + q1 := getQueue("/one", rt) + q2 := getQueue("/two", rt) - if err := dc.UpdateDoc("", []string{"g1"}); err != nil { - t.Fatal(err) - } + waitForStatus(t, q1, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, q2, scheduler.QueueStatus{Closed: false}) + + updateDoc(t, dc, "", []string{"g1"}) + + // Queue is not closed immediately when deleted + waitForStatus(t, q1, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, q2, scheduler.QueueStatus{Closed: false}) + + // An update triggers closing of the deleted queue if it + // was deleted more than testQueueCloseDelay ago + time.Sleep(testQueueCloseDelay) + updateDoc(t, dc, `g3: Path("/three") -> lifo(2, 2) -> ;`, nil) + + q3 := getQueue("/three", rt) - waitForStatus(t, q, scheduler.QueueStatus{Closed: true}) + waitForStatus(t, q1, scheduler.QueueStatus{Closed: true}) + waitForStatus(t, q2, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, q3, scheduler.QueueStatus{Closed: false}) }) } From 6f7b4bb4c3414c8f9d40010fc7cfc7d6abbc8b53 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 21 Feb 2022 23:40:13 +0100 Subject: [PATCH 0003/1047] filters/scheduler: wait for spans finishing in lifo errors test (#1964) The tests was introduced by #1944 It may happen that not all spans are finished by the time test verifies response results (because spans are closed after response has been served). This change waits for all mock spans to finish and forces scheduler metrics update. Signed-off-by: Alexander Yastrebov --- filters/scheduler/lifo_test.go | 38 +++++++++++++++++++++++++++++++++- scheduler/scheduler.go | 6 ++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/filters/scheduler/lifo_test.go b/filters/scheduler/lifo_test.go index d9c86ad7fb..083074bc40 100644 --- a/filters/scheduler/lifo_test.go +++ b/filters/scheduler/lifo_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "net/url" "sync" + "sync/atomic" "testing" "time" @@ -13,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/aryszka/jobqueue" + "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" @@ -301,6 +303,38 @@ func TestNewLIFO(t *testing.T) { } } +type testTracer struct { + *mocktracer.MockTracer + spans int32 +} + +func (t *testTracer) Reset() { + atomic.StoreInt32(&t.spans, 0) + t.MockTracer.Reset() +} + +func (t *testTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { + atomic.AddInt32(&t.spans, 1) + return t.MockTracer.StartSpan(operationName, opts...) +} + +func (t *testTracer) FinishedSpans() []*mocktracer.MockSpan { + timeout := time.After(1 * time.Second) + retry := time.NewTicker(100 * time.Millisecond) + defer retry.Stop() + for { + finished := t.MockTracer.FinishedSpans() + if len(finished) == int(atomic.LoadInt32(&t.spans)) { + return finished + } + select { + case <-retry.C: + case <-timeout: + return nil + } + } +} + func TestLifoErrors(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { time.Sleep(time.Second) @@ -334,7 +368,7 @@ func TestLifoErrors(t *testing.T) { <-rt.FirstLoad() - tracer := mocktracer.New() + tracer := &testTracer{MockTracer: mocktracer.New()} pr := proxy.WithParams(proxy.Params{ Routing: rt, OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, @@ -366,6 +400,8 @@ func TestLifoErrors(t *testing.T) { 503: 8, // were refused due to full queue }, codes) + reg.UpdateMetrics() + metrics.WithCounters(func(counters map[string]int64) { assert.Equal(t, int64(7), counters["lifo.aroute.error.timeout"]) assert.Equal(t, int64(8), counters["lifo.aroute.error.full"]) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 62e1eb6517..5fe06da0d1 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -409,6 +409,12 @@ func (r *Registry) updateMetrics() { } } +func (r *Registry) UpdateMetrics() { + if r.options.Metrics != nil { + r.updateMetrics() + } +} + // Close closes the registry, including graceful tearing down the stored queues. func (r *Registry) Close() { r.mu.Lock() From 6927a064e7def7443b94648466fa38d0efa34fb1 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 23 Feb 2022 12:53:28 +0100 Subject: [PATCH 0004/1047] opentracing: add flag to disable creation of filter spans (#1966) Signed-off-by: Alexander Yastrebov --- config/config.go | 2 + docs/operation/operation.md | 9 ++-- proxy/proxy.go | 24 +++++----- proxy/tracing.go | 43 ++++++++++++----- proxy/tracing_test.go | 96 ++++++++++++++++++++++++++----------- skipper.go | 14 ++++-- 6 files changed, 128 insertions(+), 60 deletions(-) diff --git a/config/config.go b/config/config.go index 5dc7a9364d..4da393eb06 100644 --- a/config/config.go +++ b/config/config.go @@ -69,6 +69,7 @@ type Config struct { OpenTracing string `yaml:"opentracing"` OpenTracingInitialSpan string `yaml:"opentracing-initial-span"` OpenTracingExcludedProxyTags string `yaml:"opentracing-excluded-proxy-tags"` + OpenTracingDisableFilterSpans bool `yaml:"opentracing-disable-filter-spans"` OpentracingLogFilterLifecycleEvents bool `yaml:"opentracing-log-filter-lifecycle-events"` OpentracingLogStreamEvents bool `yaml:"opentracing-log-stream-events"` OpentracingBackendNameTag bool `yaml:"opentracing-backend-name-tag"` @@ -327,6 +328,7 @@ func NewConfig() *Config { flag.StringVar(&cfg.OpenTracing, "opentracing", "noop", "list of arguments for opentracing (space separated), first argument is the tracer implementation") flag.StringVar(&cfg.OpenTracingInitialSpan, "opentracing-initial-span", "ingress", "set the name of the initial, pre-routing, tracing span") flag.StringVar(&cfg.OpenTracingExcludedProxyTags, "opentracing-excluded-proxy-tags", "", "set tags that should be excluded from spans created for proxy operation. must be a comma-separated list of strings.") + flag.BoolVar(&cfg.OpenTracingDisableFilterSpans, "opentracing-disable-filter-spans", false, "disable creation of spans representing request and response filters") flag.BoolVar(&cfg.OpentracingLogFilterLifecycleEvents, "opentracing-log-filter-lifecycle-events", true, "enables the logs for request & response filters' lifecycle events that are marking start & end times.") flag.BoolVar(&cfg.OpentracingLogStreamEvents, "opentracing-log-stream-events", true, "enables the logs for events marking the times response headers & payload are streamed to the client") flag.BoolVar(&cfg.OpentracingBackendNameTag, "opentracing-backend-name-tag", false, "enables an additional tracing tag that contains a backend name for a route when it's available (e.g. for RouteGroups) (default false)") diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 26056d8e0a..709539c748 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -566,19 +566,20 @@ runtime. Full logs of the Proxy span: ### Request filters span -The request filters span logs shows `start` and `end` values for each -filter applied. +The request filters span logs show `start` and `end` events for each filter applied. ![request filter span with logs](../img/skipper_opentracing_request_filters_span_with_logs.png) ### Response filters span -The response filters span logs shows `start` and `end` values for each -filter applied. +The response filters span logs show `start` and `end` events for each filter applied. ![response filter span with logs](../img/skipper_opentracing_response_filters_span_with_logs.png) +Request and response filters event logging can be disabled by setting the `-opentracing-log-filter-lifecycle-events=false` flag and +span creation can be disabled altogether by the `-opentracing-disable-filter-spans` flag. + ### Auth filters span Auth filters are special, because they might call an authorization diff --git a/proxy/proxy.go b/proxy/proxy.go index c59f0c725b..667caffdf7 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -123,8 +123,12 @@ type OpenTracingParams struct { // Default: "ingress". InitialSpan string + // DisableFilterSpans disables creation of spans representing request and response filters. + // Default: false + DisableFilterSpans bool + // LogFilterEvents enables the behavior to mark start and completion times of filters - // on the span representing request filters being processed. + // on the span representing request/response filters being processed. // Default: false LogFilterEvents bool @@ -735,14 +739,13 @@ func (p *Proxy) applyFiltersToRequest(f []*routing.RouteFilter, ctx *context) [] } filtersStart := time.Now() - filtersSpan := tracing.CreateSpan("request_filters", ctx.request.Context(), p.tracing.tracer) - defer filtersSpan.Finish() - ctx.parentSpan = filtersSpan + filterTracing := p.tracing.startFilterTracing("request_filters", ctx) + defer filterTracing.finish() var filters = make([]*routing.RouteFilter, 0, len(f)) for _, fi := range f { start := time.Now() - p.tracing.logFilterStart(filtersSpan, fi.Name) + filterTracing.logStart(fi.Name) tryCatch(func() { ctx.setMetricsPrefix(fi.Name) fi.Request(ctx) @@ -757,7 +760,7 @@ func (p *Proxy) applyFiltersToRequest(f []*routing.RouteFilter, ctx *context) [] p.log.Errorf("error while processing filter during request: %s: %v (%s)", fi.Name, err, stack) }) - p.tracing.logFilterEnd(filtersSpan, fi.Name) + filterTracing.logEnd(fi.Name) filters = append(filters, fi) if ctx.deprecatedShunted() || ctx.shunted() { @@ -772,15 +775,14 @@ func (p *Proxy) applyFiltersToRequest(f []*routing.RouteFilter, ctx *context) [] // applies filters to a response in reverse order func (p *Proxy) applyFiltersToResponse(filters []*routing.RouteFilter, ctx *context) { filtersStart := time.Now() - filtersSpan := tracing.CreateSpan("response_filters", ctx.request.Context(), p.tracing.tracer) - defer filtersSpan.Finish() - ctx.parentSpan = filtersSpan + filterTracing := p.tracing.startFilterTracing("response_filters", ctx) + defer filterTracing.finish() last := len(filters) - 1 for i := range filters { fi := filters[last-i] start := time.Now() - p.tracing.logFilterStart(filtersSpan, fi.Name) + filterTracing.logStart(fi.Name) tryCatch(func() { ctx.setMetricsPrefix(fi.Name) fi.Response(ctx) @@ -795,7 +797,7 @@ func (p *Proxy) applyFiltersToResponse(filters []*routing.RouteFilter, ctx *cont p.log.Errorf("error while processing filters during response: %s: %v (%s)", fi.Name, err, stack) }) - p.tracing.logFilterEnd(filtersSpan, fi.Name) + filterTracing.logEnd(fi.Name) } p.metrics.MeasureAllFiltersResponse(ctx.route.Id, filtersStart) diff --git a/proxy/tracing.go b/proxy/tracing.go index 4f90363974..6881769c74 100644 --- a/proxy/tracing.go +++ b/proxy/tracing.go @@ -2,6 +2,8 @@ package proxy import ( ot "github.com/opentracing/opentracing-go" + + "github.com/zalando/skipper/tracing" ) const ( @@ -33,11 +35,17 @@ const ( type proxyTracing struct { tracer ot.Tracer initialOperationName string + disableFilterSpans bool logFilterLifecycleEvents bool logStreamEvents bool excludeTags map[string]bool } +type filterTracing struct { + span ot.Span + logEvents bool +} + func newProxyTracing(p *OpenTracingParams) *proxyTracing { if p == nil { p = &OpenTracingParams{} @@ -60,6 +68,7 @@ func newProxyTracing(p *OpenTracingParams) *proxyTracing { return &proxyTracing{ tracer: p.Tracer, initialOperationName: p.InitialSpan, + disableFilterSpans: p.DisableFilterSpans, logFilterLifecycleEvents: p.LogFilterEvents, logStreamEvents: p.LogStreamEvents, excludeTags: excludedTags, @@ -86,26 +95,38 @@ func (t *proxyTracing) setTag(span ot.Span, key string, value interface{}) *prox return t } -func (t *proxyTracing) logFilterEvent(span ot.Span, filterName, event string) { - if !t.logFilterLifecycleEvents { +func (t *proxyTracing) logStreamEvent(span ot.Span, eventName, eventValue string) { + if !t.logStreamEvents { return } - t.logEvent(span, filterName, event) + t.logEvent(span, eventName, eventValue) } -func (t *proxyTracing) logStreamEvent(span ot.Span, eventName, eventValue string) { - if !t.logStreamEvents { - return +func (t *proxyTracing) startFilterTracing(operation string, ctx *context) *filterTracing { + if t.disableFilterSpans { + return nil } + span := tracing.CreateSpan(operation, ctx.request.Context(), t.tracer) + ctx.parentSpan = span - t.logEvent(span, eventName, eventValue) + return &filterTracing{span, t.logFilterLifecycleEvents} } -func (t *proxyTracing) logFilterStart(span ot.Span, filterName string) { - t.logFilterEvent(span, filterName, StartEvent) +func (t *filterTracing) finish() { + if t != nil { + t.span.Finish() + } } -func (t *proxyTracing) logFilterEnd(span ot.Span, filterName string) { - t.logFilterEvent(span, filterName, EndEvent) +func (t *filterTracing) logStart(filterName string) { + if t != nil && t.logEvents { + t.span.LogKV(filterName, StartEvent) + } +} + +func (t *filterTracing) logEnd(filterName string) { + if t != nil && t.logEvents { + t.span.LogKV(filterName, EndEvent) + } } diff --git a/proxy/tracing_test.go b/proxy/tracing_test.go index 1da38ca406..7962358f82 100644 --- a/proxy/tracing_test.go +++ b/proxy/tracing_test.go @@ -8,12 +8,16 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "testing" "time" ot "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/tracing/tracingtest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const traceHeader = "X-Trace-Header" @@ -363,43 +367,77 @@ func TestProxyTracingDefaultOptions(t *testing.T) { } } -func TestEnabledLogFilterLifecycleEvents(t *testing.T) { - tracer := mocktracer.New() - tracing := newProxyTracing(&OpenTracingParams{ - Tracer: tracer, - LogFilterEvents: true, - }) - span := tracer.StartSpan("test") - defer span.Finish() +func TestFilterTracing(t *testing.T) { + for _, tc := range []struct { + name string + operation string + filters []string + params *OpenTracingParams + expectLogs string + }{ + { + name: "enable log filter events", + operation: "request_filters", + filters: []string{"f1", "f2"}, + params: &OpenTracingParams{LogFilterEvents: true}, + expectLogs: "f1: start, f1: end, f2: start, f2: end", + }, + { + name: "disable log filter events", + operation: "request_filters", + filters: []string{"f1", "f2"}, + params: &OpenTracingParams{LogFilterEvents: false}, + expectLogs: "", + }, + { + name: "disable filter span (ignores log events)", + operation: "request_filters", + filters: []string{"f1", "f2"}, + params: &OpenTracingParams{DisableFilterSpans: true, LogFilterEvents: true}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + tracer := mocktracer.New() + tc.params.Tracer = tracer + tracing := newProxyTracing(tc.params) + + ctx := &context{request: &http.Request{}} + + ft := tracing.startFilterTracing(tc.operation, ctx) + for _, f := range tc.filters { + ft.logStart(f) + ft.logEnd(f) + } + ft.finish() - tracing.logFilterStart(span, "test-filter") - tracing.logFilterEnd(span, "test-filter") + spans := tracer.FinishedSpans() - mockSpan := span.(*mocktracer.MockSpan) + if tc.params.DisableFilterSpans { + assert.Nil(t, ctx.parentSpan) + assert.Len(t, spans, 0) + return + } - if len(mockSpan.Logs()) != 2 { - t.Errorf("filter lifecycle events were not logged although it was enabled") + require.Len(t, spans, 1) + + span := spans[0] + assert.Equal(t, span, ctx.parentSpan) + assert.Equal(t, tc.operation, span.OperationName) + assert.Equal(t, tc.expectLogs, spanLogs(span)) + }) } } -func TestDisabledLogFilterLifecycleEvents(t *testing.T) { - tracer := mocktracer.New() - tracing := newProxyTracing(&OpenTracingParams{ - Tracer: tracer, - LogFilterEvents: false, - }) - span := tracer.StartSpan("test") - defer span.Finish() - - tracing.logFilterStart(span, "test-filter") - tracing.logFilterEnd(span, "test-filter") - - mockSpan := span.(*mocktracer.MockSpan) - - if len(mockSpan.Logs()) != 0 { - t.Errorf("filter lifecycle events were logged although it was disabled") +func spanLogs(span *mocktracer.MockSpan) string { + var logs []string + for _, e := range span.Logs() { + for _, f := range e.Fields { + logs = append(logs, fmt.Sprintf("%s: %s", f.Key, f.ValueString)) + } } + return strings.Join(logs, ", ") } + func TestEnabledLogStreamEvents(t *testing.T) { tracer := mocktracer.New() tracing := newProxyTracing(&OpenTracingParams{ diff --git a/skipper.go b/skipper.go index 85547568f1..16930a6b91 100644 --- a/skipper.go +++ b/skipper.go @@ -608,6 +608,9 @@ type Options struct { // OpenTracingExcludedProxyTags can disable a tag so that it is not recorded. By default every tag is included. OpenTracingExcludedProxyTags []string + // OpenTracingDisableFilterSpans flag is used to disable creation of spans representing request and response filters. + OpenTracingDisableFilterSpans bool + // OpenTracingLogFilterLifecycleEvents flag is used to enable/disable the logs for events marking request and // response filters' start & end times. OpenTracingLogFilterLifecycleEvents bool @@ -1641,11 +1644,12 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { } proxyParams.OpenTracing = &proxy.OpenTracingParams{ - Tracer: tracer, - InitialSpan: o.OpenTracingInitialSpan, - ExcludeTags: o.OpenTracingExcludedProxyTags, - LogFilterEvents: o.OpenTracingLogFilterLifecycleEvents, - LogStreamEvents: o.OpenTracingLogStreamEvents, + Tracer: tracer, + InitialSpan: o.OpenTracingInitialSpan, + ExcludeTags: o.OpenTracingExcludedProxyTags, + DisableFilterSpans: o.OpenTracingDisableFilterSpans, + LogFilterEvents: o.OpenTracingLogFilterLifecycleEvents, + LogStreamEvents: o.OpenTracingLogStreamEvents, } // create the proxy From 7d9a5ef180f8c690f49165f999bed6f0137e046f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 23 Feb 2022 18:21:09 +0100 Subject: [PATCH 0005/1047] fix goroutine leak in webhook filter that you can observe if dataclients trigger regular updates and this filter will be recreated. A common fix is the way tokeninfo caches the authclient by target (#1967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- filters/auth/webhook.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/filters/auth/webhook.go b/filters/auth/webhook.go index 4ebd515a1e..995d30301c 100644 --- a/filters/auth/webhook.go +++ b/filters/auth/webhook.go @@ -64,6 +64,7 @@ func (ws *webhookSpec) CreateFilter(args []interface{}) (filters.Filter, error) return nil, filters.ErrInvalidFilterParameters } + var ok bool s, ok := args[0].(string) if !ok { return nil, filters.ErrInvalidFilterParameters @@ -89,14 +90,21 @@ func (ws *webhookSpec) CreateFilter(args []interface{}) (filters.Filter, error) } } - ac, err := newAuthClient(s, webhookSpanName, ws.options.Timeout, ws.options.MaxIdleConns, ws.options.Tracer) - if err != nil { - return nil, filters.ErrInvalidFilterParameters + var ac *authClient + var err error + if ac, ok = webhookAuthClient[s]; !ok { + ac, err = newAuthClient(s, webhookSpanName, ws.options.Timeout, ws.options.MaxIdleConns, ws.options.Tracer) + if err != nil { + return nil, filters.ErrInvalidFilterParameters + } + webhookAuthClient[s] = ac } return &webhookFilter{authClient: ac, forwardResponseHeaderKeys: forwardResponseHeaderKeys}, nil } +var webhookAuthClient map[string]*authClient = make(map[string]*authClient) + func copyHeader(to, from http.Header) { for k, v := range from { to[http.CanonicalHeaderKey(k)] = v From 505a09d4f1c981971a79f1638378e1a2166edd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 24 Feb 2022 10:57:54 +0100 Subject: [PATCH 0006/1047] fix: oidc*Claims filter paniced if no claims were used as input argument (#1969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- filters/auth/oidc.go | 2 +- filters/auth/oidc_test.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index 90e3b7e684..2d3f9930b0 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -129,7 +129,7 @@ func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) if err != nil { return nil, err } - if len(sargs) < paramClaims { + if len(sargs) <= paramClaims { return nil, filters.ErrInvalidFilterParameters } diff --git a/filters/auth/oidc_test.go b/filters/auth/oidc_test.go index 65db0bd0ec..09247a2404 100644 --- a/filters/auth/oidc_test.go +++ b/filters/auth/oidc_test.go @@ -592,6 +592,17 @@ func TestCreateFilterOIDC(t *testing.T) { }, wantErr: true, }, + { + name: "missing claims result in error", + args: []interface{}{ + oidcServer.URL, // provider/issuer + "cliendId", + "clientSecret", + oidcServer.URL + "/redirect", // redirect URL + "email name", + }, + wantErr: true, + }, } { t.Run(tt.name, func(t *testing.T) { spec := &tokenOidcSpec{ From b485d467f39b195fdd784542b93c8e312c675b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=83=20pitr?= Date: Fri, 25 Feb 2022 10:07:24 +0100 Subject: [PATCH 0007/1047] opentracing: fix flag disabling filter spans (#1970) fixes #1966 Signed-off-by: pitr --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index 4da393eb06..8fdde2585d 100644 --- a/config/config.go +++ b/config/config.go @@ -684,6 +684,7 @@ func (c *Config) ToOptions() skipper.Options { OpenTracing: strings.Split(c.OpenTracing, " "), OpenTracingInitialSpan: c.OpenTracingInitialSpan, OpenTracingExcludedProxyTags: strings.Split(c.OpenTracingExcludedProxyTags, ","), + OpenTracingDisableFilterSpans: c.OpenTracingDisableFilterSpans, OpenTracingLogStreamEvents: c.OpentracingLogStreamEvents, OpenTracingLogFilterLifecycleEvents: c.OpentracingLogFilterLifecycleEvents, MetricsListener: c.MetricsListener, From 308f44d486ff4a6d5a1ea2a50a4ce0a1f27d084e Mon Sep 17 00:00:00 2001 From: Samuel Lang Date: Fri, 25 Feb 2022 19:31:02 +0100 Subject: [PATCH 0008/1047] debugging in VSCode (#1973) It can be helpful to run Skipper in a debug session locally that enables one to inspect variables and do other debugging activities in order to analyze filter and token states. adding docs how a VSCode launch configuration for a full OIDC roundtrip could look like Signed-off-by: Samuel Lang --- .gitignore | 1 + docs/tutorials/development.md | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/.gitignore b/.gitignore index 17d1e5d5bb..2e87c15478 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ _test_plugins_fail/*.so opentracingplugin/build build/ skptesting/lorem.html +.vscode/* diff --git a/docs/tutorials/development.md b/docs/tutorials/development.md index 3ffc9edc7f..ecb853637e 100644 --- a/docs/tutorials/development.md +++ b/docs/tutorials/development.md @@ -42,6 +42,45 @@ curl -s http://localhost:9999/foo 2 ``` +### Debugging Skipper + +It can be helpful to run Skipper in a debug session locally that enables one to inspect variables and do other debugging activities in order to analyze filter and token states. + +For *Visual Studion Code* users, a simple setup could be to create following *launch configuration* that compiles Skipper, runs it in a *Delve* debug session, and then opens the default web browser creating the request. By setting a breakpoint, you can inspect the state of the filter or application. This setup is especially useful when inspecting *oauth* flows and tokens as it allows stepping through the states. + +
+Example `.vscode/launch.json` file + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/cmd/skipper/main.go", + "args": [ + "-application-log-level=debug", + "-address=:9999", + "-inline-routes=PathSubtree(\"/\") -> inlineContent(\"Hello World\") -> ", + // example OIDC setup, using https://developer.microsoft.com/en-us/microsoft-365/dev-program + // "-oidc-secrets-file=${workspaceFolder}/.vscode/launch.json", + // "-inline-routes=* -> oauthOidcAnyClaims(\"https://login.microsoftonline.com//v2.0\",\"\",\"\",\"http://localhost:9999/authcallback\", \"profile\", \"\", \"\", \"x-auth-email:claims.email x-groups:claims.groups\") -> inlineContent(\"restriced access\") -> ", + ], + "serverReadyAction": { + "pattern": "route settings applied", + "uriFormat": "http://localhost:9999", + "action": "openExternally" + } + } + ] +} +``` + +
+ ## Docs We have user documentation and developer documentation separated. From 8c474aa82a2973cd4444041cdde93510b6671037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 25 Feb 2022 19:34:06 +0100 Subject: [PATCH 0009/1047] update depencies to fix dependabot alerts (#1971) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- go.mod | 26 +++++++++++++------------- go.sum | 26 +++++++++++++++++--------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 0d64b6a2ac..9377bbe06f 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,6 @@ module github.com/zalando/skipper require ( - github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/MicahParks/keyfunc v1.0.1 github.com/abbot/go-http-auth v0.4.0 github.com/andybalholm/brotli v1.0.4 @@ -23,13 +22,11 @@ require ( github.com/hashicorp/memberlist v0.3.0 github.com/instana/go-sensor v1.38.3 github.com/lightstep/lightstep-tracer-go v0.25.0 - github.com/looplab/fsm v0.3.0 // indirect github.com/miekg/dns v1.1.45 github.com/oklog/ulid v1.3.1 github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 - github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/prometheus/client_golang v1.11.0 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/sanity-io/litter v1.5.2 @@ -39,25 +36,20 @@ require ( github.com/stretchr/testify v1.7.0 github.com/szuecs/rate-limit-buffer v0.7.1 github.com/szuecs/routegroup-client v0.21.0 + github.com/testcontainers/testcontainers-go v0.12.0 github.com/tidwall/gjson v1.12.1 - github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible github.com/yookoala/gofast v0.6.0 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 - go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d - golang.org/x/tools v0.1.8 // indirect - google.golang.org/grpc v1.43.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/api v0.21.4 k8s.io/apimachinery v0.21.4 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf @@ -66,6 +58,7 @@ require ( require ( cloud.google.com/go v0.65.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 // indirect github.com/Microsoft/hcsshim v0.8.16 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect @@ -76,7 +69,7 @@ require ( github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 // indirect github.com/containerd/containerd v1.5.0-beta.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/distribution v2.8.0+incompatible // indirect github.com/docker/docker v20.10.11+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect @@ -96,6 +89,7 @@ require ( github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/json-iterator/go v1.1.11 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect + github.com/looplab/fsm v0.3.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/moby/sys/mount v0.2.0 // indirect @@ -105,26 +99,32 @@ require ( github.com/modern-go/reflect2 v1.0.1 // indirect github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/opencontainers/runc v1.0.2 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/runc v1.0.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/shirou/gopsutil/v3 v3.21.2 // indirect - github.com/testcontainers/testcontainers-go v0.12.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/tklauser/numcpus v0.3.0 // indirect go.opencensus.io v0.22.4 // indirect + go.uber.org/atomic v1.9.0 // indirect golang.org/x/mod v0.5.1 // indirect + golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 // indirect golang.org/x/text v0.3.7 // indirect + golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.6 // indirect google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect + google.golang.org/grpc v1.43.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/klog/v2 v2.8.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect ) diff --git a/go.sum b/go.sum index 0b50c3028c..c543ec33f3 100644 --- a/go.sum +++ b/go.sum @@ -56,6 +56,7 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Flaque/filet v0.0.0-20201012163910-45f684403088 h1:PnnQln5IGbhLeJOi6hVs+lCeF+B1dRfFKPGXUAez0Ww= github.com/Flaque/filet v0.0.0-20201012163910-45f684403088/go.mod h1:TK+jB3mBs+8ZMWhU5BqZKnZWJ1MrLo8etNVg51ueTBo= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= @@ -192,6 +193,7 @@ github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= @@ -247,6 +249,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= @@ -274,8 +277,9 @@ github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbr github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY= +github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -378,10 +382,12 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-restit/lzjson v0.0.0-20161206095556-efe3c53acc68/go.mod h1:7vXSKQt83WmbPeyVjCfNT9YDJ5BUFmcwFsEjI9SCvYM= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -478,6 +484,7 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -577,7 +584,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -649,15 +655,17 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go. github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= +github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -687,9 +695,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -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/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -763,6 +770,7 @@ github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -1091,7 +1099,6 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 h1:T6tyxxvHMj2L1R2kZg0uNMpS8ZhB9lRa9XRGTCSA65w= @@ -1115,6 +1122,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1244,7 +1252,6 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1280,7 +1287,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -1323,8 +1329,10 @@ gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 2c79795de8f376d34e5b4fecdcc58718cd90aeb6 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 25 Feb 2022 19:52:27 +0100 Subject: [PATCH 0010/1047] filters/auth: do not close shared auth clients (#1972) * removes auth filters `Close` methods: they are never called due to #202 and it makes no sense to close shared auth clients anyway * removes mention of `Close` from the developement docs Updates #202 Signed-off-by: Alexander Yastrebov --- docs/reference/development.md | 16 ++-------------- docs/tutorials/development.md | 2 -- filters/auth/oidc.go | 5 ----- filters/auth/oidc_introspection_test.go | 2 -- filters/auth/tokeninfo.go | 5 ----- filters/auth/tokeninfo_test.go | 21 +++++---------------- filters/auth/tokenintrospection.go | 5 ----- filters/auth/tokenintrospection_test.go | 7 ++----- filters/auth/webhook.go | 9 ++------- filters/auth/webhook_test.go | 7 ++----- 10 files changed, 13 insertions(+), 66 deletions(-) diff --git a/docs/reference/development.md b/docs/reference/development.md index 9eb4f0a12c..a2076eb34c 100644 --- a/docs/reference/development.md +++ b/docs/reference/development.md @@ -97,7 +97,7 @@ index 10d5769..da46fe0 100644 Documentation for users should be done in `docs/`. -``` +```` diff --git a/docs/filters.md b/docs/filters.md index d3bb872..a877062 100644 --- a/docs/filters.md @@ -127,7 +127,7 @@ index d3bb872..a877062 100644 ## oauthTokeninfoAnyScope If skipper is started with `-oauth2-tokeninfo-url` flag, you can use -``` +```` ### Add godoc @@ -170,8 +170,6 @@ process. A spec has to satisfy the `Spec` interface `Name() string` and The actual filter implementation has to satisfy the `Filter` interface `Request(filters.FilterContext)` and `Response(filters.FilterContext)`. -If you need to clean up for example a goroutine you can do it in -`Close()`, which will be called on filter shutdown. ``` diff --git a/filters/auth/webhook.go b/filters/auth/webhook.go @@ -254,16 +252,6 @@ index 0000000..f0632a6 +} + +func (*webhookFilter) Response(filters.FilterContext) {} -+ -+// Close cleans-up the quit channel used for this filter -+func (f *webhookFilter) Close() { -+ f.authClient.mu.Lock() -+ if f.authClient.quit != nil { -+ close(f.authClient.quit) -+ f.authClient.quit = nil -+ } -+ f.authClient.mu.Unlock() -+} ``` ### Writing tests diff --git a/docs/tutorials/development.md b/docs/tutorials/development.md index ecb853637e..4d3494bef2 100644 --- a/docs/tutorials/development.md +++ b/docs/tutorials/development.md @@ -109,8 +109,6 @@ process. A spec has to satisfy the `filters.Spec` interface `Name() string` and The actual filter implementation has to satisfy the `filter.Filter` interface `Request(filters.FilterContext)` and `Response(filters.FilterContext)`. -If you need to clean up for example a goroutine you can do it in -`Close()`, which will be called on filter shutdown. The simplest filter possible is, if `filters.Spec` and `filters.Filter` are the same type: diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index 2d3f9930b0..e4c66092a2 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -860,11 +860,6 @@ func (f *tokenOidcFilter) getTokenWithExchange(state *OauthState, ctx filters.Fi return oauth2Token, err } -func (f *tokenOidcFilter) Close() error { - f.encrypter.Close() - return nil -} - func newDeflatePoolCompressor(level int) *deflatePoolCompressor { return &deflatePoolCompressor{ poolWriter: &sync.Pool{ diff --git a/filters/auth/oidc_introspection_test.go b/filters/auth/oidc_introspection_test.go index 03de7ebb53..b1aa359810 100644 --- a/filters/auth/oidc_introspection_test.go +++ b/filters/auth/oidc_introspection_test.go @@ -310,8 +310,6 @@ func TestOIDCQueryClaimsFilter(t *testing.T) { } else if err != nil { t.Fatalf("Unexpected error while creating filter: %v", err) } - fOIDC := f.(*tokenOidcFilter) - defer fOIDC.Close() // adding the OIDCQueryClaimsFilter to the route querySpec := NewOIDCQueryClaimsFilter() diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index 2a4d3b10ac..e89bac2a53 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -362,8 +362,3 @@ func (f *tokeninfoFilter) Request(ctx filters.FilterContext) { } func (f *tokeninfoFilter) Response(filters.FilterContext) {} - -// Close cleans-up the authClient -func (f *tokeninfoFilter) Close() { - f.authClient.Close() -} diff --git a/filters/auth/tokeninfo_test.go b/filters/auth/tokeninfo_test.go index b45e564973..239e89d916 100644 --- a/filters/auth/tokeninfo_test.go +++ b/filters/auth/tokeninfo_test.go @@ -223,13 +223,11 @@ func TestOAuth2Tokeninfo(t *testing.T) { } args = append(args, ti.args...) - f, err := spec.CreateFilter(args) + _, err := spec.CreateFilter(args) if err != nil { - t.Logf("error in creating filter") + t.Logf("error creating filter") return } - f2 := f.(*tokeninfoFilter) - defer f2.Close() fr := make(filters.Registry) fr.Register(spec) @@ -324,13 +322,11 @@ func TestOAuth2TokenTimeout(t *testing.T) { spec := NewOAuthTokeninfoAnyScope(u, ti.timeout) scopes := []interface{}{"read-x"} - f, err := spec.CreateFilter(scopes) + _, err := spec.CreateFilter(scopes) if err != nil { t.Error(err) return } - f2 := f.(*tokeninfoFilter) - defer f2.Close() fr := make(filters.Registry) fr.Register(spec) @@ -370,21 +366,14 @@ func TestOAuth2TokenTimeout(t *testing.T) { } func BenchmarkOAuthTokeninfoFilter(b *testing.B) { - allF := make([]*tokeninfoFilter, 0) for i := 0; i < b.N; i++ { var spec filters.Spec args := []interface{}{"uid"} spec = NewOAuthTokeninfoAnyScope("https://127.0.0.1:12345/token", 3*time.Second) - f, err := spec.CreateFilter(args) + _, err := spec.CreateFilter(args) if err != nil { - b.Logf("error in creating filter") + b.Logf("error creating filter") break } - f2 := f.(*tokeninfoFilter) - allF = append(allF, f2) - } - - for i := range allF { - allF[i].Close() } } diff --git a/filters/auth/tokenintrospection.go b/filters/auth/tokenintrospection.go index 3cf77addf7..cfd070923f 100644 --- a/filters/auth/tokenintrospection.go +++ b/filters/auth/tokenintrospection.go @@ -482,8 +482,3 @@ func (f *tokenintrospectFilter) Request(ctx filters.FilterContext) { } func (f *tokenintrospectFilter) Response(filters.FilterContext) {} - -// Close cleans-up the authClient -func (f *tokenintrospectFilter) Close() { - f.authClient.Close() -} diff --git a/filters/auth/tokenintrospection_test.go b/filters/auth/tokenintrospection_test.go index 154d97c322..92e0eaf9dc 100644 --- a/filters/auth/tokenintrospection_test.go +++ b/filters/auth/tokenintrospection_test.go @@ -556,18 +556,15 @@ func TestOAuth2Tokenintrospection(t *testing.T) { args := []interface{}{testOidcConfig.Issuer} args = append(args, ti.args...) - f, err := spec.CreateFilter(args) + _, err := spec.CreateFilter(args) if err != nil { if ti.expected == invalidFilterExpected { return } - t.Errorf("error in creating filter for %s: %v", ti.msg, err) + t.Errorf("error creating filter for %s: %v", ti.msg, err) return } - f2 := f.(*tokenintrospectFilter) - defer f2.Close() - fr := make(filters.Registry) fr.Register(spec) r := &eskip.Route{Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, Backend: backend.URL} diff --git a/filters/auth/webhook.go b/filters/auth/webhook.go index 995d30301c..3a7374652c 100644 --- a/filters/auth/webhook.go +++ b/filters/auth/webhook.go @@ -34,6 +34,8 @@ type ( } ) +var webhookAuthClient map[string]*authClient = make(map[string]*authClient) + // NewWebhook creates a new auth filter specification // to validate authorization for requests via an // external web hook. @@ -103,8 +105,6 @@ func (ws *webhookSpec) CreateFilter(args []interface{}) (filters.Filter, error) return &webhookFilter{authClient: ac, forwardResponseHeaderKeys: forwardResponseHeaderKeys}, nil } -var webhookAuthClient map[string]*authClient = make(map[string]*authClient) - func copyHeader(to, from http.Header) { for k, v := range from { to[http.CanonicalHeaderKey(k)] = v @@ -140,8 +140,3 @@ func (f *webhookFilter) Request(ctx filters.FilterContext) { } func (*webhookFilter) Response(filters.FilterContext) {} - -// Close cleans-up the authClient -func (f *webhookFilter) Close() { - f.authClient.Close() -} diff --git a/filters/auth/webhook_test.go b/filters/auth/webhook_test.go index 468d00324d..594478f492 100644 --- a/filters/auth/webhook_test.go +++ b/filters/auth/webhook_test.go @@ -101,15 +101,12 @@ func TestWebhook(t *testing.T) { args = append(args, headerToCopy) } - f, err := spec.CreateFilter(args) + _, err := spec.CreateFilter(args) if err != nil { - t.Errorf("error in creating filter for %s: %v", ti.msg, err) + t.Errorf("error creating filter for %s: %v", ti.msg, err) return } - f2 := f.(*webhookFilter) - defer f2.Close() - fr := make(filters.Registry) fr.Register(spec) r := &eskip.Route{Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, Backend: backend.URL} From 9472414eb6d3edeb81d6749f93f3a4953a0ea053 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Thu, 17 Mar 2022 21:28:39 +0100 Subject: [PATCH 0011/1047] split into multi-stage image so that apk runs on amd64 (#1954) Signed-off-by: Martin Linkhorst --- delivery.yaml | 11 ++++++++--- packaging/Dockerfile.arm | 8 ++++---- packaging/Dockerfile.arm64 | 7 ++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 0fdfadecb5..9e7ccca14c 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -8,6 +8,7 @@ pipeline: commands: - desc: build-push cmd: | + IMAGE_REGISTRY="registry-write.opensource.zalan.do" if [[ $CDP_TARGET_BRANCH == master && ! $CDP_PULL_REQUEST_NUMBER ]]; then LATEST_VERSION=$(git describe --tags --always | awk -F \- '{print $1}') CUR_PART=$(echo $LATEST_VERSION | awk -F . '{print $1"."$2}') @@ -17,11 +18,15 @@ pipeline: if [ "$CUR_PART" != "$VERSION_PART" ]; then NEW_PATCH=0; fi RELEASE_VERSION=${VERSION_PART}.${NEW_PATCH} export VERSION="${RELEASE_VERSION}" - IMAGE="registry-write.opensource.zalan.do/teapot/skipper:${RELEASE_VERSION}" + IMAGE="${IMAGE_REGISTRY}/teapot/skipper:${RELEASE_VERSION}" + ARM_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-armv7:${RELEASE_VERSION}" + ARM64_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-arm64:${RELEASE_VERSION}" else - IMAGE="registry-write.opensource.zalan.do/teapot/skipper-test:${CDP_BUILD_VERSION}" + IMAGE="${IMAGE_REGISTRY}/teapot/skipper-test:${CDP_BUILD_VERSION}" + ARM_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-armv7-test:${CDP_BUILD_VERSION}" + ARM64_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-arm64-test:${CDP_BUILD_VERSION}" fi - export IMAGE + export IMAGE ARM_IMAGE ARM64_IMAGE make deps cicheck staticcheck gosec git status diff --git a/packaging/Dockerfile.arm b/packaging/Dockerfile.arm index 10a182e9fa..54a8fad4ad 100644 --- a/packaging/Dockerfile.arm +++ b/packaging/Dockerfile.arm @@ -1,12 +1,12 @@ -FROM arm32v7/alpine +FROM --platform=linux/amd64 alpine:3.13 AS build LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates -RUN mkdir -p /usr/bin + +FROM --platform=linux/arm/v7 alpine:3.13 +COPY --from=build /etc/ssl/certs/ /etc/ssl/certs/ ADD build/linux_arm7/skipper /usr/bin/ ENV PATH $PATH:/usr/bin EXPOSE 9090 9911 CMD ["/usr/bin/skipper"] - - diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 38acd68af6..55ea266aa0 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,11 +1,12 @@ -FROM arm64v8/alpine +FROM --platform=linux/amd64 alpine:3.13 AS build LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates -RUN mkdir -p /usr/bin + +FROM --platform=linux/arm64 alpine:3.13 +COPY --from=build /etc/ssl/certs/ /etc/ssl/certs/ ADD build/linux_arm8/skipper /usr/bin/ ENV PATH $PATH:/usr/bin EXPOSE 9090 9911 CMD ["/usr/bin/skipper"] - From 25ad7f46043a120ec8510f624f33aa71a52889c4 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 18 Mar 2022 12:22:57 +0100 Subject: [PATCH 0012/1047] Pins go version to 1.17 (#1980) Pins go version until tooling (e.g. `staticcheck`) is ready to support 1.18 Signed-off-by: Alexander Yastrebov --- delivery.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delivery.yaml b/delivery.yaml index 9e7ccca14c..646c795b71 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -3,7 +3,7 @@ pipeline: - id: build vm_config: type: linux - image: "cdp-runtime/go" + image: "cdp-runtime/go-1.17" type: script commands: - desc: build-push From 6aed0197480b780667ddcaee714d58d999434008 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 21 Mar 2022 11:10:52 +0100 Subject: [PATCH 0013/1047] queuelistener: measure accept latency (#1978) Signed-off-by: Alexander Yastrebov --- queuelistener/listener.go | 55 ++++++++++++++++++---------------- queuelistener/listener_test.go | 12 ++++++-- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/queuelistener/listener.go b/queuelistener/listener.go index f4a2467c3a..93e056c897 100644 --- a/queuelistener/listener.go +++ b/queuelistener/listener.go @@ -20,10 +20,16 @@ const ( maxCalculatedQueueSize = 50_000 acceptedConnectionsKey = "listener.accepted.connections" queuedConnectionsKey = "listener.queued.connections" + acceptLatencyKey = "listener.accept.latency" ) +type external struct { + net.Conn + accepted time.Time +} + type connection struct { - net net.Conn + *external queueDeadline time.Time release chan<- struct{} quit <-chan struct{} @@ -77,9 +83,9 @@ type listener struct { maxConcurrency int maxQueueSize int externalListener net.Listener - acceptExternal chan net.Conn + acceptExternal chan *external externalError chan error - acceptInternal chan net.Conn + acceptInternal chan *connection internalError chan error releaseConnection chan struct{} quit chan struct{} @@ -92,14 +98,6 @@ var ( errListenerClosed = errors.New("listener closed") ) -func (c *connection) Read(b []byte) (n int, err error) { return c.net.Read(b) } -func (c *connection) Write(b []byte) (n int, err error) { return c.net.Write(b) } -func (c *connection) LocalAddr() net.Addr { return c.net.LocalAddr() } -func (c *connection) RemoteAddr() net.Addr { return c.net.RemoteAddr() } -func (c *connection) SetDeadline(t time.Time) error { return c.net.SetDeadline(t) } -func (c *connection) SetReadDeadline(t time.Time) error { return c.net.SetReadDeadline(t) } -func (c *connection) SetWriteDeadline(t time.Time) error { return c.net.SetWriteDeadline(t) } - func (c *connection) Close() error { c.once.Do(func() { select { @@ -107,7 +105,7 @@ func (c *connection) Close() error { case <-c.quit: } - c.closeErr = c.net.Close() + c.closeErr = c.external.Close() }) return c.closeErr @@ -160,9 +158,9 @@ func listenWith(nl net.Listener, o Options) (net.Listener, error) { maxConcurrency: o.maxConcurrency(), maxQueueSize: o.maxQueueSize(), externalListener: nl, - acceptExternal: make(chan net.Conn), + acceptExternal: make(chan *external), externalError: make(chan error), - acceptInternal: make(chan net.Conn), + acceptInternal: make(chan *connection), internalError: make(chan error), releaseConnection: make(chan struct{}), quit: make(chan struct{}), @@ -220,9 +218,10 @@ func (l *listener) String() string { func (l *listener) listenExternal() { var ( c net.Conn + ex *external err error delay time.Duration - acceptExternal chan<- net.Conn + acceptExternal chan<- *external externalError chan<- error retry <-chan time.Time ) @@ -251,13 +250,14 @@ func (l *listener) listenExternal() { } } else { acceptExternal = l.acceptExternal + ex = &external{c, time.Now()} externalError = nil retry = nil delay = 0 } select { - case acceptExternal <- c: + case acceptExternal <- ex: case externalError <- err: // we cannot accept anymore, but we have returned the permanent error return @@ -277,17 +277,17 @@ func (l *listener) listenInternal() { concurrency int queue *ring err error - acceptInternal chan<- net.Conn + acceptInternal chan<- *connection internalError chan<- error nextTimeout <-chan time.Time ) queue = newRing(l.maxQueueSize) for { - var nextConn net.Conn + var nextConn *connection if queue.size > 0 && concurrency < l.maxConcurrency { acceptInternal = l.acceptInternal - nextConn = queue.peek() + nextConn = queue.peek().(*connection) } else { acceptInternal = nil } @@ -316,10 +316,10 @@ func (l *listener) listenInternal() { select { case conn := <-l.acceptExternal: cc := &connection{ - net: conn, - release: l.releaseConnection, - quit: l.quit, - once: sync.Once{}, + external: conn, + release: l.releaseConnection, + quit: l.quit, + once: sync.Once{}, } if l.options.QueueTimeout > 0 { @@ -328,7 +328,7 @@ func (l *listener) listenInternal() { drop := queue.enqueue(cc) if drop != nil { - drop.(*connection).net.Close() + drop.(*connection).external.Close() } l.testNotifyQueueChange() @@ -347,7 +347,7 @@ func (l *listener) listenInternal() { var dropped int for queue.size > 0 && queue.peekOldest().(*connection).queueDeadline.Before(now) { drop := queue.dequeueOldest() - drop.(*connection).net.Close() + drop.(*connection).external.Close() } nextTimeout = nil @@ -356,7 +356,7 @@ func (l *listener) listenInternal() { l.testNotifyQueueChange() } case <-l.quit: - queue.rangeOver(func(c net.Conn) { c.(*connection).net.Close() }) + queue.rangeOver(func(c net.Conn) { c.(*connection).external.Close() }) // Closing the real listener in a separate goroutine is based on inspecting the // stdlib. It's fair to just log the errors. @@ -376,6 +376,9 @@ func (l *listener) listenInternal() { func (l *listener) Accept() (net.Conn, error) { select { case c := <-l.acceptInternal: + if l.options.Metrics != nil { + l.options.Metrics.MeasureSince(acceptLatencyKey, c.external.accepted) + } return c, nil case err := <-l.internalError: return nil, err diff --git a/queuelistener/listener_test.go b/queuelistener/listener_test.go index deab029bab..15a7c60f4d 100644 --- a/queuelistener/listener_test.go +++ b/queuelistener/listener_test.go @@ -348,7 +348,7 @@ func TestInterface(t *testing.T) { t.Fatal(err) } - if !conn.(*connection).net.(*testConnection).isClosed() { + if !conn.(*connection).external.Conn.(*testConnection).isClosed() { t.Error("failed to close underlying connection") } }) @@ -880,7 +880,7 @@ func TestTeardown(t *testing.T) { } } - if c0.(*connection).net.(*testConnection).isClosed() { + if c0.(*connection).external.Conn.(*testConnection).isClosed() { t.Error("the accepted connection was closed by the queue") } @@ -1004,7 +1004,7 @@ func TestMonitoring(t *testing.T) { } }) - t.Run("updates the gauges for the concurrency and the queue size", func(t *testing.T) { + t.Run("updates the gauges for the concurrency and the queue size, measures accept latency", func(t *testing.T) { m := &metricstest.MockMetrics{} l, err := listenWith(&testListener{}, Options{ Metrics: m, @@ -1034,6 +1034,12 @@ func TestMonitoring(t *testing.T) { }); err != nil { t.Fatal(err) } + + m.WithMeasures(func(measures map[string][]time.Duration) { + if len(measures[acceptLatencyKey]) != 3 { + t.Error("latency measures mismatch") + } + }) }) t.Run("multiple calls to close are tolerated", func(t *testing.T) { From dfb35b37a08eb1379432099f04c4931deef5f0c9 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 21 Mar 2022 11:11:22 +0100 Subject: [PATCH 0014/1047] Uses debug level for `http: URL query contains semicolon` (#1984) Fixes #1982 Signed-off-by: Alexander Yastrebov --- skipper.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skipper.go b/skipper.go index 16930a6b91..03f403bf28 100644 --- a/skipper.go +++ b/skipper.go @@ -811,9 +811,10 @@ type serverErrorLogWriter struct{} func (*serverErrorLogWriter) Write(p []byte) (int, error) { m := string(p) - // https://github.com/golang/go/issues/26918 if strings.HasPrefix(m, "http: TLS handshake error") && strings.HasSuffix(m, ": EOF\n") { - log.Debug(m) + log.Debug(m) // https://github.com/golang/go/issues/26918 + } else if strings.HasPrefix(m, "http: URL query contains semicolon") { + log.Debug(m) // https://github.com/golang/go/issues/25192 } else { log.Error(m) } From e8d49773113e9e0799543b51b2d5972ce4b2b04b Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 21 Mar 2022 12:11:09 +0100 Subject: [PATCH 0015/1047] cmd/eskip: fixes go1.18 vet error (#1979) Signed-off-by: Alexander Yastrebov --- cmd/eskip/doc.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/eskip/doc.go b/cmd/eskip/doc.go index 068dcc6937..356936313b 100644 --- a/cmd/eskip/doc.go +++ b/cmd/eskip/doc.go @@ -112,6 +112,7 @@ append file a file containing a chain of filters to be appended to the filter chain in each route Media flags: + ` /* position for generated flags */ @@ -146,7 +147,8 @@ patch takes a list of routes as input from any media except of inline route. Example: eskip patch -append 'filter1() -> filter2()' -version print eskip version` +version print eskip version +` ) // simplified check for help request: @@ -162,9 +164,9 @@ func isHelp() bool { // print command line help: func usage() { - fmt.Println(help1) + fmt.Print(help1) flags.SetOutput(os.Stdout) flags.PrintDefaults() flags.SetOutput(nowrite) - fmt.Println(help2) + fmt.Print(help2) } From 800c00897339deb8435b88b4bf06f656a11bbd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 22 Mar 2022 20:33:28 +0100 Subject: [PATCH 0016/1047] doc: latency() can also fed with time.Duration string (#1986) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- docs/reference/filters.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 6b1f0cf2f0..1d9e57ccaf 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -570,12 +570,13 @@ Enable adding artificial latency Parameters: -* latency in milliseconds (int) +* latency in milliseconds (int) or in `time` as a string in double quotes, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) Example: ``` * -> latency(120) -> "https://www.example.org"; +* -> latency("120ms") -> "https://www.example.org"; ``` ## bandwidth From 68f1be39c16f83886c30f215f8e5c88c739e06c3 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 24 Mar 2022 15:45:55 +0100 Subject: [PATCH 0017/1047] metrics: add MeasureSince benchmark (#1988) ``` $ go test -count=10 github.com/zalando/skipper/metrics -run NONE -bench BenchmarkMeasureSince -benchmem goos: linux goarch: amd64 pkg: github.com/zalando/skipper/metrics cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz BenchmarkMeasureSincePrometheus-8 7068398 165.5 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7408232 164.5 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7264077 161.6 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 6803798 161.9 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7437289 161.7 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7436797 161.7 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7433630 176.8 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7126840 181.1 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 6839091 167.8 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSincePrometheus-8 7372002 169.5 ns/op 16 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 1898020 641.2 ns/op 110 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 2415656 627.5 ns/op 95 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 2226484 521.4 ns/op 90 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 2283265 512.2 ns/op 75 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 2348788 718.6 ns/op 72 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 2129406 528.8 ns/op 66 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 1936142 590.2 ns/op 66 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 1969534 602.1 ns/op 65 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 1956208 606.3 ns/op 64 B/op 1 allocs/op BenchmarkMeasureSinceCodaHale-8 1965328 601.4 ns/op 64 B/op 1 allocs/op ``` Signed-off-by: Alexander Yastrebov --- metrics/metrics_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index ae689a894f..01f9484f28 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "strings" "testing" + "time" "github.com/zalando/skipper/metrics" ) @@ -207,3 +208,21 @@ func TestHandlerCodaHaleUnknownMetricRequest(t *testing.T) { t.Error("Request for unknown metrics should return a Not Found status") } } + +func BenchmarkMeasureSincePrometheus(b *testing.B) { + m := metrics.NewMetrics(metrics.Options{Format: metrics.PrometheusKind}) + benchmarkMeasureSince(b, m) +} + +func BenchmarkMeasureSinceCodaHale(b *testing.B) { + m := metrics.NewMetrics(metrics.Options{Format: metrics.CodaHaleKind}) + benchmarkMeasureSince(b, m) +} + +func benchmarkMeasureSince(b *testing.B, m metrics.Metrics) { + start := time.Now() + b.ResetTimer() + for i := 0; i < b.N; i++ { + m.MeasureSince("a.metrics.key", start) + } +} From 953742caac237da02997ac5dc5d629bc2c220ff6 Mon Sep 17 00:00:00 2001 From: Oliver Trosien Date: Tue, 29 Mar 2022 10:21:12 +0200 Subject: [PATCH 0018/1047] Document fadeIn and Rolling Restart caveat (#1990) https://github.com/zalando/skipper/issues/1207 Signed-off-by: Oliver Trosien --- docs/img/fadein_traffic_skew.png | Bin 0 -> 81311 bytes docs/reference/filters.md | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 docs/img/fadein_traffic_skew.png diff --git a/docs/img/fadein_traffic_skew.png b/docs/img/fadein_traffic_skew.png new file mode 100644 index 0000000000000000000000000000000000000000..8687b86c6a1b9c46f9d48b7d06a136e8972e38b3 GIT binary patch literal 81311 zcmZU41yo$kvM%lp0fIBQySp=&k+znQPzFj! zkSV)3T3Xv#KtRwYni(5o0U2pWOiYZ8MMJnUuBNb9yudK4OAd?p!IJiFC&~zUr z=#T_L=z$+C77o^0#h3SYS6*o74r=Iut+V&{_su`=?^_lq zC=)G#K0S~S*RqDZ-91i@;Dyz+&<0v6C_vDI(+Cid5!Mhe;1neIApk!R5YV7-2zc;6 z7Wk3Ohx(sVsEB;%|4Bop{;epcCIJM3|JBS~EG!&cKRdd;W2~)%n_9G1*LKrZ_{4AK zXwPh7?r3Vk>}Butmjpu4iyxe{w{SBd^Rl;daOL+B0{l~hADsT1%>p3%r;3}c5I|c& znM}gb#e$5RnU$FpAdEytMkeTDZpp7IDgAG9@S70evzwa}KMRYerzf)~2eYG#6$=|5 zA0G=VI}1BI6SxMGtG9!ji5HWDE5*MW`JZ+qEnLl9texDf9UaL2wrgVQ=H0t zrg{Dr)a5x;tyGsUe790->_;!`>-QW}XbOoO90(xm@e%|92J;EbUD~x#rLoF{*_yHjlk=oz1I;4{eN}Vs|eHYQH^UPFDKW( zSf#gf`xH&;qz4{FphP|rg>o+hN*JwZ)s=R4PoUK3cUCrUv>%j8!!1<^lPBBUwDMEHv@rtUmVwknq3q&m+Z_?|P~#F08SV zK0a6*&%xA)CiW2hk$q0UK%AG~t_hL>Adld1s0PJSCfK(4uCrvKs0^wc-SZLfzWC>h z(Kxj3gEs?)5UPVRg89{?maD0QG(z->Ji)QQ5f?ClcM5Frib-0W?jJ_(8FoCgG~0|* zkSz{n2raB>d==!3tq5~nBaU^|C5;~-aN|DufP;HgX2g%nh=G{a|3#^y^o8(^i1kpy z_!f-}^qZcppg4$F7s`(XLiYjUl#>c**Nh#c&puo=9^{T3Xl(=;ph%fv-iC!^>6(EL zpY1Dv6yLT<5f7WJ7=Et$v8h47;(`1jH7FK|&zo-WiSdBFg0gSF5sj={@|EFKDUgj?E}^J|MR$o@s!I7z!=OCPcI( zLE=Rfj~*5l_DjG%1tTL0IXU^4c#X~R6+`*)PC-9@Jf(s!<*;ZtL1#m*sne-LQcJV# z{{7Ox@KxDI7{h5(voiuO#@e`pHfBLLVv|CnQQHc14NX4}Exe|rt2*y7 z?2x&!Id`a}Re6yT=oLrxn)UVdLMy%dIzKwWY!6)$OEGv}5Fw2}= z@Oh2ls*$lf?9(*xOOeVmyBs?hF900^n!10Y*YUK|dheq90$QZN+%J@2+`X}<2=BIviLcFVzXg1-AbIR>V)4oOJ+G#;6T{No%TUu9EbRA;AFQn^-u(S0 z=ClYei!z|23IjbgfpO*5)y2i>vh4^!MKvTl;`SyTOR%%)`)deOhRCE?eHG6Oy<}lD zRf~2lx4%ao5R)Te9UgyLm2>2!%GSX&y1eGtV_!QuF)^qjLmc*U*@50goHrw=3vJRfnHWa zq6+Qjzv}G0h$ZCN9l|pjBn@~qId$7ll-XU>bCfbQEj;0|@NSMCTXdxwb>Gf6tPHl1 z6vp$fjG5Z&DpM5jI2Y~le{-7TS~A}54LbbPv?hTr6q-mOQ>gJ>)8ceI=s-@6)>q7z zl#so7H|NP{uOlU?Hc*io_WVSZ@aX1a?ofHP1O1XEb3xB=bwq&I}%U z3*HiM8OvEm(!a=8UKO4~2QM&vux@jThsEE{E9C8&dW(ITmPN`I)w8@A8i&jDkh4% zuW4*6x5)-9KB&h&Z8iFBSfpRBAX8b4;+713Q3*!ua(i7Trd0nd3ksteQleTYJaqPo zS)I<8A9fRP+kK9Hx}6qr{;9yT^S!29{Pu8CA;(jO*A2w9Wc77t9ByXHRq(!QED`DV zWyde><;Kr_Y4ks@b_Uq&SB>!5c3mS8R@$>dIv&l&(Rf()yS~RqH&%>-d1tq#=TCn@ z1c}Y6{Z4;a>sfV2LS|;Poao5>5w|=!9A*Q!Ik|>%tm1~8)358)G-EJIJP|1C^xgR{*P47c* z)w~bP(?Y+AzJ1CFMi+U-a6emQ@_Df1D`AQO?|qZJX&FZhPodECgF;h8E#G0fSwc>Z zJ#&-gB*8>BANI*fvN7ArL;X=^&L5FxBI0pu6{@1z)4d_O?^jQAdDlHsB4!ec9-AT> zPZMcTYalLT5%sjcC!2}J0Hd1;A=5H z<6R^Z&uwnj8whC$p;u=LtJ`GLv<69WOfvDME1Ja7Ql>) zY4yOPilzrIKn5fz2|8~-tLDIB&tamQA3zteLr9pknr1M3o?-%`NTDFN#O^u5eW7GL zgze;r(P7w^U%~r{)GhtyT|=-e6qPfE2bm-6*Pciq$NxL;{dHJ2kh$wlm7~mItDE@x z`dSJb9+q@+?<4e|uy*g??Y(Tjs12AVlB|`zYxr2%(&EjKwcufS@k4K5wxFK9+45?t;`&6ifxW$n6A&cBdDcq1O(q zzWahk!l2Ha!NppwNr<%oCeT+QMMsz3r+TqQYBPJ~N-A;UyMi3`4z3ZhNtaI+MYDcP zY$}L*sm6#jF_AF@NNTj|SU1}~S+m!S!gSHBK4Rhz;&Xv^hDug9>|0?TmqjH9CF&oee``x=ia$>OJ~S%U>t)oAS{hPF7-=z?Ct3MJEWtK zI2#Y*M^Ew~avujD#AC1Er3|;T7mCq_rJt&Gs8!}y;pmUuT{)XG>K5bJJ|7Wx&(ack z>xJ16D@o5Jo@#*nOMi{>{4ok;YM1nS3exvyPU6Hl!e0Otv~ZHDeiS~&?OrT7KkaRK z9q4|6U!7*^PD+B;p(^(lyj}RHnmvg ztZ6?E6$r^*=Q&~$C9)Kjev(n&Z-c1hCoZ3Ek0b_5{azlVS5;jw8jj$Yk%9GJ3TA2b zZ$Ba(#T~^Yuym{bP``DsTc1!uJ2y(9#boE29k=b{z;O&r+pJEEL)? zJ)@*}iJZYe$+~9q*W-x5f*NvUP$6G6BNxDaKlLjik9fK}L`l*z6U8c+Q4*Vx`h?DZ zOh)8;UiY{YK}sgRRjx)OQ4Ov5^io0Gxoqk6OR7Doo@5ACX_JqDfzpd`eV!s>jAc|( z6st~FUY-L+Lt9pNsX~LnEYr9F77MLonwiNT$(P-H5z20=1m2boi(SKl!hW&)Y;8PY z;LF1v#)&&L=oElhLFqxSbz=1XjED1CJzeMCs+Vp^6Z-6Cw3xBj>O;C!i8i&vF>u_{ zc(-FJl5KxxQR+zko@F%q+3swy3YBfXN$Qk}O}&H5c5z~U8dMKDfOo*V`8X$|C0tVY z(Xl5g0Y)zLW{^3$RNNGMwUTdFEZ1EY90Hsyt`l5HnVpINWw=bKth~0m^bU!f~U2v(?CRSv8ww5ht+9!{L*IHbGs`T z9tQb|8@KGwq|)}O;uP)6oKv?@s#0z7_|VzT%&DGIm-kU}B@^e^z5a+Nb%c9sZEa;o zt!x%PJWETt1~%y<@kt^erpH|X$`uKWc^o&t+OIWdKpM>9V@h4vt~Qo`tUceZk1_SQ zT#V5gl#vu?IRuS%;y;xTCqTh70>4PcH7Q{!ERIc@>qoC@@S^GMmo=r8&v>b2~VsIFmsf4b0mTp05y0GB)&=M^N+%tlJE_`Smm zSYmfQ&_9gy^k_%6|BKXMJU>3s1=+MG!PNzPOujpXEpQks68JK2(q|QA8yRM{~oun zVQ*9`pTws9=h#5}lL9D*W8mI+h_sQ7s51$F@f-A%eexoq=?Du8P}yB^yX_?;qRU_9 zBN5r-P9UTbtJ@TAAOXBt$m1~QD#_*szt497wdPHieU9qv$O-gL1GX_}6D{V8VGCUwuvSvLJV{4F7JCj>jBIe-bLO$KJ zI2%i2O5A@~sYyw-^OG0&=J(K=`bMxQLtSEuWt#k{!T@a&hd|oJ$)f%!1NEI0I83Qv z&}5^r?a_vR~|RspQd^n%fnBZFa?`m7bHNF&o&_CU}<0MzS`B z7^#qVXxIICi6`kkYflzja5cPsSoK}xN|6K|pitO-`}FkFt6&UHB{abE!d!7NzhLlT zxQe3lzW!CiKcN18wuzmc-Rb@9cb3Pj|3#9GR&IZD-pZ{UGt;6;|?T4N{I(&8Ozr^QLoeszLbF|r7#X*&R{g2Deb(MOjrK*v#F2bJ|I+xd z$wa1(Y*Ca%SFJ2qmg~!wpFA=7>~?z4Y)@#)5UPG2S8j{88tGs-MQ>*2$88*qFDDN_ z&HOk@8Lq{iYZ6^LmcrK0B@w7d<1P7VKhZCc%zOL>?ni2s%VawO!&cN%7pxtPn0|9m zp|jz;nj9OUyLnkw5>OkOGULF2R4ex{P;x*xPbE6Fs7$L)MY3op#hLOgQENt~`Jb(yP z_8+-ylFRe@Uj?7`lPr5%VCs_Tan>xxH+UKm~So z2E~9T8{LkY{hMDUm+NNNKS9BLLez*M>i*l_?Vk4EnhH2RaI7esvqsHg0|^8e70BOz5S?s4Y%K9q@~l*#gl>pB+Vtt#!7>N+v1&a z86pyEQsakTkh_O)evGe4_bW$Kn88-1N?x7gM(nnSz;8#w8AO}n5M+tVr7SqKSC9uv zXF6HJn1#K2LEepqn4`Nz+#tljs*uwH_pD^4(Z!WZmR}`R@pkM|Un*_p3}ps~u}a1& zVw_YA%xP@bN(*c3e=RXH{n1@L`XQ`EwN}o4YPklK7n-)Av5iYeNs3*sBpO_b8-6$^ z#_#Z;H_W?*wRF@|0LWU=Vs%$}w|yRU;`GDMep+e)N6>{Di7B||sIh(|tt&OvP6?aYik}N^z`C#~605>Y zNv_0kPk05G$ser^2O4LG-eKnlYtIi~MZ-V?8w(p~v>oHFWO1;I3{G8uQ!MT7;t-Gg zmZf^Efie8;3og$lb%T@&aRU#H;mrE22STR%HG`h=N{(};0BrY3aNAIvlOi;eKw!wiU$s;=kdkg{bbmYX-u9Ejytwl_ zG8og*CHXB|S?Bq%$&ey#YHzUd_a0!G3F?C!+6D3knsP;y;X}uop>0kFj;?Uc~-zr z&s;29(B!vJhmPwXGoAVxy2Zm}0-p*upS4?!ODxZ9=ZnHDcx-;>aLi{*chz)`sh|hP z=`Gb_Z~ds*<_FMI9da;0L=nKVG&D1rXT)s%%`jacPjAqpwhBq1=cCCa#;M4W?1ENW z@zOHtmU`XD*}4atzowJ>R8VKix>MIoSf$oo4|dZ?gc<&T}#mu0iX z^c)l-$RgFx*p3q)gG9JlEYb-SobiHVvnThrmnE}%z2hDE_XU42*x8S1>SPM&cr%X9 zL&~5a22*K6NuH^G2zbZ2nhof}53b+ocqG{V8X<_N?>)>u@BW*od^6gS_GJ5OVlRW6 zT%sFm3@P+5u|mz^Z*ND<)f1}-MW5E5vW{C~sDt1@iiiWKB8EY7jCBMUka*P@4VG2Z zI^MW{-#gRW$1R#k&RZ=Rwdf{h7D9Up+nvsb;qBLE8Uh*Y0`m*Zrc2oZWm>8z?I>Dc z4rA2zTvT$m!NsILh3SaS7pL3zYe^tZhSIZ_uOi)$#xt`X9#fUiQ-J48q9xA4cqK!r z`A{!;Z=%)>2^Amp(}9EQbc&&lr`TT`gA#msl`1)CF91GKcmDb6(95zim`sj{{7l zu*#B0KYHbs#BSa+ZF(g6G;$PERxAG84Xv#JZk+q2UkOl#aPsv2EQOL^RLLCCE61L+ z=IEglE^QYY?oj)^1ZFH=QSerJ1qOPixg)5%!$`uVNN!SIBn?DmD{vN#<`Ew4mgwY} z0=nX*;3!_ntyiUo_bB0NG-Wfv{s;CW-wLZ7`4bGC5~{4j{1-KTD1dr&4t@Df^72?oWZ}4|3>z zuG^R{T;O{gAj~6Y```N~UKl|bMsiL)5Sq(tvq@@kTXw5I^=9t-2kEM8(|&Uq4lj2X zG49n+nWBI*;nr`re7u!?eTQx-B>^DZ=g(TlDP1pgbX~gh+dZkD5WQATC*KoYedh@I zKy|Q{NA$xkziTt{b z)G0oA&+;{6w77wXMNbGx!&hlHxN%PerGXP98kI{KwoAhuzWcg=;N|y#@TKKOiSpoF z)YJ=yolXp;sA}2U{&_oE(Q0=@IwD=*_E0D_36V!9TtU&#E^&Ei4kQRfg*Alh^3CH% z)$Wpl(#q!>SQNYxvVS6++6i*SU(ZFO-WrG5x=%Q_wxw=s%VZ4N(ZCX}>C~wwj6y$!Ab+x+Aay0~U)=o+R ztA*NX1=arYcxUQ`VIFs(yr1jDcJq^lvIKcLODT349o-cIswj|{fJAHWE2Q_Da0mt< z3PqdIFf$?2%-Pxm2moaP)T$G&*bZ-F*-W?7kV2y{`-t#jVm!p<_Nn0XiyuL(7U@B< z@2?x)Uuxy;>seZQV5Y+Wub&IPo5HEX2)QyhjO3;rNL{HpNEI?7Dcx>lIXdx9)Zg!N zNjs+-RmF0Q!*N2mUBzmBrbI;1c4bK*XDGcb-awWB`Dl{~dbJNjq~eq<{&fG^%wvj? zeH)N(CZfb;OKEE(Adak43Xa<&kut}36+WyjbXD<6qy|!Urdqad?IYfPCz>K4r#{2 zA|_E0f15!Q2K@{~71*tqvMLP7@~2EF_#rklhpm@-IbI(ByUxADLqkpp&X7p7W|NCj zcHLnK2A?xpl5LZyn)}qerdi+}4rF{W353t(O`MxmAepu|L~s2CFDG;6v$g=_Z@*>U z$dn+X#c%=*-fvY8nips*x?3YAoxVfDv_b{u7hj+3} zDpKL8XeLhvv5&nhV@>wDjB8@2eMblqoud@!H!_07rS?{puz%l!lw102MH`jXnU=C8 zw;PRA4#!Z@aO!ZGHZHqLV8|MJE4g)=*q5T$CAx(-M<|fy5oMChzUo@B_of*_?X2O3 zaE=5igXS6bb?p}h0y_+2&`bvh!a`1=#Dg28ZfZKsr|j;reR=bR8qI@nU_9(xdP3d)k%7}_}?WVJVhQlaTM6!3s=XQP=Y;a*Yo z{o{UCsMAjmknyI~G;=nGPbdxF{x|Z7I5I4RG$N>!2yh63CduNZ99oc^6j?&=hw=m< zD^AVTJNU|R>F7(8vtDYEvo_9dn^A#vwcQ#XkBX|7mjv3(r$wL?xxIQWRhFWuHkyJp zV*LD&%Y6D3HEY%&YcOgt#Ox6o)b5!t8o`ezNtndp3g7zTWiOVWQ}zC~+u3V@Jfy$X zPdJVs)js0Cj4+HwJ*lWBiy&NfwYTJa4~P{5hfCOgQtKeBqCEm=< zV;AJQApU9~Fg#%g__eQ(*1K`mJeW_Cwi9|7p;krY&>1h`#pqNaAcHVo2d~plaYGT6 zpP0+mdN18`cRrHatB@_w7K!dmg&%1GL~)sgT#=JD0Np{fyagg{d&!3tbjxqBU#66CCz7BD+5>J zbC3OkR5ldEG>2ur(SvX!5)OgP{P1mYE#U_%&pRBmcND%#ds%1y1T(tb1bmCLa zqkM*HRpYUl*HL`BL#Yt~B4B-Q;S4 zXcuqCoBLnpKy!20FVGYpUs$8fouLL+iJZx29!7Gsmt8~im+Ee(VEW6~i*Q zG9TEQX%&>y=`T*BnAYyL7tT$8+JXSH4NIzbYZm|E11;d zl_-wXuVAE-=bQ|GQJX@*F!P4Z7(BSO@>Qf&E#)qa-H{Mc3&tNnOrIl>;qgP zIfoQc8o)nk6ZUWhkt#Y=CflmlPrtM)hg&i=StPy0qMhdrXFAaYq+E*VblPMHEeqne z=RPtfIU#Prnn$uD(P{+Spxflzlo8c5yf78yP(fJTP!hbn1BjzPg6-wm+*a2fzbvTF zBgTluu|PfX>0QTL8aR3W5|}M<&?_orgwPmy#+w){s%ZL7<>Nt}OsvNzF*52V4!rM7 zisxTX0_g}yuE5~`&|YvT&OW2ipYU_BUTk}spzD3L*u1p77qqU3YO=mP#o5{WXGJg( zct=g7@=wRy5Y)+_H2X{CY3ILQ(^Ip(8uu-=n%@mP8Xs^{N)MVbXrxskwd|xg3~z5> zN8p>%T45`s)0mbO*E7y?3asZvi}hkjFdZ0^16Pi%0^KRYeUNEpaPJtWLIM3|kMRLI zY)$35B?O@1tY&j1tOaD!B2?-$ESs8j@razGx8=uMK11!M)tLqAV+^N#*5l0i4dq&Z zaa4A(AHe%5nbrCmsJIce^)OAw`KWlQj-25GD&Wjm<`PXGGcSfk5kGR==N%b#C+aYp zwSwN=f7GbfRMX;8eo&@(8be}ry6QOw4R6bPpC$|bneD55gM{EPa(PmUQBNiaMc99r zhUIdxD`=08;}iAoD9S(mwZepD{#tgDK*N&E7S$h_!HFG85u#Lp?GY2I;0xvhjAc9+ zC}073csNct2tV9(KKlCjeS_?)_8)=Wen8z2UM%ht;kD{_U9a25g3l|vP#|&PLl^Z# zJa%{!8oKo~&E;uVl*DTRgU@dfCSUE-hdfN$n98|@b~NM9BVT26^s%FqFh5e$s#fK_ zD&@0XupD$IO|-$t2#vnosbcmmI?L1kZm?cI`Rh&C!gSid`jy@)1dhN82_$!sj@;kF zFE9T^%T#P`EpLf*`Tl01e}DEjk*~|v2AQ1vVfE=ES)KzhKIOP5TqUA3?O3WgQ3R)t6c^x1FdEWV-XAtHqKL{ zRd~7BPhqTKa^#PGZ;bk!hhl;vo;ao_*4YGDA%nr{omsVz1M%Gi7wsYu>j~jkiFTo~ zSVOLMI$Wu4=Q0B4zsq-bQ_rm{Iunxvqi|e46~Re~o=kSM4{e zhF_fx(b2X5vHJf8By@#%=~mkC8gjMOwEEU4!w~URDtj61fUjmGdO>_l3Xud@5!t~+ zo$N1a`VV_W>D?9|?UDCt+gORLLMeARkaVi~FX!q&(1y?wyZ?1OGt=$8|C9EpwVa9HROi}& ze1W|s%OQ?0h2R8CQfon9>o=&w%QbRkw!c7?t+id180sZ7a73SsE%|5Ew;sL3cHE_S zKcbLtoe_3F*d`ETB=Gn8g|O;i^4#nrLSDKfyOH;VWiIUl%>4oqMOqbUIvgm`Vo3O4B2D06QN|75D2S~yjR z?K%G%UdEJM0^eL6ME}L6NTm!DCzJkR8gO?H41^0Ts~)u zX2+RHzqa{T;^NjiUwkKx`Hj#6W%K-xP#}e>1Rcfha7=(HnJ7WSx4!Zj z7hNI~2$AJX(1kq(S07oM&cS(a2`dR6z*k;BaudXjcc4X7xCojW!mBqtOjTSav8;e_AKqzL;PJ=2)6%N$DI+&lp*_`x z?Cf|lhP?RF82{zi@QwK!cgtkT-1CooqGOE3uU1%#{2o9O98o5XMqWOG7%Sb$f?hMk zJUDaMq}=k`mV~XCKxcFr52VhkapTV^EtPFktVMZJAA-%6{rs^uVMpsLGT-k!1$P&i z9Fr}~O{(};$J84ApJ%4SdUVrlKksbfO zBSjB{WMW8pBq6SP8LR;y8Fh75)UAw2Ad8lQ1hyRV(qo~M`LX5~oEDP$uM=uRCN)geNNo^O9&4e-yy6J*~`mNiNx+8oWVHrB^)dSUc$2k#>s27 z{(tr-+i^&Xfwr1V z8!@9#bDRSXvWY^-rIq_5zR<+L`b5{ngbgwntbhf#bZ_E-U-(qcF$OIm)KF9b-ZyF{ z8se6>2S$?gS2UN~1{G1qec4s@HyTT}T*rNhje&4L%Vdc0ytrW~8#>n-BD^3EkIM?H z2w9sXN(po7JgYqi;<%n67moG8jA1tYFPPQQS;oN4C%(TSvN>Qe-inP34)O&%LZdMyup*qMJnC7! zshEj4@Euv6VV#p%t9}pV$FO|(HO$l(Tcw-79T}gvbcLc6YZprPr!A3dnNM8h(q5ez zRu^E3fMWDLliHC>BWFJ-axc%7^g3~+-mHoaWg_5n0A~*|Y zHC*gRN@!iS)_Q~Kwo?ER)XQHN<4bU+z%Y3&Jd7k#id{N^{b;w-{MeS$AM_A5hXE)` z)!1xx{GjoHrJbMrXHIv0rGT0xgh$ggwb*QMfC_Es4Yq^?e|E)0pzgw-vZ@#a(luHO zrmNfcW5K@rPb31VSk*srWKCtYiwT>azOVYJ-#@2Y1$vE9=y+sBRpH~mxC}sJD(KV1 z6%pi#5Lun&xVEL$OC5ELb3YR6SOIxU04MzF-dGR2{9vjf;zt~ zIW=Ris;ab`pCj|teh4c^lq(M2|0XGVmz1N2zc9@3$NVwVS^wyb?sO(94FZuVBLc*{ z0*GF2Z3OF7z@>MoGLcegvTJ$$QZr>!jFwBLc*Px+(EO2x${)X;Vu7FJ%Gh8Vua>&G zz2nFQEYevM@H4HiJ{$4+j+^RFtSD~k@}u4JB~+EJ0c&RLA8Gwz;oOd;Uiw+^za2Vp zpptBWgt*v;dG})VFPq$8Qw5-;_Csn^F$e2V3>s2??gK#!--Mm^KZIWq-l!(GtlO$I zAmPM4(*e8I>quu>STr7!DM9F_cZ8y+)UL}!o~^#xtvz7nA__i#T@d3I?g+`@fmC5X zx|J)K(Ud{>{!L$qm_KsBByqDyH$?JQGNTMP?R>v>-e5ZRAF)7DyM>gsxWJy!&L6GCfiI=VSh~U#`^lEhcG&RoyN9~q>+nM11K-J(N$Fwx~x83oHL~p zQ1lPoX$*k6{|qX?w6~{6+;z=8)rkj9>bd{=u`ofdd;kUzwPRdS-<93P|6-C`b0b;z z)cC>eA)P$q?MD+FfW6EoFUHA&FZj4^2sK=2Vj-IniA#r&(4fYI=&tIZD2Y-UMPQnF7F+{7-t3@XT6JrH~k4?T?`p6&t zQ=S5lm4S)Gg_=sIJ|c-Jo18W-JK5KDl^a7${jX^SmAUw^N|^$eSCo6VIwEVIA@yY7 z0(J(Eg@VO_MewMF3j&VJ-xYENqI~#f>3@Ty|L14V75yF_w`MwlWP#O){P0kgLG z6xIOLDxS+Y#z3B?(vO2i7NQn^l1$g=M#@z@L%e1JQf^s^cnUc3mc3hqd#M>coy`$- z3O&;@NO_U83UsJca~x&8AR!Cmy!jN7^TiE*$1Hz+Q{CzczTASI2h^GZ;p?JQva3P!BKP zKTEA1=gO79t*^S6b+Rn9gV-wateqB&1ov+&E`&y-D~jfk->x`G*xY_@Yy6BOLrn2^ z5d`tOg548ozZV)e)g2;!!>y7^2=F$wSAb?b{Iw8hw74OGf_tn9& z1^)U8M(D#IBJU=j4C^aNT2!|ECqTI6cEzz=$IUn?BqkcBSW@es;fUV)*xcj_W_uK+ zf{iS420;9mu8Jyy3NM(@HuAv1uKS(M&T&5dG}o~jiXe@jbStsoPYMk>)W2l4GN{{v z$5XjDc|>nCix7zdhb1w@g0e@}$ZZc@{eI6TDBy{Mh5zbR#X=xdi~7rjpi|!0ATC%! zSkwe&V4n4{u74g!&UBx7~GB=G1w@#x<=zC`i0R=$G#thP@;2Fv<>zsss@jgUt_Lm&ZVVj&@ z1tfI)ZQ=Nf>Z~C<-BR>+SSN(HP_=X$Yibdy1Fj^1YcjUPx~xQVws`rJkiaX=7uwC@ zR74aiw^h{5CbMOdq*6$Z<7@z-N|b*JO|1|^Y3e2Y-_K;ghiu_nxxN$wA=s8f=G~5= z4Oh|S1O-*GykBpnH!&e1W|Q(|=x>Hmaols^_2rL@4uUX~56^K>6Fc zm{2VKW}a!&8B@Z-^B+v`0lYl&VM7;{qX>}*aDVpQ3VRo)r(ZF6*lO>avpH>0_hg|O zuGG(w1Ot|%A_ZSL_N7{Q;%J0k~H= zy2ewZ6+D|CNyKd#t$Qs6`0N*EGd>t6ge>c#Y7NG4nvX?mA z>?Cx0rpl%9hWV}V3Y-33v=oOVrJmg5vswMUNg}`SucjzWa6ix#fT@fvNNvkT*LnzJ zP+QumCnX&`i}_LLppC=LTI-(I&-G~ZT{hd}Tz2SJ<~N7iRl2m7hE^fa z+~5SxA^9v`klTg(_Uf2H+t^?KQi?r+R^6C*Z$&~?tO-e!35?KJn#vbsyf|+Rk(#WC zADB(GnYr<45!F9rHuwu~RY=KdJRC!npE1O_>eV%n?CeY8QZzh0bC9G(2m2D>G10o2 ztpcX4Zw?_q;$hdGH?fTZUnxdMZi`jI)3q&C``lfJ(&7SB;N-@>dqfqi_PmqTOyaCs zZX#_TrFA{1BHHQ7V8}ZCF!yUuZm!e_D=KidO;V27^FJL z0ypf}vg^{Fo1PE^4_}bIcTguey~wlSYCbn((*&_T!fuetbK@W#WSNj;j8GIE&qMN_ z1A5dGNfF}kHFLgc=^YPSt;o7}K6?~pDf6lrJJ*$rIh)r^?>q&E;(%|ojwhhp)^d$ zHIneVYa+kLocH1*K}i$9+|$wC%WEC_+pvNmmegM$0=YMEVC;l2&kF+;WkRB%w4*L& zKe--G7~l^RKZzfdennfXYOwyzm}ApH4^QjPmlzgxD*r^Mdk0o(P0^Ve2>aa0yktjpcCXqNCMn+$!0!wWptt2RHX0dJ`Ni1gQO$7`GB6YShJ(d~Jk z%Z%#wixV47YlLGBa2Q6uG&$G>agazvLJ-%Mg|{6|3cuGgPE){UKLRS_7GyTZ-u$%y zKTLXy>tcomP`N6O`O%#K9hn8kBHIx=A~qY3hFov(^2a({9C$k)kEpWA=?#cYm5n58 zSHH(l?pV%4*e17v8dmAb9`Z=J`yKHPovO$v+>~oOUa=HcEu9hvmUt;>Z5`V|;-bYf zU`;L;+G-&P3edhrW+r_jqqa8;J8Wdp&&i6>+f}`qx?U<$Ybi}U`X0-w7=LhI3FkFF!e2khFL+RC8DFze$P07*0us z`9p2Jv*k+}Os@&%Y#xzrOsA);b;bu4#zW*&hu2KEvs9chZdN5M(Tddh zr>*oia)-xzzRK}QF*h$17nh$`ZWN@3f=_Ie&8;ABMG9eF&jJ88Qg)AB0%kNk)?< zBlko$JYRn(GC7 zbYVZ`4*c-8ukxf{v5Jt+jdgO1J(zn0NuvUl-6tJ;$oe>rza5V*OLkyiEZoc8O3L@^VALNF}39j7>uaa?}h^KHYOcrE&C`rynVScxX zY4_0}gYr3O+s8oQhp8-9DxPX!x%+6!YjGi|&+Wt7z14`s^w}tVaM>Mi*kW%F+ku>| zGmF|H@k*urb7X;Rfxp8Qdix)hQ`C=hWKozUKtOoXCNwSxpMJY(jt|98oaF%!1$quU zBP8KIxLfj{v2OdG$82$Xv0!2EYhB(P_Z61e54Q|&@4Clcr%|LcVv62HurO5Rtv3Vp z{KFu#>lGNanp`!ObR>0bqY0A&9O`#scEIXSWR)sn{@zM{LyO3VoF{E-bwmcy+xL5n*pP> zo;}S?P5vR1&;J40KqkL1{^6Z$SMHDEzFCWaF9v%i1L_Mb+XxOzmQ$=yx8V>7J_P8B z=aNGpco49IkM2leYNSTa$=!kH{&)s2FP=&xF`ToV zXyDLr!r{ZEnuLG|g9w16MVeJZ)2prCN=YI2*XH)=>?cw)=I8Rb*H3B^JbQbudJgRe zTFG-o;PiayxP8uwq!ea{-RqqhxD}#yDnrusbsZ&Bm%wwhU*&{FP&2@&1xH^G-?C}I zk3Vv%J=Wxh?|}NPI5pzkB!?u3vOl!_qm0)NyqsHi2vkP|9679xt>)_dHv}vxv;&g0 zFWj8E9QRKgh7Z>)K%>ZV6zos{06+jqL_t(K*vMH=4i4Rcr;q55le*nXX+xNq1}~J7 z1T&QQ2QD)h{Ou}kN6;c5!XRQOhlCB39{ute*Lk-JU2`Js4qF{ooETby5ww8p-Hnxe z{&cp;ndGd3QO~7Kb}fw|qqbz_U~5hew(*yolZ(w+*+}O8?Kyea!KKa*583&_78x2w zCo!6e&?Y#Hs^7TqP{fCYqd{~e8b(E;0e_A7s~1W05PQL0ELC;^C5Ap93F5`W5VGl9 z&Yev&0(mfh_Bsh-Yv9lUo`jJ^(hKU98Ja!AZ4i;l(~zphNR>j5ishZM#lX@?>UfNkT_Tj13FEaQ*)9?MMRHx_F)Az! z^}@r^I3^lRW1`SHE(V?B;|+m~;7L)|(>$Qq!i|gJ8^*b^H*z){)xUN$aOc}7Y}FgS ze5#;B`~)%J9JlHa2v!6fISkg8ts(biB^J6}4JMrN*o2=MIrkkrG;IXd=7%CSED9;v z$+)sfOAI}F5aMfh;()~Xg&ahTq|u!0sljRpZ0woJrt9*N*{H*uXA=&*{b zXpeTn?GiD0$8COlehpj(K6AQ#JwrxHNNmD{AyWp4;M&Y=tj^5D?Bwm3xotbuv8+%|gHuJuTTMkVomz)2DGd zwIp>LphFAb0otL-2GOP(+)peiw9k!&L`&^cTD2ns->=(%zqX}dX<7zWW@H#FBf~;y zzDW|vJUL{9B7E^-p-7C3ph;)A`Ncz*N1&vOMjg#E@+p?I4lPk6vm%JiC}C_B6N%RK8lc01 zZ_wqN=TSSA-piUtyL~<2whw_uo!CuCVJ$&qSxOcUi-EV-{dEXbZv-4UtlsTcV{31T zp_X}tc~oPCV{6Jh-2eNlcz?|zG>A;Vj=T&sr&aA!J^z6-55AHE6XTfqr@<3&wu~AJ zqPuZ*0!oA^C6cqr=x2aWhEq>TU?c&%=MA9a3B@EvWCAr3HT$fHplMwsPh`uc8Oh1` zY}GpawI#(6LOK5$sUBgJ)KBVPrDjplIJ91E^s1YHcC}(D)el7sXD*30%P>TSdXhOW zciI1DktC+&aY9Ix}=vPl2UKW z+^}A)1k`6N)W#{R&?#*Nx~-Xou8DO~{|bh^X>u^n*m>VNM^Z}>L+!OVan&V8p#{$>BN7lfZP$a+dt4nJd zVtUq;taQwC^zsPYHcIKHZA-?dE7xLra*9#;iXw?PqGC}P8i`2S8iw=ZAu=-sEkg6rxAj5j z-LwVT#6?l+8EFWx&QUlWNhn2#mFzppY`8xTfoh0=BZt+n(Q0ayiw{&9f*8M$o`zq| z`v?zDdK=5aY9oPz_@>+pT-Ufc9y@9f66umFsT&m7XQnzCXrHTt^6S*oq6YH|FkhCKmDU?F$!!wK|H=N;%3K_;I zha^(h=@1{s`>cZ_>hVXZVxwpVM`zM~mAnsgfPdV3$0FgD)d2xV4y!{exjGgbB<%di z$W6v`Q~!f!=Z>WxTS(dc@4MFYjA1p88Wb3Zwu97_>0vV_pSAN}Z5(%Ol zX;zQ~FXnI2#w09G&%utEx+d&pI6pS?sm9Kx>RRU?oIo`>C8MV_s*l6!)h)WLCBTxV zs{7cw6=iTM4uL9+fFp-hxN%(7JOqkfs(j&V7L#z=hoxn#!b873iPw@=pmBICQmM>p zk&}wY+MbGYj=u{93_DTaCx&*QD6av?wM#WdK%_w7tI zL#Slctm1!G0;v5};Ta5kA81`jd5piSO; zD^}u@6>E&wu*g#;4R{Z(9f#u@HNZtJo1jTF;{hrWSHFd(XM5)-Tvh$~3w+0z+u$FO zMK!;ny0-cMpvUqDPar2d7n5ms_%li3Uk!R-QNlqeNKd9?jl%JS80ljd##6nrO-meE zzpe?)ZLndPlCYX zCK4z?ObRc=)k$0MWS^VR=(x)$?Vw>e?^5=Kk_DJIe6_l3SJ?_hK!dXNPG0dRolvd- zvPo$)fAX1U0~fu(%lrFH4Urn!r$o=2)~!>;cMM7M#^MzWT%E?yGNHyBxG85Mxu|6` zT-d4^>eC@Zq(|ojCXpTc>;~?y4(qD7lD!98XF0$5&N)HRdp_8zpqJgZq_oZGAk6spH!EQ)AJVRgmIV&j&5&B%0?gDKP>tPcmMGbHsvYwMI<(bWns{~WITA( z&4@nZa?Y%fLt+@lm$?1Zuc~+F>UE$HD5UgR{fj0FS{^(V#YTgSUE?9(do3~;ERnQ?M(YbVP((EnW=)HY&hc@V)SdSji zjuuw;ZV=4%-z7OrW>C|#v@|C<4CdCa!uO^yyPPQ%5#gD5Z}D(Evv>yQPK`z~2ds95 z>9~L5Hk{IPATkG>O@r`s_EX9?YJjAt3O8BRuNuiuAw!bX+OZl5uU`$MQhG>X(DqIT zVf%F-!I#Y#hoyvFsromz?==)5j3xn0<6Ok=Y}kfRR;@uS?Rpg|x_~o{^oxtet?gTy znM~xBERB*?JMa5dtNEFVgQ$6ysi}w_aW4}8B0-Gi`zMD4u{Krh=iP~vt`}j>)~y)1 zaut5d$iWs$qT{KW*UT`I#Gc-`Auec~h)yJkshnz1?Xa&Bo@$P!t9}I|(7bu`eY%1p zhx-JZJE8X?J#$fBf50DQLYmahUDBlbYvXATiUch@5pfEDGWd7Aivn@)`5*xydC9gC}pb8Q9-&!%+I zk?#PE-D8sQwqA|5U45$>{4z#K|`=zQW-$t9t5+kpt^eWPI3RGa!XE~a-f?FzT0aqwoQBMt=yenM3< zXU?G%$n$UyJURH*z30qxmKd{cBOaVNpL38aP&}Q#c)OBf-P5VPQ5~E3eB9@rvEB3o zGoDc;tYGTv<^k{D!Jz8PIQ^c~%Z`AC*=3g%k2$&C84X^yYc@;2jk~eV2(>CmWPLbEB6ZNZP zikc!XNK@c*CM0At_$3`r9+|Vy#Pd`9P=#5!yHh&`aBgPa&w+C&+w+4Io_)J5kwU}R z_($s`eP*_u>~!GE2Z1r)c;zaOWGMYkLg58ZqcFOTA%feov+?foBz(Fm3ELP-DUSAt z+c{ldo46Q55YKDT)SPc@5xHi^O4k+!7WbzT5D0yQX?NFAXH=5p?~Xg}!1nFiQMYbg zeDcXBIO(L5uw}~@TyxDe=-an1zWw%Fp z&9q5e#<`s}9pMg+pTBI}f`!}D(2?P9>iVZ4)H_mbqPDPYT1u_lE{8z5A+Sess4IyW zszV-j*kMKzc;bmC;{EsE$DxNF3P}w`7>5lThGz7NdF{2=aN&g);_I)!Hv1+`nuNxU z8yn)NJfXC+P+28a&*gSUuAnN9fPJ}XnMbKjIMu@wmwtvDCcKHo**S;{jY3vT0UnsT z2KTROhNvf=2Re6RY`PqVpQCz^cc}NWUX*U&s`AZU`4wcpG6S|*^71>j0K*3Y8T3x3 zH3GVJ0Iqn2ok+ci3O(ucG}!AGL@Dq&6FCg!d;GoSt1xu#5)*?jlj`KQlvF+6vm56G zPB7EF*|>%F*>cary`lsLxm7pNL$cf)^DWwVwfR`;cPoOvncwF+Qhc) zZffTaIRy3#0lgM?n`}o^XB5d>ym&GC_wSFGm>5GA7cE+Z&Ye5ctS2As+P1~!&6^R) z$oWz1>{nfN6>8V6jURsa!Kj=iJ=D4-h$_>inuCDa+rHE^<3NEShT-^e!F#xV!h2Xv z8=VMxllmg^@%V&QxO-y)|E_sU`2 za7aRJBQdnZ&_1Vog?mlTZ2Ay8k?DKvZ|Y|^g= z5VmwY^4JfmwE*`_pM&M}C|6KzNf2$@x^}9` zovz~VeUIeOR>2O&vOnz7rHfG+Pn|jy(VSOPom9!@(e^#}++$S8efso4Htj29r}+K% z-{Zgk{jX6qE3Z`0F4YVK>ACqhgdVc59^M@t;%o6EjiICb7}#^|yF7?s zsn}Z)e)eULB|muH8_kC6+X18#W|Xi%HeH1fElh;tSJ&t8wPFCIW)PA2`Md}tFJ zgO_@A#nA2tqhVAe348(lSwivc+D*7_@(hevz0Qz)^A44Cl1c0Ky;!j~=t{3?2<(v@ z+LwivvM-bq%43f`W+aDW#*D#12OVUjiIN7cT)EOn3e{niZ}8y3W?!dHosg82WQgGr zM;w8P6DJy+Ri;Zd0|7JGu(>r5>9r>NnfInXi$T-AMp{7>3i%SO>&wH7V^-q)=)Ncz z_5wm1HY~C+wEfVOqnEHt4uR4T&^tx8Uy=-_QJpt(s28#(m-{<52TuB@|2R)pHwv!#E|!<=j=j z{$|w7JWtc^_2N{GgDFMqcY-HH)T}Bb>GZK9&HZwve7RqD+>b$4#e28 zW3gn(5=@yg1-IOC3%YjgidSBF1?lPOc>C?Qao&078FjNrfSggj{PIi4=Vjr-g~o|x z&6+hh{q)n(rcE2~iR$9iA^V9$5@--GZ|Xyz*TV zPHA;I3Wq+6P?|01rD|*p-OJBH=$5-fAfT@qsp0n|hT|u2iadS;NANv!HMg5|_Edhf z{Hsc~oNLQ+XvZ2}#2(3^I+=)`>A||R;*ZoL4yWCD&x4gICSgQ4WmYlLcf0faP!SK8%TBDR>rAoPkXVpv_%w&6j@Xm zB9KbXDaF7Lw0}He=t(8aI=`gDdXTa6 zuW1+HFp|WA4eX~&yqKGm>SEpkV9{uVL`R^o`5}zGskxZr(I>GkwoqC)pW(d}7crUk z*JIXiLJF7NXdVzlBB^#)`|D>@;JV%CmJWfkBCy9ehqlw%WnjU2!IbQf=B2|2Auxa7 zy3V(G0?Tsi!G*xiHm4Lr1Ca*g{2h33{A2jf(kX}!tAn)>g*YxV1J8fG8Hb#B1@iB> z6QO*`@%8Cma>3OdD)w$P;M#%MdY3%Ia3b?K@M^4VTnBh?44qNxad76g9jF5a(u%EM z#h0$F!TOAmDGCqM~K? zU_Ve~6Z;uIVpeRVbelP-fu?E&IQU?o|AWYD(;Jj=BZpd)|E*kuS2@Fq;vq(nB&O0~ z<*>T7F{pC~<87|CRQt;-UESWbEkST;pApz2IaJBElWCAw>_Vk5eyzyPP)VN3axT%! zMsrQtFBaw3xz;;5CntyRWlE2Du6r>V=z{u>_p&OWdW#dzbDreE;~&NF<sxqC7FUkC(WW z&H%FcGIJ+Wf}_Mf7Az_7{jB%vhRiJ7Jas1KrKC}t9%ANCewuL%`!%f383PLt?ti}Q zdBK81wYm?s0|!U;SB{}sJAiLU5P$iK;g+a=wj?o%$NC(Pf-@h0uYPMo_-51Y|CvQA zF?ma}ks{`>j5w;BuWQo^SG8@9C~8|YvrVAqxqFvZtDaG1DqALUSY>MKDpKVU(12r; z)-_n|$XJIve|-#ZuUUeY5ph^W8?gR-$vyv59$H=TPfk!zGXuWRG+b-u6D2or85rE3 zJt1KGn{2u?%gLtYz|;TW9Jd^B<%DufU*NiT)v-K6Xq^Q1RBkmmZl5&`{<1MtO6N!V zJ%7zLfs|uu%PR;T@7fUqIOB!_T+0qo$vs~NhRXkA`>Q@qv~Gg1OE$=~cs%euW4g|o zwlK4zy~YVP@<^tUm-|iyPwjiKK{w64?D!i znYNY(y81?7KQY&Fe;fj3K){j1GJxc=lnsHv0Vj`A8c7Vdr7p%D{TzB!#i+&kf0Lx|FGXf%rd4;(Q>>7dfTc_i; z@h{+ut(!>mIB@woJUTA{4eopl1&1@1q0AJVnZcfcbvgcyfE{2(3?&hq^DQup zPAAmHnJMxvp>y0R_ju}L*?1{`5D`Nw5t0p|X3+R(#cDh{XE9@i(jJX=X;L#j+@%wt zU>rk2VoAc^fpmKg7y`Brt6$3OLHZL(cheb$Y2y1NiEmR1POoN35fv6VoX6X@rK}NKbX!(XE6-UpiXVkfRj-aaN88?@B4ut@jSsLhd`+a zIC5AjENR&VAY%k7RWfd|HNMH~JBmutb2?DNK9-*_GW%K3Qj z_-nQyzzUvxT!Tcsa(GunlN8%L!E*0Gc^~$}MN4ZUp&LzJLwm$0fGNKMDV+Y%%ni={ zvJU9n5jg#Jpwo%?Vp|Sgn?41rGqMm95n-f=ZQ`PF&%y0+a-)VS$w-gwM}i}W1`_vY zHwZX#xEpZXq1_{32PmtevZ~~T>&9Z>xR)?7BNt6L5q(mG4-d^C63wCWC9#_q4^jyeR0fR(lg46Ba&{5}q%pL@pq3V?n8yHvv+$G!c{(;R+zELh+SQ7|o4tE*-rh(<2xMm{f+*bFb0#3FXbL2VYB4pMq=-sO zCjevUXf^FG&z6=S+h(3{9q!Q`IQDWZ?RXZRU$g>0G1Q#AnI%EwmjkEfYlfR>T2PPS z=;S0S>AAufDLIm8U~qp*LBNs2Qb2LL_6`BPVDcz245!3!^73!+&q>2ECp#4Job_Z& zXd#}Mu@<*R9*L;CsY?$QyEce17_TQP;|CWYA7(Q14;%R5pJG38-HxshDbUl#L{#1HHNI|Suep50Yd@&M8FZl(z{WW+8g-c zqjpmtHj+fSFf+{?4gB;5Fmo37b6c8Z#qo?LOQa+*|HOg#sLo;d&*D``%}NJDogQZS4IC5ATDsJB%5wH>rOAI3zLgbgl|06Mc z2TA$Sh@wG!E1SN%XK4JI<4O;H2b&oK+45cBU0aQQJi8w;O zJb)6Fxt^_Ia}IuYe+ks#EEFc-wMI`SKSA;~s_$PtGrV&X&rTs}o|v~7Z!KGe+Tmfy zqS)}MLp!5?^Cm^%OmwG?6jsyrHhtXCdygPWmn=ylXOqH`iNL65xm?PRBEC~)lPK48 zg}>?04vYF;i^p;sB(fu?e~A4bMBc;LP83e%)NKc zN#=gHeC3??l=nGrMH$1)eJC$Qtmb=3o+UxlpIguUSSix6(hb%2;IJErTFSTVx`;?c zVbbBzuIY|9|A?mjqvH^?%>hG= zC^?xt^GD!!Kd@e%lB9g`S2Z#<-h7msfXEqKId%lT zIeCI%`Yw#gA?OkRi0-6-#=8pp+-!)t2GG{`b7wKGC!JQ_;O3}<6F}-_Ij#s(P8fsV zj6DbcQ+qtN(h3=|m)^{ExiVD5EcyQRe`~QGj zZ*5nCTvUKRzxH4F)(d|}f_aj&WK?}JZTRu4aa?-(myw-$p^@Ndu?f)jt1GxX;D`GQ zdnX0NC>@g&1zMO@;s<8|8(Rqm`T5u*fva!HOBFdV-MKmzc@acVE1aF-8_c)+spscV zWtcYU@um;xi(gTlto*wwUK(V`P&zLTB)S%v&m0xnAgR2{kWEkh1$g-tI~OELRQMa( zd%rqz41PZ0Qas-G2-Kz9nUElf7(JWcH*%;6b2Naqh+3CTb``Tudr6|9X)k~A;INku zfc z0h0-Z-0x4Hrv24J)(Is>_3^)xK@OT^!J*V|4ovRLJq!38%{u`@2}X6?&hSs4ATXS} zrG=3J0vO2DcsHJS6h<@8y*6zMJa4Wk0Z?4P-O_Va>pHGwI-&Jg0pO)>Ui2jJ;Ptdk zq$Dw+5kr8;a5wR)2>vkU1pI!&htVFcKzX`@0n;-W&)m&dPZ*7J1`jgrQFawcot-4H z=Njps+|v~B;IOBAph%wYm`#?8k@6&GQrmFNvp>U67tBU=q#7**h7;O1;o21gaq@@0 zkMz_@9vBu$-xftR#Sm3on2$G^EBTk7%E&7uB9s}L44@f zR5!m!$$Fc?h(?Mypg(>#aIOXriX()Oy1p71J8 z)n-2x{j27WfT6rgXRU6+*XO=TKF_q2%!u*X38U}@#tc;;Y$+JMtys}__x?4&%?(|p z_tv`GY>f0N&@|5m)1LDvz4|wDZt%>6h#wp= z43*q?NDA%7lUv7mb?0|_?@Q@Yz=Olm?b~5g6JShFnEFFnq_o;BR0kAjap6@gKU5hOU%~D7uG$ z;AiNydE`Y@tK~!6=mJSpio9svspu$G@rvzjxP11@*hrf(#WS>ahEr*0NR@j295-i+ zyy?FB`$K{JI5A^h?=|!_bSO>CN!`p6*tmjH#Cz$yauY!!ZFQNGdEvk+{A${V@W9Yh z5MjK~P%2KxXF9C(ufr9iN8+3zHh8wilr|@B^Wny&1CF}CFQrET4-QMOPY+6q9Fxg$ z_n6|s5#=JYY~2gE`r#ko!S)P>M$561p=z#JzZL&E^<$_%{o_b+9SRE=GE|LWYg|

(Y1LAUElCln0v@gE^}n#Z`}9gSnY7`Y;_{Q!b7#tdHaS@A^C{ zPr8sX4YLSSnxRlN=6Y451f)0boSOa)2TTFS{TnQJ z6w}f*MLzDj&Ob49IS2e=!BPe`US@(^C&;9Ke$*74KV;Aj)gR9h(-kI|O=(h);((cpoY z*1w-_VQgD9Ce21?_Lp8j=&C!b)>RhSw-`FpJEO8dbwwwF}!4Q7Y9?kd|> zWkUHV^U9v}MH$2LJF+}+J&xT{D4L71Ba9I6K1UOd&%F35KQnp{L)UzK%y4{b@&d1Fvzc-8A+L%o;P3zlRXx+}j?H zV^A3*P}KLur9=B;>Y(ABmDTNSSoY4Z%$wcKLI)Xl_nYS(JJYWaK6!9h2)$4Mb#T%O zRV72)t&nV0QtkM|?4RPhjK-e~RdClCMl87*->#g5PoDBMWQR2vJFlQo=Xv|W0_eul zse)r_I*OYlJAiA>29~ZR806-DQq8!pE-+7S+K-GJ|n zJqwqf^i`y*${4C9$=xMEq?j_V^$e=OI=0xWBUPBBhGbs2|0lp-eD53Ph!Ap6AW8zE|GSZK-U#l6F9yc z63bCb6rf#sj`NKAB9pEe>lY#Y%wKWKrgq$1IRQ(fwP>&Ei(EQ|ApO3o15uoc7F;^v zaMbr5)+s%d2C0Mb72|Ylp)SeL#AIOMobAK>&#_1j21To}noGvJ zw{OFbkNpr%JmFI&J5xx;$+GcM(L&>Atfhs2ediP~j7#H+;E>}H>lXt*X8zq2S7OBB7nzVYp+ML$v960t(G$cspnxb~2Boc!HXwia1idRCW!_l6udARF z%6&jBV;gGl3Otq)FcjEh%T~2@nDls!9UX{Mit=ve!Ts&=N0?ZLYOl95ETG|9WL430 z(cG~@0ll+h&H^j_NbmSe7K_#<@kUbstJ@iDeM=HsxH(G%DhV8=3RX~dUdQ<(L7tWQ-4L;D0VqJIz# z140Co3Z$b8H9K|`@gQ?Qel199_e=IXz4zt7DPT7T2Y$W3vKT4A*<=<19@BX>G8p6P z_9w2vca}ecm9!%n5Us)1_$FM{uK_x6(^? zA>gM>C{I03jzM6kT(ReVk^pfScQpCmb$*d^0k=`J_E!vM&G!6e(Q+(lZD$O_AO@60 z@Rh^HAxb8!bNu`(+~Hg3HFIMvM+u))x5|QIQ|0BT4$h%*YZ}ijPGM0~2HQJw%)=}j z^fqK^SDu4%Fem6F6<}^={c-B0TWJ^Dj@D=l5pLd?s1L~iFUo~bLDh0yFpGYb)d(j! zU$bS%GMg47p`jz0M20HrOh%wFfF!L)QuMfLiRaIG8h%#!>YljOZ7_ zsDU9PZHy62MtF{!{+b)aw8rX$vPO1=w}XEtw^gGMzXEz@$GTP<15quotY=9xR7K#Ff6kEF1qSEpeNv`LIZi+_8;U< zTj1$VK%%?1Mvx+<4ek$5m2lmt|Nn!3>y%w|9HZeV32<^;&`_;onVNz{16lYFZ zQq0D#Xj|(mekdu>x#o91RAvj>U}&t_FIR5NVE)<^URs()<7PXzWY#NqrC{qEb?8$D z^sNSgUKE2V(Hq(?i29l^65Chdp~h8swj+QATef0(q>k$0a#TqhMV^r+FR4nl;)LiH z96w?_M$`>Ke`=*_D~F&xRErf`a#+1Ffo*MRv?U1|6U-e>P}&-|K#$fGl#(+XBkdR) zd2TM3BIIK|Y62M6mv-=!ERG~#99Y9m6#pF1)zV?wkyqHF}@p>Y$0Vbk1$~*6TDpPHe?s8Xa%QCNM4&$9E^3iA$#cUn8l|GGQm3 z--(sz@aXwwIiRN_B?@2=UflPez~2~$O4&K5Zq3(}vc-ZzD^bhhg=Lh^ ztxjV>V;Y-Vvvd%#;7a+r1q%DpK5)$7AVyPCCm=SoZxGdGQtzs`o95@=wq|-buf_A* zlX!t?Rp$ii&{1BCazaa8(Lu>$Jek74_(mKSO=H5)k(f$b!{e(X7}9V!&xjc*<;IpQ zHne52ep?33TQgYC&+E5jv6(UN1y~vX-GD{4Nm9ZTUlfaRaGwB%u#S=}j;U9jrQve* zv~{#}*imo&?r5Ylec71;9vtpWkCOc4CV-AoBS++6oJixrXMTq7E_eXXmRDj}sEq1? zZ8*K68rK~AaU3>&hLILZ#U&3kcaM@JkZ)9BP(bUpBo11&o0+}gr|)MBHcAgu{NXHm zCSOhuWy6&69HKC4eZaNQoKjnrg-Azjz&`)dT-@E*jPfw;te6(>rjw4rsA}4I@f>ew zc)+CYRTr9Z#TIJ=AyuoUjHGY;hLX7qo?o7#>Ntb7n=*)VAI729S)|4tKO}@B8zLA* zTfcr)Qr+e^V!V++ql(6=gm|=`u zL@StAD}+qE4b=>^J&+F^SX+ljJkOXC5anb(pbJeg_Y|w zShX>Sc4@BJF5*~o)O&$NwWXGzwwk8N0*aG|MKEphVqQEr+(C&5*PT35b?-_E`szuCS7t+873T{5v9R}371@4n}I zsID2#MSwd#GjY7Mu;d0tAvQ>>ySSK+k_!IuO6!DD#zkBDtgqe-3>?Sb$pK2l3B_~+ z+iRI?5Vx*s#JA?oH{QvuR44y%+9bTcVVD7ij%m4LA$Fm!Q_oYtZQ>1B;)_!qZzf24 zWknKCEl5);r_G1}9?f{Ewyhu*tM3!S;Uhvger$w*aAzPT+eE=bJN9xvdp%dt zE`&lQ=r@HxF-3dAg)85{W6PJ~xlms;F<$3JKD4Eb=1XN2bYx*&QcTxJoA*FCivHO+ zj?Qex5d#Nd+PJCIW>H-%C~BAf z1O~;!q70wY&_9I3hJ`6r3{m|YqBpeer2ARGYtH9qU1RyCeXp~6Yw_T)1beRDoDfG)F@vU*E;EEH!M0H*TnGxe&kwLpj?-^1F z4w6D@l9-DAR3ze<{}82y|H8GK-%&z<5B)1}&Q~cK;0F_MT_d?i_cOKJPS3 z>Cm`n@hf<#HHFtwAuMZLhJ~33HkI{9Su{$;at1*PB^W&y309z-wu)nun=q02!^e-D zfUyL5W1~5Y7&MZ$@pYXfZH;HJgdkD2ldCsov2H8>wpqR>si)c{32K`2DoV6GdSDP^ z2Zb?~>T-d_G6GN)*ABb3bhd(Rmt@h>p!1=<;Ee}|CDzFr;0DZTGEjvKs*uf2u4yrT z^VBc!n=SLOq^uV89PCZ$I8Non_|f#MaonhLjfkGph}T;+vPfg@Rbn*q%_%SnXdO1I zFW)HW^$$-6mNeQ*jvEGi{XrWDTbxExMY~cLSR?koI#Ly9t*8Rucy%6bp(9Eq^+XIo?mvx7`3g+~P6jhBwC?Mp!FN2y>DmA2eA@rf^H`1?^ldWd zr72pgt)#8r7y-fvM$65FjpY&S2h}B-U=04w^Q*+<0obN&TGSUp2K7L+b)`Y6)Qq zrHT{zCxKG7mjCRKJ%<*)cI?2a?I#7It94i?`r=i~oW9QwhXq zB%DhnxkHQNBZC_7&3Anjb$!O|Zm^(A2GB;{Y)pwczYYeZAUPFGY;henKgeMJB8TUE~_NO#idVA1yJusVR_#zk=2VG*NB*7urvcKYoAh2jOBU{WU?0&m94V#TpEM9MSxkK;FC8R$WS?l zQ?fc~D8uD2Y)^HWm9QEIA3lea;;Ww6wRA82x<2Vpy!YVnP*AxWp7EB6reQ6Br)O>5 zfV*D(fB3J(kK&$S8iT^s1_0)g+wh@bQ}CrDKa7SUCw2jay2wsgO=Y6|$s;%7pTbJGn}a*5f6rGOhd`lRV1C9(}KmSZN9G#;HtJHD07KEmG>7&=Z5|2ldapF~h7 zClkdc)EpDIalpon#hzO8U7H*)Q`>A253+N`eZzo?Sk<@$OKD%Yl&a`uO)If<(?%=| zR%27F&VW0qtQD#|M?Og17$xrn&&fE!;$}23A@uON{unV}C?nj@cr;@z zM)3NYESk3|h9d1A^I$tcr!axW2HGf&qmANW)M-zoC%0Lv45clQv~}Duw|k17owPbg zZ#+0WNQ698w{a86O>70S4$>~e?7V2NHSguW<7W%*!o#iWu&JUBeYje*Pz*nwWBMD@ z&c~%wKgw_dHJ#Z`471HMtwYrY{~S*NLjl9(2o9xAUi1X;6H2lL78SbY{T~O;{}zEE zBjYk-k6n;UgKi3D9U%vLkhzn;{K^|BX~fJZ^2W1KF>jtS%RsWA+$6_~Lexypv(_(q|Qt6lk< zK2!K<>;(;v%XdMpZ4=`mwqqlrSg~;}=B-?Sxr}eKBsu`wnch`;W2NlN(rz&mqN_`Q z;dA8B#%wONObOeEPA2_oDp6lsgZg%c)M<~SIyw|Bxry|E4jQ#{nAeb;=wSRFrT*(f z2cALn>Yh9zj1xwO7({{3F(8qWTlS0Iimuwf+IAGTB(Y$@;Z74GKOp=;6wtnp;&=kV zEaOHgGz}OpYT5dQ`16y$!rv1c(9Aee<*^!UiML<`r`CH1kHr^GybASwC!3+1p@*`| zkL+pcDbOPn5SKHBM>(PVjOye!7L)Vw%Ta@XZ$3g12;)uYG;u?nuT{55i(`7uJ9#tH zhh6#nD_GFd#*JDIr`7kzFOQvyG6H2ucU%~$B5kg(_CcV48#4~jITdmT_e;+&N#g#w z32be(vHiHWFzM{Z4+~+&;bqK6Y@w9m63R)%N*OI^DO6y%E6H|kzdlQOEXknB6x4YE z*9O|tOYYafZf7=;+^E%!<&Kyhi|PfN4bE|+m^lk{n}7?_|kxem0-c0~W17HQh)nXgl?L zdM8g)iagTBr^s^@>zzltF01WUZE01=_Vc_oDT2Zz=C8>Z38AKvfZ>=yA)G!XiepAe znq~J=mS2{Ry3_G19Zeg}&QZKf0p!hBZ+%I&Q60Sqi#Kn^;#>t*rUPi+v=-}HThSOO z$A&1qqZxEtDPtp)CJHDDX=LawO>l&|R3!$)t5Kbo z8^E*?v`?hUS${5jld4(7`&qR=PwBUkwMV~k13{ieP0eix#%PDb8g(56`fP>?2Zjm3 z!ZVl^IA3qObJ8#NFaLFcA50rf9rezBN-F^oUGWw6O(jH;`Jxs7gWEQ}iN{yHhI!0= zJdiu0AVbqMli|K6l*MO`yc8FWI}f4Cp|rh705n0%zLDWD&LKx?*WUKP(9+Yt9zqJZ z6;r@aE9cfm;72p)jI!N2p`3CO@ag}x+)d-%OkceeZ*fbk0i@NC002M$Nkl`uyCla>T^0Sd2ntUcA455VS#t=PX=T@XH-SoTEV((gCrS%YOV#@pXBT=# zw{2=dQ(GIF12L>&y2_PnmtYz5LNBF+a8+yo!%GFlaXCF{ff!`CDME?zTm*g6mFP== zI3Q7nnj~$a`FoZiQeklvH?p=ogdz1YG}LXuDPzhppl>bDDPG#!ZN{D`+=Jgca5!&8 zGd}(7H&9Q5(vcLWm$ePR`i>K^Hg$}#TaPI1O(x2PwVFn#l`&+$^CMK|#=h{+BT{C|X5(>7) zwde3Y)%fICYiDS-vmO{8`Y~A`PgJmFjS1%wD=%b81)pA+#6y%8DxkGMp{9_|J$XVH z=N}nkMiW7qyj>xsC*o*3ul0q%;|?y7_bS*RDZ`|N=pcF;bu=jx2*yx3paEkC0Ao#+ zc;e^=wA0L^ok(Fb)z8aT&BucEYq5k<#--tbV0veQzwOwBt!SjCBUs7E9~HG2nizoo zsX8!!6JoI_?G?iq`r1oa!7yYKgR_u!J}pxmus!J!DMj%{9j7aEi6 zv4UPhFY&u<QE+=^2)1SW zQcP^&m!_ZI2Phim-V4E;l(6i)UC3!a=U+7&J4@I1onOAq8$HoZR(q26H13@}W&%+T z;A*B4B3$KPIe}uo$b62tb=VTha7XwI%4=T092z=2!W1MG zg2MMty8>1H?T}BCxoXJ>(^5A!|0qxlcc zU@qlhQ#nET!DK?&-?^rn7f$J9&}?^(Yl!T=e!rOEIks%4eHdpazyIoC5!ij=0Udqox{nJb!pu7auN&HW@rzmAkG2W!R0W1a2OX56emTz>x0P+sBY=zmDdQ#MaS(MR8#M*Y->s&`_)z6&@F62?iHqT_%K3Xf(!V2Vmx!7jSm-qi7`nZ4NV@aBiT* z7TV+%VJTQ}m}92|iPD#Ec^PXvC|RgbSF&GzLW3wr^xVCsq2CqATnyz#23q>wA#uo`MD~yg7v-+@z8o~AQ)WF1_UE>5UOZG z2h#;TBgmVr;yF|)vxd#ASUtUwKT=bVna6(=Ge^D)u>q4zwVYicpV~PY+(g-*di!%d z1r9m|xHwzAmqa2=hI$w647c&97!;$pl|S_}{9Wp1O%g549Pm08X>rU*xs&CcJb&AE z+_Zd^2{qEfcx|5^KN83FQ|z^_xoeBGvA){-O#vq<)G${OuMvOP){$cn>m;6BkYuJ4 z$^ke(Iw%krP#eG*R3X1>B6%2bq4<*lKAJB1>-}19Ncol*M;AjcQcEHlnQ(~aV*2?m z*m0d%fB2DW(V!vY&EItQD^=55nwMk!_7Qx;Y^EV-fD(ud!J1@% zlK&+rB}?+pBCs4{ln&Me5`nIXfxB>q1o5=BTz!25M+r+6Q&C8yK8RF{*li ze_>Sf9@U|8)G*{rRT;y<)C@sX?`sF6x~`#9>@P`xhMAq3JG$HMPFM~8zHQS|v~639 z?c1BNrGt{d_Kj%CbfAfU>v+j}?n07TN)ocov~OTuRieg?!6>%mD$zg{$oMy<65bLc$sOJ~sVBA4L|E}aHphegeaWrza z7ybtP`rjCTgI{XD_30k~Cw!PvLxMwYrp$89v0ThW^UyFKwj)WtleM&6pyQ_ z!i^^$h5k{7+2Gukce09~x5eD!`&M@|Ch~o4$B1QFy81=tLB99ZBpNp}Ff;YavOb{F zjG0ZsICokM18bGq)Pfmt9|vpvxDoXZUQxd|UaGt5vz95dit$A`n@xHC1)9dpLT)qp zI@hg0GJ(*@k%(RNUW6uCfsM{9#{MZ-a43Sh4$^z6qH<}utLKp5LfhQ{L~pQ)Psdl}a&3zoQ6`lE}$B zNo+|SscC1khDh2cXAVjJbT0~9d2gQ0uoGEIGUd0lW18wZ**9#->dyS0s&8d2k;GBQ z44fzW(Z)sHA7Iz~D7UO~Ls?2JPg8QnZcr72jtHbmeKvh~q+@ zG;hmLg`B{g<>VR~`ut-IW(Hsm;JhQsn9|KE`4pPQU1On2anF-|CR`7m==#jeX|7=b z&Ec(^5x?zbq+WcHm_PyL6qKB7`Gk`Zz4%fD%gYUZQMHSe9~Uq<)Uqn$T!JdTGeAw0 zew&h|{oTqn&!ds{0PC}D1cq5OQsKO%WfQh+T!I!lZER)ump0yU8}GA*peiN6N71l4 z*r>BKD>*xrYec3{intyj=;D6O*aHgsbuBx8+VP9_75bxsm>x~~o~i-fSFxA`9!;=c zF+&CUCFmW;GY7FQ{RsvK4j6~AeFtMA2jbNJlQDAA1-e0J5oH86bz8ww*)#Erq8)^t z4fAhGj{^A(i)k1)MrMO{^Vfh`w^&=l2xr;1{s{~}oKuUl$#6!V?T$rA5ywoN_G4Wl zfe$|Y64oW+OlOwEg+uG{^P{JkwN3L^%)?z%gw6F;-U|in6ws=zVQu?b-${k6T*yz( zPcZFW5-lAHWKAhEgTPmp2k`F65u81RDr8O(ou5<03d^3%crmNL;VpZqfd6^_DL{hT z9?SPxer;0!rRKhdjWmwsaf8VTQ(}Sq8-n zqb-@jrgaOjZsUALnP;%fR6E)!joVIdN`c5$PQDfbJkDVrLyX8yDV+umzYm*Xu&3FX zMS>>{rD|I~)RnAlEw8KLHMI=3*_SEp2J{(*K|>G6fPN#!{V!YZz2sCB%eUy?mLit@<|GH7zrg7Lzdpn(W8$>bmqkf)(W3`Xk@!$6*{ zzqgbL47f|FVvZ`{G}YH>-b2A?r2wW}&!~q5K4sgeKRr)%N-%}b<_!yXnyC6s&L{nc z9ZA!v7{UZ~QUe$XB<~>;D+Sz)5eNn&71VF<0D=6;6lvPBlqhQ75u`NHsI;ltcF(nE z+1K6Ert2C0y{AAAQh@V;1DBJ~?xIcTn$67hrToba{ejc3vX#?>)hXs&{#K;L%ty9C zW+P!Th)36Lz&&f%p^BV;8=X+TIe7wxRg{}GO6TrwP8Vs@eYJO?fa_ZgY5QK%!T=?N z8Qep=!si#Im;*U4E##szF!?O7KNvi_Y{pE4PdkI!M`kwS1~@>|wNjba1q%-MXXu#D zuEOm0Ze%f299R>`cI_v<8 zp07ZsIeJp-*|y$vt>=IY6VRIa^Svvso$j+gdI}UY1+=*8Pg^z4F}zBN;d9SZD$bEE zamYDW0t~}sOe^F_)@7_0bi4LYIm1lUTDDnpxP@AY>lZFXoQ7gDB{_pB@@7({Y`l|2 z7lOwfvwIIW^87rAVC^QVh0nm@a1JG0>e1gZKcDc)WvKaSrk2x!Byc>@#y;OM;mn{g49v_)_3T@2_ttOR zDQ~y5zxAq}&$gd9{0L5WAB)h{-#yJAJq3!60tWDLtkc#PxBsVhXBHSv8V#KMalV%w zVjdW39u_e`)O?klq1vGLe`eU4SGKg66nSM_f3BW52354NOz|B3zVmuebUo`UybA>! zz);_6C`*GysAquT()C%~|7rrSuSyx|VU(O#Qo{+1Rd~*{D5eaztk|fKIWmX~P4U2R z7b17V$DL@*xv3>8C`n**6B2jciqxyGB0v?g=Cy=Ov9r!b^jv0jDx=eho6GqQ+v#52 zP}f6w%aR6%Z%0T?U=?@Vaqe4}>|MWc136z|kad=d`LgemYZ4UZ!Ia^q`8ip@t=ePNx~>IM1I}Yi!#^%ti5jYt zJ19B)=-A;nypFLANEeqod0*FtJwDX`P!JSweeXb_ypWaMM0}$)p2K}}6L@Z6itnuo zS?Si4>h6ML7_*RR?YL*!(4!Qv+|mL* z2mbE<5wMwRW%0tnRlp3FKbgz37S$qNSPf&z6I{Shc4B{9u?A1A--HT!CnrdcFH9Uu z)v`~K*Q4X|AV1voyN0fYy#56KBraUUbaxNDp1_-{8OV%Z3K%9RJsw>j!5LGdIChlS zuEMktRPbGh<3#t2xI8o8I=A29#bfnIA)%v)KE?+D_Yxd-b_pVQEU9_K&jNf3^XIe2nIxKps~k+j*R)$@^v@@z}%k8cH#C zA+r`Sc41`76htq&6ro|mjaER{IjO&pe5uc64-UJ3O!_By3iKWdNc>?WhUA#>RtCGi zma1eqosb0M=$U-#zf$i?;7A7z!y&D>q-g15n0vnVViq?pU5Vvw?I@?XXmnKtJ~^hr zNEZ~9K;W?xToi4KduPRW0v7B`YJ!dWSbg3a&)|_aQh0W85?h#-E5e~I)v~TRc1#H8 z9a)AUefTBeh|O?9km1L|As{uM+)cpii|ExSWXLk}|5Ad; zAW_Ci#c)r1uKl&>UJWo*o7rBelch?&eN7W82@DfVihISF5g1cl$-K&0hIO>esOTEm zi!1B^p-5nXfgDFP>y-!J+AP|Kcg&De#UckOxUzb$J^yz!rKY-*tmkCnvbdzKaehhcno` zlPY$e|Bjy8V;38yBfw`k2+*EN_CL<2&GPbV`~fkx?&Y7>44D67$%>YFq$z9YdQg;V^rN%Arhw}s3+gCk;+ObF7W2=0*wZhb%FQ z;B`k|vZDm_2n@5Wtw`KE3#q4{GJwy9s7WGv_z{R)`ay*H^|K*r*giR(_+t0b5kaidw&il5|&7f##e)>QofSElamTS>Rc;AIq>K8;FXT?xJ)SV zOd2;VUWN?`X1-wiCf8Ntqoe8Fs5bIXp-g-!EDER(H5LQ34>WI0^}2Ww6Vy@Gi#40F zc%7i|c{-Y`-IAf|*v2Ac*gMoRaP_pYVVp8ChK2#Qj|Cbzx{c$J9p=5uMy;oN+4ifJ z7zfJvp1?4(Xdx1}{}buu%jt>CyymGS0#((Bo_!9Y?>Wmz2)jC|biGecx2Y$e?0ifd$&;fveq02j2MQEAP$_2=U9VB#V-_CDfzuVt>3z;+W#Sx6 z+;2m=T|xTeRFn>FJ;& z%8a4)IBRgd0fq`&V?l?m@}(y!u!G@SAZJy^>>pDpc^Bufr8SEen9*eJsti@f8MI6A zP3)(zG%5mx(hXiA3N^?M9(^xo7Xa$1!N2;bejb~Uw7^=4-R*xhyUa$;3=>#6fhDJ zok(UVFeqEXd#(W*w=y;uzoR4#m;As=6SF#pf0ldh%iRm^95EqN@@)_}{N3W^Si+#! z6_hIVWw?vajUQwDZ-f~nQ|ijN?8W}IJ1OAk-Pu>252GUj0iYy%0&1#gI0XrhY z{93~hHM5^Wf+_H_t=kPS%q8Loj~IdIg$z-1*i<9omv^$Hh33tWL~-0A28O1cY_D3JW;>)Vp1olc zZdx(rpyj6%g(KhUzV6^Jn`^pO>w-;b)f7 z+jvoP7Moic!h|BikdPq%CsJ9|R)laE^B+$h7RHey!zhaysBea_MUOkAh5GD)MYnbD zZg1S#=lYe>LT%fElosYTY(V0!+iZv$1+CV1%8NYx-H6V(kg4!$&HU^dqDGzS`){{o zHXGgD|Kh*(6z~-A6xbaF4AA2bI+{YRdgp)835DuoY1~E)0M58N|GT6yV!Zq-(&Ctr ziIc{5Y)_`}%Y{pkq$*i%Hm4}2;qaj(N4~9UXV>>d+U`QFwjFkksoRh7@oGdW{B_-11ot2Sn_p(SIonCN_|jAQgLS5e;%?;pZ34N;6A62^#r%7~$R2!GgT zX$ZTq;Ev`5|8mGE;OK7Vn$*Y&QNyQa6rx7b!u9JIqNaiYtve9x+ZW{*y%(Wlj<@yW z`9@NxPj(P>887w}|GI+$UUImD7XF>5fTzH|P{49Ya!o=b6WWoqT#x&NAL|Hv3 z3{9WPASMkB<7i3>ht`D&7H!M)ESKs|=4$#ZJmI8F?${G_=&jFm{jZ}WhBib^8$;CG zK~?f>tHp@WIF#PV(IbvDd69zy>#Z#!iRMGp?BB@o;Bf!w<ml66_E+?YXv z;bshQG{BC-(}2S)`cP#&q%MeIloC!J8p0$>31x?92e0irj$~<=>5yVS>yrnAot<#V z-?(w0KInW;iD8-{YLYHQO?d_JBJBtjqJ|D9(bLZ~%TcG3O4n=o-j}^oz=OlR)5xFU zDc~vKC?KFG?x~_h_OV|BiD(+zs14GwF$6zEq{PlvRXo@Eh zBk~wmRe?{9X)sbnOAgDtJdiMy+piZ56Adg&yRINjz|7r>zw=)Q1FJ7tm!kb(7OOXB z7;Ywu^({FQY9_+inzA31wpfy0nuD5*C1dJC7+oKvx;VtRgkh8J&ODLlSiqr&*nw>y z=ljx8y{C?yKrj#N%siABmv+vH`|n2bk%xDLsOjiHc;ZB6I#Gz4QS4KyoHbf_zV4$x zKQH&MCp|daKYIDIJq3C%1-QT(POa1s0)EX481VYf1crPc=VE-`Il!2c$Yc4w1c0Gs zC>AnET6H@eP##^k0W~yMi&Ikgxx>a_Kv~Qfx%-%g2R1DnjjdFui&rt}O?bwe65w$- zZDpvLHyMz6-r5YB8IZb-s^T_UEwCGmKa`#xB`{Q;<4`b%eoSFEVMvrJV@eA91W{8# zz^KiO3dgbYUe;3tK6eiuI#H>;&c7Zq3Uuv{tn(};(95n~$q+RRXuWt5jYg?L)_1H` z?ED#soOzB-iO0}c0ctJmnDU_TKs(KY!vm$9zs^&jH&LJy7?NQd@JGgZ`}b@n6!F|a z)xeBz+TX;}#aG33i?lfAWYoo!M6F{?!y6dWP^!jl6i2;x_z;{usJ>HbsJ0ewYY$x~ ziLQ2V=%^x9F+m=`S6o4XYz`uN5ofWH&Laz(GFZUBRa6xV2)e6u4gG=45UXV9q?&SI zaGwyy4hmr+Z3qX~+H5BFS>{2!-d-)x5GH_>98y40=Xj}-rBP46+VP=&wUS$Px@-$2 zElfT2IPDAXMz*buA!?#@I#Gz42HF;0jPRr>W^CBlg&r6ljDGas@L*8U-@{X&H&8%~ zG%Jr}?$}?Q#Y`wKStk@a0pNmvp%cmw{+^dA8YW+yL~+2d6UeZ6*DqX-KKS-Xt}MO;bjN}7t@JkX>$gRo9PYC zQ{`1mOm0A=YzH;8>M0Ez)|Y?%f@q-q;Ap0>E01ZHV!@@32KRNsNqe0G9XV$2F+86I3PNzNQfsG-ONA3FQ-G8!)P>vNJ`V2ZieN-Iq>=&*32Hps-q3 z2aq^77H>4B@cODWR&SF1U=DHaKcpTe>GQ$F&1D!dfbj>1Slht?H6aYF4(9KrXIt;$ zpaGi>Alf>){&fB1j=Qh1|F$P6uw$HPpim^N5H;+^^z+XmPGFeZxCudKHOWwd9~>~i zgs2H0b&Q4nazs%~Lo>d3J&W(-cixL09KLfJ`a5_Elr{yl=(@s17|;9(n7@LtK%@uc z-;A%&=%cZ%VRmCDxNx= zUd2Nr=*zG(Hq?wgQ+;G?2YHS9A!Onb>bJK_jXYQoWec;YAKg51z%aMH1<5-Z(E5ev zS!vEaW+2L@Q-~gYG-4OO4}pIDY={~SDcU!?dlY61i?dc992RH4`C5Al92yF6;pXaV zck|-An^)2a1g8jwn(mD|C$(@*)L>VlbxUnUxsUP0Lr|uL;;oC1bU0=S;(>J~Mp@bWUNK9uxZMh)0`bC4!o@@GvA7$k!=Mx=w?gx9Wxxl{^ECt+rGr&;NLXH`! zh12t1N8xJO0-aZ!%@?Clalx$TY}tlCEnkTmqLvm$ zQU3gd1{_{jYk>&Q+Zm9UOMB&uhKJ22VnLdLat2`AA+ds)NuFJt#QfE1N(}RkC$!xR z64)gfYGy!nfKDVKOc)%-=z$>&sG;c445GJ&nGu22O$HR`!L%K;L$62&T`wFlJAp#> zVP}Y%L;{I>W+D0H;|yd?kK>qCBS#LKjMzn&AUJ%a*_5g8cM4JC21PHVWp_5&gTwBm zuD`dZKrvIma(#+_$iXEQeB*xLh1t~kkr69&&CE|Th}?+0RFU7g^NX;}2I#TgY?X12 zNK%q<-MqzURU8#!m81LC;)*d+Ckv2RHJAgFMcCYfR%Hi}<>2Ao%kEEPTEU>ykG`40 zeA*PYa6cifax~9zbk1nT9z1be1moyo+^r(~`JX%M?#}Q0!oMka z3b-+%igpcAlUcHu(!!gOUcQW=FlI47^COp?cRnJgp20PT-?@-B90*B95vn%)3cSTYJGz9#N zcP!ukg%ZQro3=1TUdRMfzIwtK)J3D6nNU11v7k+K5Tlk>(5i@YShO~c$LFW;#u|nn zVdfmEl~v@3GOz|1PYL0>#z*LB9O@i08g4H3plm()-`v-NLJJ!0UHogYQosQ^9W`K# zXECLP$;Tc>;-LpjXqrHjATXXlc=Ra5F1!q(@#D>Mqfj>96Zz2gya$Jemfrqpo&v=| z0n43jUFC%G#D4<|R#KfzMlFwlnRG&_sG}1~l3brUW}RQ8#V{+@UEomO$%|WB@rR`= z8MY>fHX6fyaO6;&THnvw&l%v-dnFe;F?enoL^Fl-359Z4z9Eap-;CoW+7v1;afGU4 z?IUDQIBrl7M~{x+SpHR&xv?TTxskAKLilndF~v4EIJCXgVn|AOS6$6DAZ>Y zqGr_!Bxc=?^qX%`q#ToMSUUp5&O8f|b1y)kyu33+jdmVxp89cdXhwkthlhsF{wbaU z#YF*eZT%_J$>vqSJ-;U?WE1FViW5(ymD3fT+;>=Zno8%+t4Q;$4| z#KV*pZr#cdHRZ@K{at9-FnS_iityyAc4SDpn0NZpTI9#Yp&bt%93EPF`=@yd6g~w6 z^jwkj=FT5jCzL4PS2F;bQJW&~stKb_ ziacdXQ79G%kQ{k>%h9!4N2%ULiv;a4Us#qVFic?Mc82~T*H>gCoilE52xm==VjS%X zqmnkWNSdf{GrgIPy=AI+4$ShTcX=Vv2+*B!`i+{_R)Wyn%m7#vuS*ab5YI`#xZnVimdz)<%v-};xH zpnwO5JwbK_{L+RaQe~8Lk>XioXQddvm+;HrWt0 z$s_|>SE6jjMF^jGvKd%DL`_fi+S@5%Az&tP_ed zp`1D$n0j8`JDJOd5Q9Y#MM)pS#8`g;ztvPH|9asvIz$Et46`_E$UwZ09?Ftt*svMyL)ZKpe<$}h5<`lNiWC@%k(t7y zfQy=X?fk`9+18E<#x(33i{i@@$98s_oKzh1>-@4r-?(;)M3Sh=xrADF-11@vo?eh* zdOO+{l3vO)ebV?SW*!|wUk0x>p4h4gAY+-=uwC-9vl{8ulPE73LHub2n`sn zZWnmG$xJ9uKL%719Y|_;E}c*Yj-ymj#^O{Lx$_TY9S_p8oFvPo$h&>@THM#T9#xSr zv!SH%jmZbWq=(#Z6DgF@H@8Wl^fz8Pc*}RRA+7 zEmUR`6-f)FLI#Zybssx4_}Q0|r9js?VL+nBkL(UQ+Kt0WYBr^X>9hfcnRo|m`n&fc zIHJLVNUk+LL`~rj4-XCtzmI$kJq5at0+yRwb+SZ<8H$Q-{=RiWN%66h8i4m)Wr3lr z$mEe=tjW5sO5Js5!_;_lT1*nq1hH~^8-7D|vLs>xhUZe9d>K8Ijgt!LVkL{E4|KZb zMYa;d0G2WJ-Azx%v35&_aSMY~BLh>0hw;IaV$6NakR*8*WU1W^K#=Q6>65CvhGx;+ z*8xM*=aLp0+d?iE3*JQHmYZo?xP}fV3T?owIgtp;6wvz2b7_kZHRCJKn78rC3UJayj*bIF+1CB*9QC!$WFgS} z^7179`g8*ANjmEgIHxEuKJ~B&-hV=w5fCZOom9xSIQm%0q5IY!OOpZ)*cne`0mIf- zBp-QzDexZSt}BHA9oce;I6`B_pzI>1zZ*Nw1Ud*X{hb*+B#WCMSjK|D-zkuGQ;MLv?bJVCSldV=%s^+DHurka|-Q{uTdF@80`r9QVyh8r8DwEk%k; zj*H?wlO+wZMYf2(^im4s2a9YAGxOg-;_g|wmg#a_3Xyd7I zWF(Belw`%)>^*|R43WP6eDM_U6ewW|5QA5z@4p-Ir=Mah)Hu`M zl_5>r!tC%7h+h0&VB%y3MCZD}`Q&X4dVah_Jv0PpqiJ{EdAWrY3+S_X30ZP?bD_ zjylN%UVp>#_+T%Ss20uO?&gYSIWJ6j8mg+|#-P|?%bo(g`A9Nrkz}>Vq1ip9*cnb7Z3M3e^=0B)HUbT_7g(Sk% zalqFeC`oGkb=6w@VD5YGG-1NT#{|w*GB#2=_H<7z|1g2%L%$4PaG1) z$4@OoZG{&M>?;jR{yf?dW*>e4iN_wLl#szL!(rqoEewwujp%t)BTt=H@+9bO4cwzh zV0V~tcXo(6Z`bd4{Z6}7w~N@_eR~_xeS19xdYl5<6XxiG8j2xs$9F;ZR7wqrm|_G3 zmwlhoB>gU=$nRt`RHQ}ika3J0P3E_>;`(`uj59@RI)h8;nSAb$dgGj;7CI-CB5mqE zSKFq-vmT>=eyRhr7v@9Na5zjF7RDz}D@PSm;AJvdv$hv;_wO@J{E1yCz&T^)KedLM z7m8b$yqC6xD^^fiXl+j^RYL5na}YV>Yy@j+>90nIHVqU%Sh^6hFy8Kp&-S%edjyB> zh8iRScl!K~fBXZ#``z!befxHtfByOS&Ue0JK;Msk^dtQ9pZ~;!2@`O`4L6`)zkcRC zo%^wmeGH%f{O2)${(SuY_rJ%_fBtiGKY>N}jD4-FKhIOZQ{Wv^fb-b^z8r0W2n?}w zHt^&<78u6)+r<|HqmD8?ov`xFfGFG%A-4TC>F702ww8I;qMKrsxD8571wrCz?N@xA%L4rUjdJ~qLu`QE6J>4}_TmXn0y7N!`;ngOkIv?B~RG$3}-dl8y2$z12s z-|+?ZhI;M?E+HXeNNK|Ds+^;xrKVJ__pZoBO^ zJo3mR7(RSBzW@F28%GxX{=M&g4==v>B06Zm`rrTk-#GKkGdqE%h~^gWVkV%kyQe@= zQNS$Vln4em^^;T&-O123+lUM#UK=$4IOB6f5Rw?O=p3D2WJT+WSzGdqO5Tz;tyqOe znm3@5-pMMTn=~3j=wT^aM3+0cQ~{8s2c1kv5B02oz+axB#Bfm(_hl~>aPuU`{#75lEGP2lPrWt=9l!i3;*UOJy^u91s7j8VeGbaM z@Kp;8Ii93l2uQ-Hd*tWaULNazE)ojtQF7?&FJP!aB#)PCuDQm2jZvdUVe{tA=I`{= zPdC!SE3do~S6p#LCjgWLu}_~qxa+RFP)3iYbI)ZkOcveGFF2i~^lv-`4h;pgn47|5 zkm}>-ZUpAg;Y6CEECImzp9SiM5hxNY28kNn`Nakf#HiFH)?dKz^{p-VuSF}E1to-S z^uE5lVHnOIGT5v`vZ~X1q*97&l3QzS(CUD3M8EaL1fE%#Ho>jq$t(^Z8Nuc6Du-~h zGM-4%s=(OFuUu?hZ#vSy?3Ds;TxgC96dHgiUcYez68GJMjRJ-Od+|&f zKb^N2+Zo9@#qTE9R^hV@cCFtHQ{y>C>QQ9v+?O740KhVD3zYADErEyTI_DGF7)J21 zQ!7xeXcrtJ9vJQmWsCP5jfJk`!buC&qSWlC(f;e}kmONu33(=RjDH%t@M4tx>(v$* zYJJe$5|GT3p;YHj@%Hk;srg>Pq3d`JsB)@d1Pp)mt6$-VKl~wTYHCa^6c15qXB}gV zgQRl>0-t*7DU7EbqNIik7A(L87hGTf=FKX@#dn+EXR z_AOvTJKyuFT7tpLer%EbzTt`tyfZ=(h=E|KP_90*&8-9M2mu72k@1N<4|7~ z>jaNFzZ6SX03eN^>hASF3ID{+TV}YS_Qt=YQQXCCC+SJLs6immC-t&y4W%N!=bn3T?6JohT1=ZZ4Ua$mxRDm#ci(*&F=B)nJYm`=NTT>S zZ4b{o?>ytga>^;EV9=mJ*tn4)Ggx$QUpxgo1-h96nrHf>6wPERcy$&a>43bGnIa7{ zKMB;2mnh#>MCTSEXwrIO))XEi1Ve9ZYr&06SDN&A?etE*Y~&D}HfVqWhE_smnOrwh z|B&BaG;j=@J!!i9wN)wH{48xuSs3CG!}KMP8L`XjC#U`8na{ zhB?~Z0y{p1)WZ*={koqbIp?a+9L8ka#E4{*^RG~ zp^w#Hg9Cz4qG9bIcq6;wj)M&^;93qHchm@jm8Wuw^}P z$4{t3A`lc7oZP@@$DiSQd8Duuu@j2g>1?6oo&2+TOR$MvmQl88LQN$;JKn03rBarc zaw#%7b+@4d->7}s)UVu_#{WK(FkLQP;J`iseDd@P^sfnWKVb7nTQFIadjd0T{mbqt zU~CEblg0vHBtkYDf%KyJ^hCZJsf7y>ASeuwuV)DsqZIl?&$$4Bp+oKYayZG)6*FJ- zm?GRb?w&gS+qa{@UV~t}1vJjUmjZ}#LeYCICS%8r#lV3Bk>qaUl~-QDa7qn_4jE#e zr|T5*M#t*vYO_eqn>P=&bWm~Uyj{ioZ=M340tbl#idQJ`r~Juxeh0YuCMz`zQ-%Eb z>w&}0;_s9yGP{9MGasbK9{StlYzP)Iv)?XSj-N9{UKQJz=Uqet&- zk@&8PM!0(~V3=!dGpX+q&&>uUH&h{8X<=}{0F=$R0HLFgGv*~yD+@6Cv0=#AD_`~s z4n-9Wu01WJyZ&YtUk<`uOV{(&6|iwxhNp?&eFrjY)*w_?j;wMYGpKdsxZ{}RR1cCybS;L zO`wgyPrFZgX0$??BTAylMHEy(Biv$eS zK0}-0YUkd%#I3)Q7>W)${PXz^ELh8sHFU-yM#iNl#0U%{yQq@)yz$@LGyU%cL;>+< z^?ZJO8!u$Om%~YRT{DvRGd#^pFEAD%u_A+6oA5NF#vpp`43qj!*E*97BVp7War32s zn(3=@=qRvPaOf6a9o_p~tGB1$x}LwgbGrV%>plHvPXSMXcR&F#2^FQtQ)rq;e+|$E z&mI1S2_uPc^OPfCj9qXMBB#6uK{}sw0!gWzb*=u~ z`oZ_U^iB%w6&&_XviMee3KTH~gcei;{A6d?JRi8{4@xl1bQt{Z)ak%cm+^OslME}+ zArB^XU6B+qMblCqrA~e$AMCn=Gi)5I;Z8bZ`v*v=C?=~NDX2nt@XWd~P zP6!5#Z6SeSWa31V`Yt$pq?tQeY7~MDPow#7?xm*{;ac#vMDjl#5CuFqJRq|9i#!Di zm;zed#VDjxW=0U)@qNZLY$Jo<_X8^#y5?J!0T`7s;X)BIgHEQ%cFH3&!Q;=UPTooe zD1bDc-pQY*ce2jYB3Fu%GPTW?0OL9m023fLZpq;9Pb*{%^GQ=eJhFcXA33?q>x0}pn)e(_eoPeUVCbf5dvMrIg!Q-e6zCBObOHtf2T2WI`8)9J3qUzR0o99`@yUEl zLxM)>=EgIb zG-&C_;4cJ*+u}AnO)d8U|7UsyYRYMMMmji}2{;x}clXFND%gi|9CNmI_N9>)67UCT zH=KF-1;ih?51Cb~j0&0V=~NCdF~#jpd01nUQNjsrIq+*~Ty_WFwT7zI2y>@m{& z2YU(}WC}QtCqN|bzpa^JYyOiDAv;luylD({ed@nbph#dS1*7zg?i#)=mSR8=>+QUg z73}&?%Wbgh7UfPJJsf8as<*(9bWk%LkSdlo?n^zL^hXVpJn+t!5?IolVJbX2q|hVf zy~mYfcz-#gNUFGJoAG^lmOrQGDd2#dnYJ+FVgNJxb|_cc16Wh^jc%);QX z;fT$+5TPlDo4GE}W#g&OYuvqhzE!>kg-QVr4hywse7!sc_J;xvGk5t*EVU0=Uo8P;~t#*UJb!)mJV=`jr^ zY>i}wk}8&3F>TK`N?j-$!bjeW<0+=cE059vC7H!}N0j0CF_C=SM+*!+ASl9-ps}8> zwv+Z56*7Tgj-hB$v!6oZ!TXTq21zPpBO$IRN96P~5IN_31R1Kvj2CVeV?Y6eto37;ysGNS5Bo0)TVx0-k=%0z)~WT=Fqs#F6}6irCTsh5~;7 zuJp!|002M$NklOwPQH~=Ld7kS`0TI-;EXTucYWY_LKa0>9Kg{rNQSqP*&eQ6xD@4~0K+$C zaM7^AIFE++$`xFy-pLN+%DF`EH@9Z+k7pA|)A@woFic%_L)72176iV;(nxnpH!R(q zQc}__xpe2!-OUmLf^>tlz|xI$mvl(SU4MVwcW`z;XJ($5Gh^+FOd$Y?bxA1IUpKWr ztY+?WJWtd1FkcIj9Cva+Mz~mBNo>7iE6LXer+AQz31*2GI*AQTn2+_DXKsa=XfRng zkP1qe26Wyinmx)={QtZX37-rg++@(eF1zXPewWUpVJE+@KG+@8-Fv6aht-^`72Sxu z8EK2UjY|6B_G-rCE^mh4GkitZB@9(m!u!G6<2RBAXqI2;=&-9)c?40u-4g74ZUDw` z`4k;*JRt*~bP#QQ=qsqZr+;6RmNRY+{x~||IStL)R{chK1Z1ajnPjoZ&OWjqG}$NJ z(NjG?RA8WCS&_69&`=Vhi`6GMp)V>&-efkw`Zpb&vb|TgPjmC#N0T7AlNNAN-xH>=u~o-4^C6k|-a;Fr=MtWf;G5IB&s$Gbqw=+0JQBJYMI|19crp@)qM$Avan zX!`45=AV}svyDQTzNfs|U!pEBM&N&+U?=56KEU`Pm+he0u=XJGc`x+U2KO+#R>DM$ z=`Y>*$JH0bL+f17ib1{PN@$SB zaCyP81Wdy;qI|Z?m{r_k{xO95JvlPrSFGcA&D{B08~JhE`NdTKA^kNWqhS*M z+ow`P_>Wh^e*rY9!7riXB+l30Wwp3@5^~i=Ih)~j2)kt0q1Xxrid6y?$+RJTPk+u< zvWT6=>6i96MC};v6T!wKH zF-ig^+5aA1g_l*5!cQQ60A`4|2l#tPfn1;DGA?>QW$oQqXLH?*NL zSA9ZHQaX580U?Q&N?e*Gwtvg58Cc*i`WAcnkP8wh>xPr#z>&8|lAb`OO$ANBmCm0H z)+~X8uzoW# zB#U70)?#Qdw(`t0NfPIEdA!Q`ijg^YEL+Ccbsei;cy?C$=~m{s{xT9kb~X+Q(BV&N z$}FA5PhVmDuS_hS6v>(;k9Hgmj+MzuCP^>KYL0bO4`q*^M@++OVeZ#QhIeIa12W+F z<7ib37mBwhl?D@DD?Y)8HWdR-%W*bes9GR_UU@6#69B}`F(Grz< zH}YpQo91?&jNGR#q?1CRo~LUBYWna~g&YDpCv8ma!JLyC@r|8q1NG7GJ7j|1wI!B- z7MnIsy5ktN^WWr)H)p2ExO4!#kEwp-&}*=2$(MlegmL8t9L4;7i}s>Dv`ptuIC%zg z@_?U|q~v4*q+#JTBnLI}kin^E*@l3S8=A2i!}u|C7|`;}*WHrt3e7#JgS``eP9DWR znw`wG*Idq|P-;)Dfpf{b!-Xw}f3$5hG|u6NpTUnjS;FzISw$T48j4ZXQSE2$LU#Mp zKY0{mlicoHFmY*MUU1A7?a5{k-m<^;k z#dE}v)o@WtpkBVLg6j(J{Wt>_YLvSw;*899;rlOH-$aQvhS(G&vbBq($Jn}n3|q7G z8=11B#u?eq0s(-%iCQcprQbWQ>~hI54%K&)2B--T3tXq@N416uHEl$oMHwN;_@=g7 z-NxtLA4kvI^oIrb+Ni$X7p@aR!?gy~10n-blZyOJaFK`|q1`b_2bzc0^3&sqw?HQF zB?E_`kq;gDF1A7$@3Xi#$%B*fTpIaL!Y#R&qh z%5#ys!k5(aQ->yIvulFNBvu0hDI!R!a1|R18|M=DXm-|I3}NByLUFgLbZ05d$-6{D zf>D$;$(lgYc0k&N`++oo%c-IwsHi*!+reOK1L=krt$YlM7^>hLnM8#|yl$2Pd_b`S zWH><~)Z~2od(1?6tI2fOL^(Z=P|4mVSMkEL-+FzH^7#sC4Go!NkKVY>IMMaDoUkDD z{VVMUIHoG#nBk_fSFiZ!))l_TQB;ko3Od~X-w_k}BveRb7$yS{yiD5(@^ZH(IQifC zs}>Cg52)+Q_;wDbsAcOJE*U#~2rJ4B_hi=#=WlT+DbCrdYR}NJZDw%Qu;U`Z zN$1-yy}^T#wW}EftNcjgg@F$?3f*U(u1QCdF z;p8w#a2@W9FoH2--Vx+bn_o0*pTpe+4ADdb`R15M5B(QT zsUJcWXCje=_WX9SV*7Pf6`kqBuQby2N?A?^^g8uYJSlpw+;3oem53lRes0enPE-eu z9{Vf9!aN_m_t~|2P(+Y5l;S+M`_QmEHqCU}SNTO$W)(KOmZ+Sa5lDf;0(@t<5#qDd zl%LA2o{@>dIL=WvAIgM3WftkKhaCsKmYr7e-b`G~H{k0^*)7>;X_j#nZ0#KL7t>{F zfWwZiL!Thb_KZ1{cv&xmerW3Hd%x%G#Iuct>!GN`?K{5)t^LXFc1%zg@m>J7uNPVV$5#&eYr7sVI z*t5qVz=dA7r#4?BS0r27pJA_E>U`ADacCpfn3L~k zHec6^3{%-{O60GGbH{pjii18nmDXA6!i}x=i>D@9tA0ZZjz{_UmPdnkTWvGvV6+f^T6gyU*CZZ8v-K-ndf!$w8#ziFA@|T`YAikZU-2Ii$A%esK$Rhr&HPi z5c8iI>E6BJM&`C#yEt=2hqWbu+bBAi40evo-V6?q@;2qSCZT2;DO}h6 zzq(kTf)bz6(^{PGowJjMXjaQ=9(twRYdl5&o1gMQueQg4D}bd3h#Jy}djk=rdIsS# zj<;H!&Q+iG7c5l!KW5vf`M{;rVP-ZyQA0PyeXU!gnzzQUbo(6sDx)?CX*}KjF2nEa zutE)})f)76L&@#8vAk?#iB~)u{-A*bJ#2`2z3kA7=jipXK4+BkgbG2RGL2sL_DGSS zPhy{38?3#xRTrvx>31dLOV62@uqZqc&6xZNtV8Ux5=)JC`*YcoKSud^74SWXw_@-R z!C(X*(al*58T=Df;V-F}BqoToje+3G)S0PAr!$eA_ahFm zWXepZweSDfCqNJzpgCa3ibSHlK1LmG!h zea^JJLT$asL}+g^Pn6QAv}w}qP3#&g-Fzu?!QOX|qO$v*HTGy)VjKXibnV0AMWknOt3ai9Enx0*4mo+GtnD?*nU5dD~&Pqr;{z5|qOTsj8r(J_o7s1FjR2tj{Y%EJW z7nI{e=ks1b^p0kDHfyZtioCI%#&-|3w!KaI{rYUVys`4H4dO}pI~d`P3l#qp>xDcgN);cyV$X+qSk~>YN^KiR!W~jr~`p&dZuh#Wd+oV zYW@6!x*RPQoLrg=R4sl$nj~iETRD+3$NBEB3=Yq&QpSt_l-DRffEWl4yn&;~kNa5n zl(1C%OzF02jS)O3DbAd%pxaj}GYw^5N9pAKubk4}#2J1tkmzu3a+a9O9V|!f9iPC1 zn@(t3S*n|+{@Q59yS~&lD$NjK^XX^Y{;uKz__hcXssLcc`0mP;HVs|5o5~$KJX!eg z#jT!h^2(`Uh;M=CYy1XH>^uPbAOY`MRjex$h4KXfmMO%JwSlG&9e&@5rzJv@2m|Sq z*Cq3msBJ^kywJJ7tpB<>$(HJAfn|!r6$pq769BJo0)X!53^>o4nT$BaK&#u%=aPDk z3Me_X)KSImu&JtqWM!XZ^t6Ta^y}J$JhmuUlFCLXwA=Lk?8Mn~S0Qq@XI1QygSs>fpabPA+OmE@e`)H zL95%x_o|2660Gb6iLIGQ!vtSqv0eas28`$dAAcpb@6Y-+1ZcSHqQS#1G?FP^9^h3b z2%(3CFDgc4z=lP-4dwNv&(xyQrQ-rvjp+OTZ;{l zk6RHa8@Dy>pue3S@N@it8mu3E%(mKveML!@#$vY|P3X9mH?+fjBq-hou~=ldiQ1V9 zj#gbDuR)9>CHTdZ6PIUf_0{X`;~Id_J=gmZ(c<$_NqPX73Nf>GM4{2sX<$fA-0g5W z->Oa;5L&v%mecD(X6p*O7hkWaoXKn!kmYA7x78_`wXjA;1CiGA5oueEB^Y-^TWWwy zW!WgKJyzKJCZh=amVfny+f2FjIaeAKcl{lmjonOV>|rTGYb3dZatsq&B;`ck!0eJR zYU}aTn^qxqWhPhH!va25j#FPdNp6uCDR_o-SHLEK$XMbhJ!h3s8|jf8xuoN(q2}~} zjT4Z6=O91gp3WTGZXWgwIG{6AFha1v{7BxfKQ^tyZk8Grdb8W-T|e7sOd_e8z=OG+ zVf(wr-WEyGQnA9z+zged(PR4hscxOkqIMo|^Ezn9*?)+i9WS|3PTHg4KI-27iRU!h zFs7b#awY#N&q6hQkQ})19iTD-)Dbsz<4XHQKuW?+*OA_~Mt&|o)Qct3_u?Lz+xz)k zVZf4nLiCz;W>Q7Y^guN%&dC%tiOr~-wG*e#JmIb}e1^@=@8)ki=F%!5Y$vkjJ6P@D z2nzipeT4%53lXdYcuRQ}xiNYq(b2FUUOdI^(XJ{bt>LNQ26)e18smQzW zbZ)`3?|YF!G!c6OOx0?=a^f+m_{^+Y`fls(1YUcV?1I2wlWbimhn#)V$|2JohVy=M z0NzO1SxG&a0JGHVq`xE=b4cmK=Jm&Rdpsq;Hn~!3)#eI=5U%@lLSi#XK}P+zVIjS* z%L-S$l)1DIZ)ps_fY15RC9^$bvzPIAn7&Tr(S+HIUJv*SbClQ5-12IH;F`)TwrDo8 z8-9T2dHeuf290Z2TRci-ev?sMjVx@dTo zZ30!d1$NVof~4B4D_g|9-T^h|K2F$j)5dYkuuB6-fbqKiS2cY&r@IpM zM>+3TCEWFc|8C7y`^!-z>!g|F(Q4^FTyd{zYsN%aTO~4Zvf)V*&aj4nB7CQzGpHFe zkbAePQClBWPoacA^kq6XF4@|u^)+3JtfuIr>)KpSRQ_nCtfWJo;WY7_W^*$`{9haY zJ{mRO>F?1NAIC@)LST2c7J}`u=!rZhBOcR@oD!!R@D#A1=Iia`c9nS?{Dr}5-@+E% zUlISLhzV3bJbes<$7<=r_8(G!ZTJNgb0qyrbXOqO78UXeAh4rc`T z7+Aaolj$>j)}uCwd{=;%C%orj@|O=5Y^q#dWKm`^1_`8gNmoElJR(>kHctOB*uj#d z3qjVPzkcFU+NHZ+M))rg8!?GczdqUOsQNs`3P|H_LxdMhbvN@<79q#j|{`|42$dXcT-1JfJdEaDb>-je`^q za*oL~oK_Vuf_wh)OcU@7%BKZnFF;V38eNM#!htYSC(l}p>Nhds!8PO?k+yQ)8wO6i zSC1qeEO6x3*s0LvU7CF&EzxTK;qD~HpHL056)Gham8qhXyR;1pB?u50`XT4%p#^q2 zv#bs!wyqsTOxi3V`H{cH zq;-FE2p%WTk*!y3^&awLQhb;Ydn61f5DsX#bEOf$rxP;uNINXzZyI~TZs{rU%xsJ* zUx9sv`E1dCgB8C0t)9UH(f2Fp8>>Fp!FB=jTi_b!4d?UcjW1xrB=Q{dkVZy>2M4#S zXjY&y12FZ3DE|7e*c>1o^(i2mBaUb((A*G1b7uSNFzG=yilBy1wlY#yhTh{@g)QUB za-1{uL{JJ(pTi0@=Og@ptg#KU$w&F{1Nq}&l;cQKAfW14O79VvWjeGAFFZ^XlHvWS|PA!M)?mFY}f+ro9twPjYwbJ~9q18gOon<)Q z%{cOjjl8no!hS_Y`lY5uFaV5)gJW-&wh$#Cgr_iU@A|&ccyG>ve?VOO0^cN72ANoP1N6$9*kZ?$)`6nD>nS=w0hLHPHR)SP3^N!thdk=s0_yYfd!&NKU zSYIkRVO;1slaE_vsb9X1<<>T|SfGplWj!(hyp~Mrw7GT-!NPJZ{kAypxkcM?8vYSq zRmO21RXUTuAzg^5EOwxxjEe6THEgBv?>^}eowS)vbrI(jv>J#&3QzH`{8At%JWWwK zhat}yO*OK7M7I1x)}#1HGLl<`_~-HM?--Oi!SF)ZJ@O5otrDd{f>Cp^YG{_ErB?b2 zp>4LV*bt7l5z}pUN@$~if!QH;CYE;~oThb2E%WJvk%t{w-#sp2Uez=!4sM4?U)KCr z)dqpA2gwuV3FUQ$CT;CuVG;B!f_!Uv*YqP>oMByEn&mP~Gjlen8?Sv7%LaOj^L4I{ z)fA~7D(6gCsCi|IxAnu$jS5$pm)Q1MTYX;`Rd@*561(3nxtF;b0i0j+VT>j^UHLJA zgm|Vn+%O>QxKi`JfBS=MmB`$V;mDO&hTFNBe9B=BU9tpKqz*Yr*wy1U!0N{cN!-WK z1{b0z)0K3^l^xrv5bC>#c7pg#6D2?sZ?|gyGT+{)UDrAFCKa9GE8p5`p_CZ;qhH^b+!+FYu zoG%xDHu4IpNzWuh+ZQi;?Hjz5zT6te+?{8z49$EODYuB~EG8tp_$Bd@SlBE_%Bjp9#3$SV%5{;8x#eT2N-3*o(vqA;jJu0|24 z*vnNEd;O>9NOmKBD^yS?89m&UKVKg+5P+Me>K8t(Qc?psJFhXo1jv7frhsShESK7> z#iG=>APkO;n#&Nm6)`zN%1*S^ProwTZVA%K>!eKOpka3J#LS`dX_j8UQ8929QyF1Z z0NhR0TvbXvnTc1X(}J5=h=#=9jU1{eY={YL_nST9zfU51AaYzyk_y?sSzwiHeI6)D zS{#EX$a@aJIzFjTLWRAh&_&zI&f_Ops?ba{Xi0}-Kv~zfSg8}hVeu#EzIP2v9hyR_ zXuaWaS9fP8gO`hkomehDPiU6vA^<^nVe^!K8mb1bHd|sSkvu$O+q&y&axas=Foh$P zx5FJ`qmZO9-q+=1zMNRlAf2BbxuqxP-t*VL^M)!~wK=AHW0wb>Y*QgkMcSPeqd~{= z;&CQ7N_E1*-esCo!Oh;jI3)(VUX*0#edHQt2Mf5pRUw=8*=X3570(caOv(wvjpUqeXVMWp0mz?|8OFi*W!ZT> zB6B#&EU*>6%>L*VGO6iOf^=l&lS}8H_MR8m_Lr9_Q^jDjS!IOoI2_CgS#dS|ENudH zk@C}5(;@}x+AIH6sp&%oUf;18?o7yKv%W-oVmcc7}W&<18O3?hhxq7b`W$G zjXM16gUof3_XV6Rb&zi!WS#?FgUCWei9*;gmJ~*c;_lCO zOOi9=ruYD`>ELp$^m~UPA1BWk@`lESIsvBf_-zw~WQ?u@pd~_PI7;|V045Xzu40qmu z`;s6qDUEt+2$&(Y7av|LC4-NMgWZV;^faLA4a!TRHqSOJTCcT-oPiz#lJ_U!zp7>34_j3 zL>{aQg@DxS1*?k@>h%9C>g^FBhJJpeKZW!`uCk^`PMibX$}DPU|b?3>pzj zF4q_Wnm+km_33jCmubR*A;S^*Kv@QYrYovauvr5_19|%%+ZL8H@|*+=P70c|W?%%< z$clfbnwQ1UkZJ|d6sNpJl&GJ3|B_f6Chpp2{rU(`#thz)b5BlcNLR4?42i8dH*80B z`UW|j2I*W}B`c7Ml;2MNGY~n79r~}g z!2&!vMBZN#bb}PO%h3S*gRou=X*J7Jj}vjLKeQIsV}%Lu88q|K^be}Bq6HEREUXF} zeenKhD%yAxKu}!59JXiuzj`6?uU-f+wwaT{|0g9rpgz`<*#iTSv#HO!p@ws<>@vje z(+IMRhMU+@GVJ!=*E-NpqdJy55;J+AjGxxGs_Y~JvAphqITd_8!FClTKPzzxb5%b0 z3qoI2J=7a2Axq1oXQrfb_g#`QokHo`0hP3Htota?|Z#0+tIBxEoiuM8|&f;llfHI%r_k)8EK`k{|*XmD)G z?lY^7MBk=-S>-R)O+qtyGxY4(SvBG4u^Vfz0*+c~2?pMew6gKiUrFt$J^vUg+S1@u z=(JBHc?{$oz;3V~<3Dvz-MKB43|7uQ>pqMcOX^85 zPO>VSUV??UHIn?V0@H^p0j7 z85^$feT!OlSm`WGaU)I={h5^Oj86R?i8xlpR(RVm!;l<$*8w7h#|TaU|EtRAVxAr# zY4H+u7z*wtI}UmZR4y96+l?k%O;NQm{u^l?S->2!6fw>=^{1Uy$6*)BytoN#FPKj5 zeQ#?Q*2QJO3VNI%`(_!GrwVdyn~%q@B#s z+}G7yy!{#oFl!p>jq~f0fo{6_)4m0}yv)@K2*a!Q-8qlBS}afh-(?dj^vyG)VNnj0 zqn34!lBrexI$EY`(uCi72bnv_CMf(MvMe9P&r&OMxAoT~ZeYu!iZ+ZX;fNI^<(eHBpPPANOGBJyDTn z!eTMB;`Cv)P;`C!ODd;4T00fLg&?sk-QAaNk%TfERh{NEp5l~AQsVy0pFeTDi=@{t zU#Z_HJXHOrF0D>vP~8)Ux%`o?D|IOSC&dyC8lqprVRMPAVpf0rhqJg5Dc=qEWeNq8 z|6uv)PiWVG|2{1Ec!*&~=Odmwi}8awM2XoWJ0Fd=7C!JWyoCtKquSUFTuKFBJ6(@K zWh06bo)riiO-7BCZSC>GE^E^4FXzClHWEKfqX+JFaQgGpSmE`lCuiUJ*xDXiD{u+? z{lVigKir7n=2(2V8%7mI{_*CD)Xt8l#VSymu+2U2!gOY?^M&4y{spc4yBQCcJ?cz1 zN5C^mq*^EW7M}yK#S3$Kt#6Dwuj<;*r-rA6)ANcC^Pl2NzH?Gig^AGUrI1!4!=du+ zf?Y>sseY(3Z?!sk8Y)(z*A_Cw;j>g001kn&-52$7l~q@NA3RTIkxL%4!So+wJDn!V z@H|JgcjQI$3&QtgPLS_T?XF={HoyIodr6pRaAe6qKLz>lo<)G5^YAOx-XsMa@i~oy zds8X(%y;*ybZZWi=#)loJmXMv_L5 zV03QOiqTMRgcuH6a<(;XL4ALhfBp6u(6GvXmLfiB4(qw#O&PcSyZ}$u3IY?_O)vCS zBj&iieck$0RjJD9u7a?}ymS9&Dp)qh21UXs*?F__(Yhw7U~OIpTFrkpyi=E@H!}Je zb)Q$*+XTRH8<(|-hj{2ET~FjnvaLxyP3=n^d{d{Q)=sdL@ntuGcAbWa`*+a?oJD^C zO!?)`jOK`z)))L?-}>)9q*DA-CpR)%$#oXYyjf{1NNLy!=>X{xa0hhd>aR%P$qXV?u%c&3vXL!)%#or9Da`CrMfnhxCtSwgNS-BKF!nnJ}e9D{wDjx$06fHgEn}oM+pA+N^BAr!1m&Lka|S?H@;K`1hXq4 zDj1p&hgFdGLE9cc1OR;cuC0L*s4m$^?dc;&Om;tlf&}=f=2fjwQ!IeZC?_xqvOB-+ z5Xw=X`jCN^VSQo##k35E3O10}CT}S38&FU#`y?VI@XEhg!{yQ0#z|cFB-x2%CrSU*K9WE6a;nhlpp&QNx?!I%%w>Y-Ul^QBD-00+KC6! z$~?oU--za36kfp$S1XMNNDdPhIxgc>Sf61j0dO)U*ei+yZ}?7SRwpTqNS1Enj@5)< z9O}j_-NM)NVdGYL*eslj;i=K~a-om)hiRB=RcZ5uE?PR3Y@F$b(~1laAyiyn_dC%8 z7r}-69nGY>qh>nxWrZ)X2m585AQ=plL9JbAIy)8T$-v>R@CGtPG<*E+;s)H)a6maf zK8cXn&6C^!$I~Arc@-qD(WS~VcnK-qJO+K;Pg%ooz|C8mSF`nKc=|r0VlYT7-XuaR2wnk zxRo=&h&2!|~!g8IuZynzBKKLE?7cZE*I%1d0H^_+3kxEyB6Oqx^pE2bd6_ z1qS_Z4fFQ@A>BM2&&O0Pu))D@dd`rMo{o5v|lr70C_eK%6HN z{!vK+_{2h}eCH__n+N2lq2EL5EfyUYRKLU{6mxiJ+VfRgTgRSGm*Ry%V7pyEg}Rkv zv;RTQk~7VXrfxrIUUuytPX$++IHnd4D- zs60~Om=eMz02fPmhWCSvv^?N?-WQ9E0}GlE2oEW4_o}cnd#3tDZ&WOHjy&ssw+kx@ zc-YQXmi!+`;ql=rQpQ4sN_hQF&#S_&h&n+g;<4ARx=w-5ex?7968D-~)fP9)i;RFIw3SO(^05X(B zg=PnG!!>tO>i^9__pk&cVfWzzIT%!i2!La?(+9Nu&hn`x*>CXR0ugh)gBzO*Hb;UZ z@Xsq4#Y`fWCbOTsB*N1lE?vX^!Mne^rhPwNWLaJabjNJ6(w)i`uKv%S0T=DX2Tu?A z6Suxpommm3>-}!WN0yaLndE@!@dfgQh`|YYr9Y z93j}CS(-J<)NdCWx#7j#H%FQMok3Br_+buk0mQO$ERdosM-~`2RhL_Uy5GZ8ow6UW zfo$_V8+sT{40;eRjShs1OXF#j0)uI~$AgD#LRTu?3mMjy`7xEdV(*wby1Z~4<2x9+ zW3I}pvWhZ|;>rB^>Yq%y9vYVLhUYKrgHgBmFCilnq+aFv?>#jO=c}XAQ>w(O} z{tthB**Ps)qkv-zp?(KGQD9~ST{Tj0I*jAgphj=2Rzo&S4|;t=)lYy=#-sn8v%+C< zrs|v&j4p16kFTgh8+i@?TvGHS73Savj@S^q7P7qRkShhPSB~TyT3m|j{3pFJfo&Mz zH$x*&suYuk3I0pt?!wOYWiyT~-Sk3Xzk&hFEG;EO88$6024QwSEID?a-b7Ki*s7MN z=6^~r7A*pXoz8-a6G0>;%87*wawjHS-Fv$!tv#y73%$)c-}~&ay6mS(5wvhk$CC38 zO_4A)9grZy_0WZu_J-kWEO*4fXFa6)>~e&fK4k9ZN*`nYc+(S}mmrxA&Ht5QD~!3i zeV#;Q*TJ|#b6%3QSQ{jrifQJ&H=L>9XB0ific#jN`jBuccVv3xB=Vmqw|l)DfSE}F zP>Dt6!m`aDI5MZg=MC^yHd6l6^~&h)d>{zZ(*Gv(B)ztZF#;RA>5scB&!RDcxunFE z<@YXczrH{Fnb!uWV8i1k{*BnWRp!Q7!l!Zme(HoG8V}|2ZlA4hf`vJlL+~L}mF&`Z z2!n?Rua<%dU6;GXa22Kr1_3Og2F2I?q;<)1G|S3Gn% z!r=M&-wO>4SJrW|UbFPzL+_*Lr)ESxsakq zZBy4Ak7nrqjFW&;ZP0W@eNV;-xS(||0wSy@^hoeVoPNevNa%NX{A;U%dz<_Iw4tEw zhg9KLtlt9vd4SRZ%(bF6*{AcjXyBc%IOuSb6O#W(n=$ zQI@_#RXWKc?8-seU^d;|mAli)9BvN7J83W!w8* z1~J>(OkR8ZvvycF5^Q{oz{74z+}n02rf4LIQ|I)v>GIp73VPwwt5wju1Lq3cUnvw@ zFB{bMpLME2`urC;rrm3BvAED^nrvZy{A2{eH11*wP}bw-{xPoA>90>C9j8|P@e256 zs674@)-R1MwU%-#%D%NbOOV{p_rkF@PFV~yxoL8CHh3S&V14z`N(pKl1(0}iuCF+Q zlr1x@cCmlWjkHzxs-zw%U<)V%c!%CMKEGZ`EXEw>z3{pbY`8ZAfANw^z5~4{xc}m)D*JK3qNhbwc5Z%-p8I4On4})mgG+N9Pw+K zAKEI9)n!+9(Sjd258#;Mw!VKCp!`V!BbbpD(v7iS(DZD^BcLCGH}3atDc19q<33!6gYV z%$6z+(13F)Sf7Vl9`c{`33ixD1v5N532b7V!Enl6tUn;4 zxu2Nj5{_|um!$Trhc8)e(L<@8f(IuzP50lI31X^S z!l2?E4fl>p`zMY~CZo`Sy&Wu}Q)MY~sb>SHgMr}cgWLao>-TU8f5{|uUh1Z5CQxM? z6qw5+93FSfbBs(H&nt~zn7%1zg>3alNQp+}z;>C#qSh3jRYsx!Hsxgail7qRv2M}X zAVT~n4du4^{>Ovw9~17RvQrO|0IuDQVBJ;J8xe0~kn2y+A{REB^Px^EIn&M#e};f= zuQpvox||_x80oJkH;Uy;deo3YaEt%xNm7=9e$7g3_S6zh?)JNX`1)yG-rQOxADYvD zI42kq#lV1Ftfv<^>GoCKLJX^jZV5Sdx_Bmy`swOgp4(EB5&DOr!j4u|=zU!b+sCXN zR{HiYSo;IP&Mr^?Bv~mM=g-+vPja#bytX(F4l1_T87z8cTnD3N>e-Sn8XC1Dfp2$# z=6fS)go192xMS751+O;@vBbQn?A_1xZSg=Md5 zFiC;Q-oKw!ASb9C-ae|tF$;T1um@b({g(_3Q+4jYZP5SnkBAa`ea_y!az6;sMFWPc z`hBbNc4qoX??q8dYVq~LWT3iz&<5{shLP9r>zn_zUlcx2UqE;w_{hfN$=s}}D>%#J zh`0w4ik`1Bx_Rh#>}!55oGh|~Y*GIvQWT9%UMW>sS>(pqcmxJzpAXnOLu14&TDPaQ zl+A{1luwoc&wFAOtaHA%g#_2FMt2}PTw5K!u1I0og9m5R#+dfz)OcuhZ~?o1bk>R| zL`GQ)>z??U$U~7n7O1zsU&sSJ;Y}i8cUZ{f)sw=&9dk75A6qO=P~9x)$_MK88)P6O zB1W{ketrmxqfPg(fT_uyC-H>bzRXTRHq0^TPXT1#+ORzQSZ!NZvKRI`J@ zzp-Zf5dVb?jT$2m@|+O785~%^nRo8?-1r^rSyZNGu6|#_8=+0VI80DUk|W#EG-Etz zZ1wH(FcogJqB%2an!xH`s|s;d>RuNp1Ie{(!K`2NpF787*Yxd$bW+QXl_Ta?#~-(` zCBZOf!>EWb&}?Q?s^YyGfEV+me>3^J#C}1b)Tvc^Z?P?qRE^SQXB^q{EEeAXSKNol z)}`)C6`rs&`+vWYOOkRxjM@-8q4VgbJy5HJ6OXg1qx;O>KSgAJxO+bD2ulDDK6fE| zsYzLhW_*+9k!XDs$NPJTBn6M*9m;0?$^)=pR|ep(jKPWnT=(T-U|;(AUxD8X+1E0? zHgL?_18sP>=z~CZvaKU)GEJW5U02iM#md5~!}P*4)y}LD``jk0EQ6+Lw19TGac4Qa zf7asEBN39!?fba2U;QKBId$0jVSG?SX?WnQjfJWDO`xbO-|-m$L>UvVJs>;GhH)#H z5%!Fgh_h9j{pwY2TPcq7a;Y&(K_SCbHfxJpZuWZtbM)OiV4;Gvgcbp*gL|y#tbJaY zxm&f!;Fnp$VOS-Cm5p(26+W}K!Yn%QRpr8 z>_Q|V*CfB|s`n9!ixlA6oBbV~eEs4ti5lMjSjeB}o%TU6jpj4)IWLT)qp`$g2){`t z5MG^gVXpfM8+ZIuYXuNk;3M`x<)No$MW?UWgIv7Fb%70{(YJo3traULyvJuC*oCD) z3EnOmHm0WqS6y7JGi^F;Xw{3I*Ng00-_QOumYCV)ePfL;rO-`n>v{ULd#I+Qvy~NR zpvRn*RH3}+x^e+`xbdCNcrkrsM-Qit03ooe{>q-2Vno~E+H3bTcAAi>U<<|HTtL^> z{=N-fOL5?1q4e;GHFM!EKD+qibOcdQAD1^Rn6n*ThP`Xm25{y8&c&UjUR6lsA zT9Ppe>p2u_%j>d5d+q%CVYwDCjDcjjx>`fPcC`E}k>&+;4tOhE5cSk`rVl*fgYB|N z`Jx1gKC(~ktHmu&t`X(6g|&12#$j*>{eAK~d)Okmx3Aeje&ZT*pR)uhOTSM(qdIcK z8Ta(WrC1A7kX(Y)oFPT3kn>-IDd)7u#=JA^%`0OZS_e$ms6J1YXnh;D@giC5Z z?R=N|e_efbR8?K~HJul@^rbtcLpm=h4N9ZZEl5Z=T)GhfX%SFL8l>|A(%mf}-3@++ z=Xu}vH^%2Ue;vkfoN@QsYp*reoO2@#g~9GZ*30+7Hc#rL*=D$u@OboUTc_Mi7F2tVkd)gwZv}-L31faxF<9EGpAq%9Ri)KL$A~L$Unj?YF zfaUyYMfl?-V)pO7U_>NUBHi3H(S5mez39f}k|b?@ZN z==_NSf68yH;kYIhAni(iRf6>5FIW52$~N!4v4S9BmV<3bn*|BxXjh*KeQwVq5Sgp} zv9GOJ^Fnw^)t)6!D~$b;p-0yBcl@o`;OIfEv#iR09SR+)5e)gM;5-%)`d&m3 zYLc?@=8Laqi=e)yBpxB!qBGa#vNu(_b&f*BA3Ll;w$H)`Zrvkf2lC8&RXrQXDyevQ zq8=$g{&2B9{^MQOQ6>}s796u*z&UMZxS^(C*^w!R2%x#+=}ofn-l%SaM}b!r(y}d z%|yz#T>4jSh=^o}G*d7j$qe%X3d&V-~$ zbs-Sxev2{@9P{Nuar4#Ok_E#j`ZvL>EGXxClK=ry3jcT(Nv&rRg9MVz{%Wk{4WK=H zCBWipr=sQAH4`XV`k6yYjx&R!lG)6ZS~7V)fpVafNDEkrH_4q>TO6_(NA@nY!j({p z#pf1*nykP1K?G!)UgFUG{#)Yohy7-OWs`JhQsc8T#;#8}Y0^h=0$=RA$C&IB{h+6r9IfBgbl?`kUVWmyk6;l=C&|$~`B%gF9dHa687L zrDG(Bqb6gJX|r7C($J39?+=}!PoyiX!e={Y z)IeuP$13G)AM|*Mn(ry?NrDVN3PK2cL zS1*u!#D`S_vI$>`U&7%n9>D3fiw=xI_RtkQK0e91z9?m@_88s}1-h`Dk(MtVeuOia zb`GteisGHdY31)FRT!GPS3S1E?R;xqS)6{~m1r=(^ffnJ?)uKnQsWfs^g}$G9a&p@ zBief8Tia`arH_o!8QH(Bh0W!0M~2Mj$sd`ObR0dGoeRtU5L;(&xBNcY7uo6#lu)iq zQutAb_J=K5bGGHzJu8;GO?>x9Lj0%$HMavL({|=SLO9*1(o=p_l5VQ_hK};0Tk~bd zDC+8C1#&fsr$5Z#u<3N7{khTjzRKE)gyRfpohFiCA0B%?dFS z32oLlFPzv_dKbF@Vq;B&RMd{sFAX_Rmr0YCVb8kwsMkMi+$RlGKhH}!}C(eBHtLmId*3iR=pDZ{*C50hkq8O~m4 zZdM{`7M86yo9=|f~dPJ-L0iqw#IT5lEd`ncm_;&O8K zK|Fv&}}zFm^$q6baRvK-H@F&R=~mH!_d>AWY^cGlH-TQ=wfWkc@lyye$iThU7Dm$tZCsgbpq&iNRVT51~xbi za=Hll4TRDwCRFuA?A{U;;Q+s%wj@+TWve<*vh)xOTb}tDLYPp{rxE8<34o_>yXDbf zV$@yUNF6aTq6v4Qo;@)23li%CCQ8m2lnB6f!b6B!c%^{hT*==(ijwR6^H3lCKH&V> zqW0HX0-Iz;-h0l`a+U|sSF3eiV@3&_eB5I_AXE+c`&-05TOtg|I;dK*Y9lVO6Q1)^ z#tbM;CLrK<5u~4fDn{QgV|xCfDcm-ad2ED7o(B4;SpR{}_&tG27x4(LC>eV{i`O@q zVO1ZLxt?fcGy`0ic}}J?aeh|Q{*ZN_pD6r^ncq%#tOW>*j=$t?4?F6kfcgo?lBYw< zQd?nv@9}h{4Z3;~v2*DO-`xgCuV`iG|x5jRS*9{iSK(tTu&{b_xwY#|o- z)5BkCUWlJzoxtyt7u`X3A<`Hebd#ybwiX$uRyexvh8TJ)<6zineW#v|&&WOvnePr9 zeU^CyQ`Mez?7J7(+VDfgdc#@?yTtaAvaR1c_tCJbh%Uvt-Cl*y(@zU<;!PJ_fQYpIBZ>9jaEWKl|} zk&x*g>E9>M4+l5a$QU0S{-|=ou42~j*DwKj#OEqS#%NGu4E$2*rdDsnp}`~P5?v)@*N`HaP2b7_QKhprC1=;|r+V-r{D}}6{)>GPx@r;+sdtn^G}9sXQ- zrgzqoU5pPPOBY-qYM6YicoMBz2LJ>=y{=cqZMobMMK32wTDu&D(mDELp)&aM$B~yZ zrUX~}XCt&?*3V@zZ^^0(Kb%%oMx5_k2ERlJ%94`l?gVNofFgy1ByVIq;oEqp8{U2g z{?V=Z81~|@=^AzQ-7PWuY^X7!#5{7&wM2qVhaWv&_TO~6l%pp7o1=seN@^5LKT|z^ zdu(xZXJ$2xMY$)-qqKDiNSLrEh2~Dnu^0^{iSQz5(CPj-dHZ%#uogC9UBu%N`#pDb zBred{|H%blxAxO?cnCGZH$0BuEIcH}f*RoFSo()+=$5QkLxO6Ie|UXzgn$6D-C^`E5~bf|Z-bOoXS$W# z;{qD2@w$68cp#w>uObZH_naF3)-L+|e|e#d21O35$3xD06Uyqinq6k`A_ryK*`6U; z>V)cTyTK%_wmF2!O91YZQGfeKiQ!Gy12;aD>vF_tISSyqN0OZLFUm`GJ@awz5p3}x zk4=B~5{eb}N;*R@91A)2cieZw#lH#}%H`O7d^x5$>iDW}tL**usq$~u(mx_2$tcHM z;X~IGv%IBTO7wI>sG*GH^6TIC!0(oN2gzP3^KN56W`I%)*l-e08%_dwm&*eMBVsd> zYQW9mlql(*?bBMY;A?~^f<=?R@(Rpvewp`?Z-y7UiuXFByqP^Onhi5CV*2^|buD~S zZ8!Zp=*N|GWE33AkI1%~98qf&1=SNqY3L`AF?(-hwdj^Wjh@~8X-#2+VC zJI&W2oi&1U%{=3p=rfrPmW0fvc9`}vWzgM9MOOaE_psk53Y5{ajieAWoA%Z78Ik{# zFEH6BYOUVLX>CLvoT&LAuPtvs z4%m)HsJWW#_ioGD&8>*#sV6Gs z5<_sbLSN=SYOek%k(?|e*+Exdf0Xbabe1n4D3;h}J00igJJ?yVlqwrg@zEJdeO!QA z^}kHM3jc5W&I!VU#mqT)&XII_n6_=Vx)+=`O2EUr{Q9__jqBqLIlYqU=gx>9oY3ISF|8C| z{!_%jw>z1XlXqMd4}HBz^;EA2qevnlagD4gB8+5tqhKUJATC8^^vMjW`%TAWt4Ax= z-sG*@0p~X8_X}AZ$?!}7D|l%u?z4>VYpIh%AN-{-%%l+u=x-uL_TJ^R1U|wDAdt!L zsE5K<5#QqS0fCglP>#T?+k>A*(#l&IakE68t+Ju@IWMyA^g8AqQ^}(ph{EO95vs^9 z{tP?UxzC~AY?<30fXJ)K<0!d~dA^!u{nH!Mt(J#Muth%QUNf-Gu!p{2$d5M&dd9{AbQ{yA{Cz=5{ z!GwFh1un->|F&vFERH((odDCcnwz8YSDJ9|zmh5>cVOx!J3HPR_tY>3wt{gYzc{S5 z@r?~emF09M`o_OuNTHMol$FMJN-d}U0;W{L`ZCH26c%i@QlHb8zz#5UO|`7V{H zKa)e}geJALQ)jPU+8@R|c9HNX+^FNjuHGUyld5V*5xRxrr)95{&N#Cvq^)1yLNa&} zD@-$!nIu=j2>*=!DMCkjb0q)(_(n@pX-q#%FZA)b<6Lj#wg;wk-;mPsd9q@C5AGZp z@;$zh_#uUM*aFm8n*VXtANSV?{`_b^JsAgo{bU}k($+y`^V3-#paife zTy*kBA=cLsd+y0&Mrxv+adoMmO(Z9rsrr=l)1Ug5bX?WkpG?6P)Qm90P3@+jm4F!c z8&%VbFQbR|`* zs72t37@_Sr6uyU_vb6^08=HAyRQ(mo|6}i5+h8cp0zS#Gg6-Dv! z=SBk_Z%V`@$f}UfameQ=-HZC&5_x3gjFu5(ZIXo~lm9aFKa&dJbD>X%6qEsgMx8LT z=b|u@FqdzX-%iUCK+6+s(JYKVFgdrXxeWQyD*|}wy0vn)np;blVV>A>R{Vjg9`s1e ztwJ>oAthDSo5MgJBtie%mfVm1QgZ1MHkusWtF^DZ{hsCl_yK7xjaOB|hD#__u%5g!iQkve}NDvDp|-GKECs zVan!FuH-af@Yf&Z=TA<6{rA#L0nGXHm)DG}) zN7EP@=GkHmB`@Rr?8DudC=3<#Ik8*a}O%{t~#pRphC*XMWIKqgqD}` zp@9^LPr%A9@d>3)M06r~qel-j23?5C9`q4PAyEfJbg*Q)eD`-*1h1w_C4@|xt+Syu%hM09E}mv94#!IbueIZ*{1lip9TGN>@Pna z-79N4^IXOG%8o@h6vc`xrpR-MQX!r_To19KXkZ4hru4}2S><>V&B;f5ax=#4pPcB? zPgd&ufv7Mehlde9b*slXKxcJpRqQ)H_OpAm@Qc^uRr_XBcd9rIkehDqB?LI@Py4>) zf076Js^iuitDuzd8aLYuwTf9MuEA}Y&&29Xp81~HD0~^HQOzvP0p!_H^@7pK z0;_C`BIrl?s5_^MUefoM`2vP0LY8uQ!859;M3}Er1nb{Y^NmPZyO5D+3r1x5JvW0> z|84b!$z%|AP`qMEgwkFE)e_{M_nkQA6$=dC4)qnu(b1p@B;PB2?Ay)^>UF1-{zK?1 zO$S4Y>JLe(*9`{R7VNpBPA+=SL!5>U17p9{*dU5Bnq9A$hktQ2E_}(IlQ)b>j``zM zQ^uz6$Q66s)7}=1hz2zJ+}8eHI{>;#|OH9iNK)(4dp`-zL z{TQd-fcPUW52CN-KarW)SO+LwB|OpIfwhi&TI)!3TAnv=K0z|7JS~vVcANvQ{wATg zbeQkTy0OqTe=OXrTWXB=kHC_ob7%h^=ZhW28zD+5g-N@W*tacC#s5wd1#lGb62JWbi!ARdBpAT-`y%|g6^1pg6&STSb0XuY zD+#@3wce`y)>mPmHce?c)rUerST56Gsj`G9pEMXr@UoC{Qs(LGM2Z474I#z<5ag-;ozO0e>$B|>ZbEE?eiJp z65g=>awA|G1l!s)y1VM=F%hGXf-?QAE69_)8=IUVz}4k))JaETZpoGvWElUg?fr6( zr(8tP)-3MaJom6tZRrYj?XQwFbiRcby14I==yzionnXNFscm1(&WentZQsYvikL-+ z6I9Yv2%Jr$Pa42kVPW(uBMx37F(Td+Hm{o!Mp!-jugW4~hb$DyJCzEK>~H>^ws6$3 zBXTItcZ1o;AkNsU#lo`d3xG-b*0z~_UJLFfh6U)oW;So8he+hKUw(pkW0pcacd`Uh z9S5oeviqD72{n{+9huZh?(=}+e0O=({Uo9E=HQ4|)GJA(AVHbog*K$^Z458Vp5Cj` zlj7)Dp97$Nx<&@@o(Ep_-*YNZko>6}=#M6|S%WF!uXR7%+1Fp(<8AHp+pYbPK%$=# z$qfLL4utL>1!VMSB9bZPmUD;fi@ZLPr;O6y16@=XbXDCXCgnKM1rg*4Cz2>k2_i#} zhre^YHk6ivW+dxSo{llBL&lyN(!<0%GIg?(Z@*mF(U=wAg~aazERQz#XWNoGL3(v^ z_q%)eQ9Tl0MXu&tC4tMYtATD5IEEgcydxe==bFn4!bXys4HG35D4kv--S48k^FW-0 zBvSF13{ka5s3=q08WmK$`a_wux4|rHQ6t+eqXl0W2P{=lQ(`Am*=1{0_~MTE5We~a zOMZBc5O%toNn87q(V)r^n0)VkW+SlRMzD0Q$KVoWwuR-W_V>|npj^fkGI^m9#x@T2 zSF{B0FE}yC(LgP#m0o+3bxL@9j`fRvJY%tOHRGl~{P=|W=sm%HdwqhIZ8*FZ=ry&4 zn`I?|f15D%>qFS=8}9e2?V~P|st4Y~nawPQFN7qDRU+g-ACGUXu~XN}9L2t|;s9*p z<2POCrow{ZLj*CZ?R*z#^{VQ!%$zQwg{IWp%hN$)sX;GGGrGx!h5QfDDvS#(<8?fzx2!j z9TPKxCEief{rOSsue5zpm4n20JSyb-UYDZ&D+8ZK6a0oz6F055t$2Lg<3vDRy*2)1 zgeF(}VBReYiYyLLFf4>hQN`QcL+Mq9mm53QxzI~&kDb8o--IBoNuNYT+q96VFblV# zr(Dndl>K?qzE)j2|HvI5Bblh28l#d3B(jS8rjUQ5;3 zTci^}(E%__mM;IU?_Y#c6ZX_B!$P#{OvcTagRRG>oOdlQ5dv;Fjt|Xr#n$`_IXlu+ z0-v>Qqvk-=+aE-^5wP>7ynjQS378Sn+FXSIyg$)kr^JBU)8g)VgL(I;pFkLl<2sY* zNQ#CNH%V>NLr;wwV7>Exq6=}o{+y>6&QmU${&kJ0xZB*#7ySN5LyACP(#JE|Mi8K* zRgi+BWsPQ1ik-*-l}J~#`qsASYtJ;F{@2}(|86cax^@gi0F($13N#rkM&-3ycfe{$ zF5DoiHaG|sts*~ca|SstRfwDE512z@8bK>6MF!+fbkBbvz2GBIn2Cb4 z`VE2!1CtmOp69HY((W}uIY>@xW3+w550*Gm9XZmHJPSe~0os&~y9H&KRevTEUJp3b zj~81Psw?E#2~G9lhl+V9#MCCsCkc9l#*abJ_R*1hT%JMz1B|5Z3d!wo0Bgx%aq_0b z=jjUlX20KSL#${Xj<8VAv-=)BEJ=8MeRM6d5)ILYnh7u<>Z1&GzDa>jN!0ZH^?cNB zu5%PK1jv(@4?_0L@iq%+WUxPmW;V(%Lt<12jVC{2B`w?Ym9*7zL~n0dYRVi1)r?P5 zQgv0ftY^<4Zbwa#&$|ZK37#q!uS+ClJrY$#lkl=e2mWA!$0-(=gabQp=nKLkp@Q%~ znga0y5T^e+=CXUSEQUp%B6L?4yR4`Gxlr_3q3O1JoR5k{>Y`mqm$WoA+KIk!ds8b zWM*Z^IOq6{bS3+%uwFO{J~-b%+38(RXP*snn)(qS$0~w*C;OiOfD~X9@B<~xrxFjz z_#V_q$omp-C*D11XV3{EVT4Uh(#DCCU!O0ChPy|&n6)uF+Ax*qzW?4;Hdxj!~-;47=8CT8G|gd1`ApWHP+fY|1$XPx*qm7b}Y z+DDsUX@U0-|8{g-E#3<5Bc15G;1`}CwjYUtXWbipTqaRU!BR=WdIWkVA-W_D#tT0AS%86=*WyJMTAhfF{!pthIbtH(H@Tw&yC1TmJf^RVLX{%MD{{=@w(<1Q5()TPxgw~CqABV$hS z#mc$AmonP|{37#@&-i_;^KLtIY(sa;`xsD zP!Ms8L%dTim*AWwC)x+29`V$xu}tISZ-Tb=KkM=MMbqc2%&LX^N-4XUZ%Sk4r*&V^ zJ&Yo=7TkQH`z%xA^hS=G%Ki{_%-q!h9OTURME?v@0MIIFyrXcU;UqCaec1!rtbN{x zPwj1wJE%FV9kmf_q>s2YyPNpX^v?%gh@)}(1dN32_&m{f6{MV9=6gcC2uw(?q;ERg z9~+A8t)ZZ@!oln-I(+WEt}@km0?d#eqG`3)Ew$}cb96kK6ngPy^WmAYH$guo45tVF(Q-u~u@}V;VdQ&KRu{-mv1C|;rhU*Y>$Yv({gnG&BD__a||2Zf3Ob$o5Lj+ga0*Ro-kMv1y7=dxggYa8USNdke6j}8Z*p{Sh`Uk$xtj!zC$k<~r z35Cbj@iAz+XT$lKEsA={;!b8(3{mCIjmZnUgX!ax3^e@Vl>BlF9ZTw|QK=JptB0H+A z6#L=PbSin5SWtOaw4war%!xNjC?un2rF}IqPmKe7AM3GghRrYbEH~Fj9!X009RyiR zDdKt=aPJ|lVNPrgx4=%X@&s658xmi+==tqt=tJ$*l{2DP#j6VFu=`$mt~di{N$*cc zY*zXuhRZX(j7(UbKB)g$g-mgNa05P`t(7f_P=8UZW5GDFZY1h7{9z&Q>_EW8l_Jj4MPKb(o_(Gg7_c&uu^ zic!WZwOCucXxjC+@ToZWpsh2W2WMB;W%;eIq~Iixbn@k}t3 zwxW>=u`^Q1|5UUCc$uJ#yjzX}%$WoQSOP5j5Hej~!jpPOfotwgWEkW++kHH@-0{0^ z?5hE@F{l~jIsQNqZWO<}8ZZ*#A;N9-C~dRu=wdH*y753hykiMPaf;LgsA&OP3I3Ket>^#o7`xrN>3A z6W;_i|CYsQ3S|@=pT^_&vc=5(;vgI1=D$3q<0osTThHUT;yuHLX5#pH8CzsBU%os1 z)%Z7GKWJ`Q`Y*%CJ4fy*cLFl{m#kEQ(=>}B!74I=4#wD5;W!7Q88QkLuC;hI3CW)N zhTQ6PPCJU7OssiH(4U(`SHi?_!OwUymd;k{6aV;el9pLSjXCB;uDqGkJoy^gQ`q?>Wsl~yjz`Au-2L5yvCe8D!_p2U%J1Aiw1+BZk3t}@6wi5n0DgGw@>L12( zSQxs-3DKGz#1zq_H9dA^vcwF>9ih>jlZBOu%nIy|4tdXQCsYK!UK&Z&DVW#l8qpDP zcdL9f3ozDX^U6PT?qoLjSAl!f{}J{26@ic)4Yf4mn%Z zYKAs9KS+~8Y3bPj5eE%b+5Fj&G(wO(CLosqjm(wl5iMR*lyoGR#*{IGc>kn%Z3}X= zT?_5qI_HQD<%DhX1PH0KoJnKdHC(LCXvc^PFyn)R&hZn3Rl71-==<0xSwL$z1#ssZ z`s!gh$R!MH9C0p4Sma~c$2cqSxHhLGEJ*H(MOU}tF#pbpa@Y9|^oPvH=3f79fRxK^ zJrTANP0Ni$-=RsNzMd zD4aO|E!f%R{m|ZV%{IO|m%-y!h0#RuSZke2mE=8nBq$WdfJC2Qh(r)xso@9qXok57 z62UY&%{`ehpbqdC_G@kapAG~j$tpyzKZZ` z)RK^0!%!z(3SVdB+9w8GUZUQf=GX|zrpxi4`HnXYW~eM^Lx<_cKjwlk%{2=NkTib1>5m*48otK zzwnf{k28o{EzGPQ_v7t#f`i+)BzME|N{T>u^kH;4zn>*3e4&YHIfHZ-w*{Oxa4!7l zZwWvvs$s#|v5r@7SUeTDRt^9zk&Qr+cZ8Tan3lrT^$yYlNXud*U|fzvt1(N68) zi5Ur{524rZI?31Ow z6{r7C|70rmJOS!np#~;AQ{iE>+Us#o8H}lJSria?_-ZP8n($imGkCu*>`sh5HH4ut za0H{ea^%Mw?#H7%|JBa-4)-$nOJbFSW6W!3yUL7BR>FH8I zJx^!HZ;^o*5z-bYjJOP+HMv1-u;cuNaMSfHr}g9htHHQT7&`t@HOlfLP5P2^&WL%R zPXD6m_ABU1xy&(*P&$CF=o;_1XlE}LXApfxknw;*-4%&^+hx8rkqVX`l`Ly(qazZD66V1H>}nsB{9^HqGn}JKXxb!9?0T@C;vlxf-3O;8+=} zzZ`qQ`g*<}{L6ym?AB&8-vl3w*5XEh5+hM&v8A1M~xZSEkR z`{&&Z&Gfb6u=D4#!U~g_;o;_3vKVH^9-r$M&S(znA8)M0g0BA{n=8&cLc@i}AmcUN z4%aREm$tsFE|-+$v)D%@i96&?IGQR=esa}I0?R&{jYNI->o5|_#+$h?d{fa z>Utdz{~|i>h$>2UYVMy>zkDqZK!vkLYAC?suQSrVhx}?5MGWmYCTGuaj$hjV2T_4x z7^I@8?V6_-XMR7)y|F_)qXwr@P5Yh=r;9p`yt&-(`m@>coHnO=<13dQxl>2+^MD2f zzh-chSB|M)3=xba+T?Q3NM3BJKcQ(({^7I3v>>N&=?1ExmuCgts@(jdXvb`)_cs!; zvIu7vQahHjP?yVb70xRh#wM6vB7sOL{_B4uQx}L#-a&_TGFL^GKfD&KUT_KwCi8ex zI2i^M!bg7Jh{uv=@lw+CC;5E+v4ce~7vw#)^jiPs44!|mFKmG${C&2pZ8HTL&W~_- zybDGHifq7ZbHRHeZz7pAHEC{13$glc1e;Qg;>4US>>^hv> z^6~d4FHtYjUFHoB3<@4>ek;Q|p@H9icCpSw#s36jDG-c4w}WW!;r7Ime1#=*7j<(- zZF^Uc@m-zkAtxUnoV%B*vI`!r0KEbxv@bY+2e+68oI!wU`1~x1c53B5Sdbb_4jU*g#X3%mXoxQH7 z;wr-g$77)^WSLt>^FMoDRFu|%B#qrk-;3T9R!nvEdw$my6RBDu#Zi1(Ye_Zf_2UUS z@9~+M+nVKOJ`**pd6{D=MXuZk>de9xiYIALI(AaR<|w^pR;59 zm(sR%1TQu?sik_Es|Bbp6T;+X)*2%;Ct;nHyaI#ID6H@2JVXyxd1*wgrn={fgSCO{ zNeB!r|E7iIZLBshs;(;kMobx~b37(rM<$USXk2Iuo9M@2`9fo$soHyaCyQ%GPm4tF z%mu{acK)eXKs+qFH>xf+&*G4G@md1_9Y9{!p)in+m`5cZlMdvkPQNF&_?;KchS%er zodV^?US~8qSE|#J?a1NX?5!*$I}rW-+RpPP^sgsrM?ctjFV`B+?)SV^J-AO7_9pU`}PG1^uzos2Q6H0FJ_dkN{BIO)KumJ+x5fW^X*WeLAZ zhdX15!?;XvQUZN)(6}Uj(V$RQP|{tIwKg0DKD=Mx+KaVVQ8jQ~_(6C1!0V_ENisq6 z>vWZ{y&c(J891ptd4XSZr`hw`x5CD2vy3gZ>EIWGtCCT&hbk*&R(Om9y>56MX0M_E zrIiqYvB%?u?Vf^}+hLa@`K`Qt_J<<#>DY=JI)+Pk7fJgUe)7^N<1*6OUD*O7g3)Nd zwoW0kkk-F=p-BM=7*G%lU2|*99K&=r%r64JsN|cPiCKFH+*yLan0uk2Co+5D<9Cy= zanE1Q2w&R?f+MdZmsZ11!tAZTEo93ASLVrk>kh%rN9I*C)wk#_Yj`j>++Ir;7Iyq( zt9Whsu?z^_D{POY*opo!b#PU{s-BVkPyDCCf(V=-ysw;`331^DiZTn(sMN;@5 zyXR%4;Ym03U)IBiZYt;8U3dT3y#<*&39O16cS?kcr{DMaJX-K|^%DiFR8TsZ-(7sW zc#n>qn|*Cu8Vmgb?#%)2vaPLcdQoQutflz}MW1D6<8fm`7;m*rrnxs-FbO2 zU)=f^*YAG`2pN2s<@vSZ{dq&d#aQg0t&*UNbV_+ykY2&;gC*1)+Qou53GFWfM`H*4 z2{XkSpAt|fi3;5gw4Z(@U@t`m)8di`C|O)V z@(D#Kj})m)F3%UT!1Bc?;mE8TFu^&%2A4Yi-Kz-2%M)2XK8$Th+!>#F^P-cX!2|cR z3Zo;HP17cswa*hA{LXKO?E6}6*IaO4daD5S&hMw3J&6=3gxovi!YS@9v+!;j`wn;e z@1VnY3}Z^n)m%7MkY20VJ4zNqT!?#oM5e*a!Ppan|DUJ2kyG=8|L| zrr$F(O_qNAVdX~r6l9?Y^qi6I!~{>Z;(wl?Ny3C}o(mOegr@@S-zQfcNRY#_9#6^W z-zUKvNJ-z`AR+zl4K0#8@XDR!=Tlky@ALFu39CL3fD!)t1`)p)cvV_Gn)Q@-|9uLw z1G`OkKLsNE?@fes7Vs)7?C}4-fc66T;dI(iQ2zH#(kQ#Yt33`kmj8YeEz&m@+V^R< iOtc99_o13B$5Q$jvE`UF<@yM~KV^A!xpLXJ!T%2)GtWT) literal 0 HcmV?d00001 diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 1d9e57ccaf..99f7fbe8f6 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -2455,6 +2455,20 @@ fadeIn("3m") fadeIn("3m", 1.5) ``` +#### Warning on fadeIn and Rolling Restarts + +Traffic fade-in has the potential to skew the traffic to your backend pods in case of a rolling restart +(`kubectl rollout restart`), because it is very likely that the rolling restart is going faster than the +fade-in duration. The image below shows an example of a rolling restart for a four-pod deployment (A, B, C, D) +into (E, F, G, H), and the traffic share of each pod over time. While the ramp-up of the new pods is ongoing, +the remaining old pods will receive a largely increased traffic share (especially the last one, D in this +example), as well as an over-propotional traffic share for the first pod in the rollout (E). + +To make rolling restarts safe, you need to slow them down by setting `spec.minReadySeconds` on the pod spec +of your deployment or stackset, according to your fadeIn duration. + +![Rolling Restart and Fade-In](../img/fadein_traffic_skew.png) + ## endpointCreated This filter marks the creation time of a load balanced endpoint. When used together with the fadeIn From 3c941e8eee7e7cd38122ae101fed00ae7ccb7e47 Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Wed, 30 Mar 2022 23:28:40 +0200 Subject: [PATCH 0019/1047] Build with Go 1.18 (#1991) Update gosec and staticcheck Ignore use of Deprecated net.Error.Temporary() method Signed-off-by: Mikkel Oscar Lyderik Larsen --- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- Makefile | 16 ++++------------ delivery.yaml | 2 +- innkeeper/client.go | 1 - loadbalancer/healthchecker.go | 1 + proxy/proxy.go | 1 + queuelistener/listener.go | 1 + 8 files changed, 11 insertions(+), 17 deletions(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 42cc7b1be6..d19980c328 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.17' + go-version: '^1.18' - run: go version - run: make deps - run: make check-fmt diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index e450d47126..edf06df3d4 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -8,7 +8,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.17' + go-version: '^1.18' - run: go version - run: make deps - run: make check-race @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.17' + go-version: '^1.18' - run: go version - run: make deps - run: make check-fmt diff --git a/Makefile b/Makefile index 7a227ee998..6a8bb79073 100644 --- a/Makefile +++ b/Makefile @@ -120,16 +120,8 @@ deps: go env ./etcd/install.sh $(TEST_ETCD_VERSION) mkdir -p .bin - @curl -o /tmp/staticcheck_linux_amd64.tar.gz -LO https://github.com/dominikh/go-tools/releases/download/2021.1.2/staticcheck_linux_amd64.tar.gz - @sha256sum /tmp/staticcheck_linux_amd64.tar.gz | grep -q edf3b59dea0eb0e55ebe4cb3c47fdd05e25f9365771eb073a78cf66b8f093d9e - @tar -C /tmp -xzf /tmp/staticcheck_linux_amd64.tar.gz - @mv /tmp/staticcheck/staticcheck .bin - @chmod +x .bin/staticcheck - @curl -o /tmp/gosec.tgz -LO https://github.com/securego/gosec/releases/download/v2.9.5/gosec_2.9.5_linux_amd64.tar.gz - @sha256sum /tmp/gosec.tgz | grep -q 524330ccda004a9af0ef1b78b712df02144a307a15b57a6528f12762f73c8d8e - @tar -C /tmp -xzf /tmp/gosec.tgz - @mv /tmp/gosec .bin - @chmod +x .bin/gosec + @go install honnef.co/go/tools/cmd/staticcheck@latest + @go install github.com/securego/gosec/v2/cmd/gosec@latest vet: $(SOURCES) GO111MODULE=$(GO111) go vet $(PACKAGES) @@ -142,7 +134,7 @@ vet: $(SOURCES) # -ST1021 too many wrong comments on exported functions to fix right away # -ST1022 too many wrong comments on exported functions to fix right away staticcheck: $(SOURCES) - GO111MODULE=$(GO111) .bin/staticcheck -checks "all,-ST1000,-ST1003,-ST1012,-ST1020,-ST1021" $(PACKAGES) + GO111MODULE=$(GO111) staticcheck -checks "all,-ST1000,-ST1003,-ST1012,-ST1020,-ST1021" $(PACKAGES) # TODO(sszuecs) review disabling these checks, f.e.: # G101 find by variable name match "oauth" are not hardcoded credentials @@ -150,7 +142,7 @@ staticcheck: $(SOURCES) # G304 reading kubernetes secret filepaths are not a file inclusions # G402 See https://github.com/securego/gosec/issues/551 and https://github.com/securego/gosec/issues/528 gosec: $(SOURCES) - GO111MODULE=$(GO111) .bin/gosec -quiet -exclude="G101,G104,G304,G402" ./... + GO111MODULE=$(GO111) gosec -quiet -exclude="G101,G104,G304,G402" ./... fmt: $(SOURCES) @gofmt -w -s $(SOURCES) diff --git a/delivery.yaml b/delivery.yaml index 646c795b71..9e7ccca14c 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -3,7 +3,7 @@ pipeline: - id: build vm_config: type: linux - image: "cdp-runtime/go-1.17" + image: "cdp-runtime/go" type: script commands: - desc: build-push diff --git a/innkeeper/client.go b/innkeeper/client.go index 6004cbaf8e..a386ef964b 100644 --- a/innkeeper/client.go +++ b/innkeeper/client.go @@ -196,7 +196,6 @@ func (c *Client) authenticate() error { // Checks if an http response status indicates an error, and returns an error // object if it does. -//lint:ignore ST1008 inkeeper is deprecated and will be deleted func getHttpError(r *http.Response) (error, bool) { switch { case r.StatusCode < http.StatusBadRequest: diff --git a/loadbalancer/healthchecker.go b/loadbalancer/healthchecker.go index 1af31042ad..9c4840330c 100644 --- a/loadbalancer/healthchecker.go +++ b/loadbalancer/healthchecker.go @@ -214,6 +214,7 @@ func doActiveHealthCheck(rt http.RoundTripper, backend string) state { resp, err := rt.RoundTrip(req) if err != nil { perr, ok := err.(net.Error) + //lint:ignore SA1019 Temporary is deprecated in Go 1.18, but keep it for now (https://github.com/zalando/skipper/issues/1992) if ok && !perr.Temporary() { log.Infof("Backend %v connection refused -> mark as dead", backend) return dead diff --git a/proxy/proxy.go b/proxy/proxy.go index 667caffdf7..f0992634ba 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -952,6 +952,7 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co status = http.StatusServiceUnavailable } p.tracing.setTag(ctx.proxySpan, HTTPStatusCodeTag, uint16(status)) + //lint:ignore SA1019 Temporary is deprecated in Go 1.18, but keep it for now (https://github.com/zalando/skipper/issues/1992) return nil, &proxyError{err: fmt.Errorf("net.Error during backend roundtrip to %s: timeout=%v temporary='%v': %w", req.URL.Host, nerr.Timeout(), nerr.Temporary(), err), code: status} } diff --git a/queuelistener/listener.go b/queuelistener/listener.go index 93e056c897..739e12d9c7 100644 --- a/queuelistener/listener.go +++ b/queuelistener/listener.go @@ -230,6 +230,7 @@ func (l *listener) listenExternal() { c, err = l.externalListener.Accept() if err != nil { // based on net/http.Server.Serve(): + //lint:ignore SA1019 Temporary is deprecated in Go 1.18, but keep it for now (https://github.com/zalando/skipper/issues/1992) if nerr, ok := err.(net.Error); ok && nerr.Temporary() { delay = bounce(delay) l.options.Log.Errorf( From d91bcf1d4e373556b2e541bad5334bbebaa95a2b Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 4 Apr 2022 11:36:44 +0200 Subject: [PATCH 0020/1047] proxy: use debug level fo serveResponse logs (#1985) Existing log messages do no provide enough context about request and are only suitable for local debugging. There are metrics and opentracing tags already in place to monitor problems in production setting. Fixes #1981 Signed-off-by: Alexander Yastrebov --- proxy/proxy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index f0992634ba..e04f510d3f 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1191,7 +1191,7 @@ func (p *Proxy) serveResponse(ctx *context) { if err := ctx.Request().Context().Err(); err != nil { // deadline exceeded or canceled in stdlib, client closed request // see https://github.com/zalando/skipper/pull/864 - p.log.Infof("Client request: %v", err) + p.log.Debugf("Client request: %v", err) ctx.response.StatusCode = 499 p.tracing.setTag(ctx.proxySpan, ClientRequestStateTag, ClientRequestCanceled) } @@ -1206,7 +1206,7 @@ func (p *Proxy) serveResponse(ctx *context) { p.tracing.logStreamEvent(ctx.proxySpan, StreamBodyEvent, strconv.FormatInt(n, 10)) if err != nil { p.metrics.IncErrorsStreaming(ctx.route.Id) - p.log.Errorf("error while copying the response stream: %v", err) + p.log.Debugf("error while copying the response stream: %v", err) p.tracing.setTag(ctx.proxySpan, ErrorTag, true) p.tracing.setTag(ctx.proxySpan, StreamBodyEvent, StreamBodyError) p.tracing.logStreamEvent(ctx.proxySpan, StreamBodyEvent, fmt.Sprintf("Failed to stream response: %v", err)) From 33e805a625d7914de961022054d8f4c7ba42afcd Mon Sep 17 00:00:00 2001 From: Ricardo Herrera Date: Mon, 4 Apr 2022 06:06:25 -0400 Subject: [PATCH 0021/1047] Ingress v1 - TLS from Spec (#1974) * Load secrets from ingress resource This will allow us to use tls certs defined on an Ingress to return to clients Signed-off-by: Ricardo Herrera * Load TLS certificates from IngressV1 Will allow users to define TLS certificates with Ingress definitions allowing skipper to terminate TLS dynamically Signed-off-by: Ricardo Herrera * Add secrets to kubernetestest api Signed-off-by: Ricardo Herrera * List and load secrets to kubernetes cluster client Loading secrets with namespace+name as id to allow reuse of same secret Signed-off-by: Ricardo Herrera * Add TLS certificates per host Will not use routes to determine which TLS certificate to use since certs are only linked to an Ingress rule's host Signed-off-by: Ricardo Herrera * Validate Ingress TLS Secret before registering Signed-off-by: Ricardo Herrera * Add test for IngressV1 tls Signed-off-by: Ricardo Herrera * Compare tls hosts to rule hosts Signed-off-by: Ricardo Herrera * Add cert registry for TLS certificates This registry would allow us to store TLS certs from ingress resources to be later used by a TLS listener and dynamically server certificates based on host. Signed-off-by: Ricardo Herrera * Pass in cert registry to Kubernetes dataclient We will be able to use the registry later in the TLS listener if we pass the registry as a Kubernetes dataclient option Signed-off-by: Ricardo Herrera * Allow tests to use the certificate registry Signed-off-by: Ricardo Herrera * Return certificate when searching by key Signed-off-by: Ricardo Herrera * Use simple key value for cert registry Signed-off-by: Ricardo Herrera * Return correct error when cert not found Signed-off-by: Ricardo Herrera * Add tests for cert registry Signed-off-by: Ricardo Herrera * Don't export getCertByKey Signed-off-by: Ricardo Herrera * Enable skipper to use certificate registry for TLS Skipper will optionally enable TLS requests to terminate using the certificate registry Signed-off-by: Ricardo Herrera * Generate a fake default cert Default cert will be used to terminate TLS connections without a matching certificate Signed-off-by: Ricardo Herrera * Update tests for cert registry Signed-off-by: Ricardo Herrera * Test skipper option for TLS cert registry Signed-off-by: Ricardo Herrera * Use struct for tls certificates Using a struct will enable us to save the list of hosts with the cert Signed-off-by: Ricardo Herrera * Update ingress to save tls certs with new struct This will use secret names as key for cert entry and save with list of assigned hosts. Signed-off-by: Ricardo Herrera * Save certificate as ns/name secret combo Should avoid overwriting values in case of duplicate certificate names Signed-off-by: Ricardo Herrera * Only add secrets when ingress namespace matches Signed-off-by: Ricardo Herrera * Add comments to exported functions Signed-off-by: Ricardo Herrera * Compare certificates before updating We don't need to update a certificate if it's already present in the reg Signed-off-by: Ricardo Herrera * Return bool when SyncCert updates a cert Signed-off-by: Ricardo Herrera * Add tests for cert registry Signed-off-by: Ricardo Herrera * Improve error handling for ingress tls Signed-off-by: Ricardo Herrera * Add TLS certificate registry as an option This will configure the TLS listener to use the certificate registry to terminate TLS connections Signed-off-by: Ricardo Herrera * Improve error handling for Ingress tls Signed-off-by: Ricardo Herrera * Add test scenarios for Ingress TLS Signed-off-by: Ricardo Herrera * log when tls certificate loaded from ingress Signed-off-by: Ricardo Herrera * Fix linting errors Signed-off-by: Ricardo Herrera * Moving certregistry to be submodule of secrets Signed-off-by: Ricardo Herrera * Improve logic on SyncCert function Signed-off-by: Ricardo Herrera * Fix error log for loadSecrets Signed-off-by: Ricardo Herrera * Return bool on cert lookup Using bool to make clear when a cert was found or not. Returning error was misleading. Signed-off-by: Ricardo Herrera * Return best matching certificate if > 1 found Matching SAN names we can return the best matching certificate when the same host has been configured for different ingresses. Signed-off-by: Ricardo Herrera * Default to return first cert when matching If a user defines certificates that are not valid for the host we should still return the certificate configured, even if not valid. Signed-off-by: Ricardo Herrera * Fix line-endings for files Signed-off-by: Ricardo Herrera * Update tests to mock secrets api Signed-off-by: Ricardo Herrera * Add tls certs from ingress when cert reg enabled If the certificate registry is not found, ingress should not attempt to add the tls certs from ingress Signed-off-by: Ricardo Herrera * Secrets without data are valid Clusters may have secrets without data. Removing condition so we don't show errors. Signed-off-by: Ricardo Herrera * Increase timeout on TestInitOrderandDefault Timeouts were causing test to be flaky Signed-off-by: Ricardo Herrera * Silently ignore invalid secrets Signed-off-by: Ricardo Herrera * Only load secrets when cert registry is enabled This will allow users to decide if they want skipper to read the cluster secrets. Signed-off-by: Ricardo Herrera * Use lookup to find tls secret in secrets Signed-off-by: Ricardo Herrera * Improve tls ingress tests Signed-off-by: Ricardo Herrera * Simplify cert registry to be a host/key lookup This will speed up client TLS connections since we will not need to lookup certs in the registry from iterating through all the hosts. Signed-off-by: Ricardo Herrera * Refactor adding TLS certs to registry from IngV1 Signed-off-by: Ricardo Herrera * Check logs when testing ingress tls Signed-off-by: Ricardo Herrera * Improve error handling for ingress tls Signed-off-by: Ricardo Herrera * Fix formatting errors Signed-off-by: Ricardo Herrera * Choose best certificate on cert sync This will allow the certificate lookup from TLS request to be fast. A simple key lookup will return the current certificate. We will make sure we load the newest certificate when loading into the registry. Signed-off-by: Ricardo Herrera * Test using flag to enable ingress tls registry Signed-off-by: Ricardo Herrera * Only enable certregistry when flag is enabled Signed-off-by: Ricardo Herrera * Load parsed cert leaf on registry sync This will allow us to quicky compare existing certs with candidates returning if same and chosing best based on NotBefore. Signed-off-by: Ricardo Herrera * Set cert registration logs to INFO Cert registration will only log when add/update action has happened. Using INFO will not fill the logs. Signed-off-by: Ricardo Herrera * Refactor generating cert from secret Using a separate function to generate a tls cert from a kubernetes secret. Signed-off-by: Ricardo Herrera * Remove default certificate from cert registry We do not want to maintain default certificates within the registry. Signed-off-by: Ricardo Herrera * Use tls-key and cert as default with registry Registry will be able to use configured certificates as fallback if no certificates are found in the registry. Signed-off-by: Ricardo Herrera * Improve error handling on TLS cert gen Signed-off-by: Ricardo Herrera * Remove redundant leaf check Signed-off-by: Ricardo Herrera * Remove unused err var Signed-off-by: Ricardo Herrera * Lock mutex on SyncCert and GetCertFromHello Signed-off-by: Ricardo Herrera * Refactor Certificate registration to inline Signed-off-by: Ricardo Herrera * Rename skipper option for kubernetes tls This will make clear that enabling this option will allow skipper to read resources and secrets to allow TLS termination. Signed-off-by: Ricardo Herrera * Rename key to host in ConfigureCertificate Signed-off-by: Ricardo Herrera * Renaming secret struct Metadata Signed-off-by: Ricardo Herrera * Fix staticcheck lint issues Signed-off-by: Ricardo Herrera * Include err in msg when fail to parse leaf Signed-off-by: Ricardo Herrera * Omit holding lock when returning cert from hello Signed-off-by: Ricardo Herrera * Make secret tls base64 decode err clearer Signed-off-by: Ricardo Herrera --- config/config.go | 5 + dataclients/kubernetes/clusterclient.go | 67 +++-- dataclients/kubernetes/clusterstate.go | 1 + .../kubernetes/definitions/ingressv1.go | 15 +- dataclients/kubernetes/ingress.go | 54 +++- dataclients/kubernetes/ingress_test.go | 1 + dataclients/kubernetes/ingressdefinitions.go | 10 + dataclients/kubernetes/ingressv1.go | 22 ++ dataclients/kubernetes/kube.go | 18 +- dataclients/kubernetes/kube_test.go | 82 ++++++- dataclients/kubernetes/kubernetestest/api.go | 9 +- .../kubernetes/kubernetestest/api_test.go | 19 ++ .../kubernetes/kubernetestest/fixtures.go | 8 + dataclients/kubernetes/orderingbug_test.go | 3 + .../ingressV1/tls/tls-invalid-secret.kube | 2 + .../ingressV1/tls/tls-invalid-secret.log | 1 + .../ingressV1/tls/tls-invalid-secret.yaml | 89 +++++++ .../ingressV1/tls/tls-invalid-tls.kube | 2 + .../ingressV1/tls/tls-invalid-tls.log | 1 + .../ingressV1/tls/tls-invalid-tls.yaml | 90 +++++++ .../ingressV1/tls/tls-missing-host.kube | 2 + .../ingressV1/tls/tls-missing-host.log | 1 + .../ingressV1/tls/tls-missing-host.yaml | 93 +++++++ .../ingressV1/tls/tls-missing-secret.kube | 2 + .../ingressV1/tls/tls-missing-secret.log | 1 + .../ingressV1/tls/tls-missing-secret.yaml | 80 ++++++ .../ingressV1/tls/tls-multiple-host.kube | 2 + .../ingressV1/tls/tls-multiple-host.yaml | 102 ++++++++ .../ingressV1/tls/tls-single-host.kube | 2 + .../ingressV1/tls/tls-single-host.yaml | 92 +++++++ dataclients/kubernetes/update_test.go | 2 +- metricsinit_test.go | 2 +- secrets/certregistry/certregistry.go | 71 ++++++ secrets/certregistry/certregistry_test.go | 230 ++++++++++++++++++ skipper.go | 38 ++- skipper_test.go | 25 +- 36 files changed, 1204 insertions(+), 40 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.log create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.log create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.log create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.log create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.yaml create mode 100644 secrets/certregistry/certregistry.go create mode 100644 secrets/certregistry/certregistry_test.go diff --git a/config/config.go b/config/config.go index 8fdde2585d..29aac1a800 100644 --- a/config/config.go +++ b/config/config.go @@ -203,6 +203,9 @@ type Config struct { // TLS version TLSMinVersion string `yaml:"tls-min-version"` + // TLS Config + KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` + // API Monitoring ApiUsageMonitoringEnable bool `yaml:"enable-api-usage-monitoring"` ApiUsageMonitoringRealmKeys string `yaml:"api-usage-monitoring-realm-keys"` @@ -485,6 +488,7 @@ func NewConfig() *Config { flag.DurationVar(&cfg.ExpectContinueTimeoutBackend, "expect-continue-timeout-backend", 30*time.Second, "sets the HTTP expect continue timeout for backend connections") flag.IntVar(&cfg.MaxIdleConnsBackend, "max-idle-connection-backend", 0, "sets the maximum idle connections for all backend connections") flag.BoolVar(&cfg.DisableHTTPKeepalives, "disable-http-keepalives", false, "forces backend to always create a new connection") + flag.BoolVar(&cfg.KubernetesEnableTLS, "kubernetes-enable-tls", false, "enable using kubnernetes resources to terminate tls") // Swarm: flag.BoolVar(&cfg.EnableSwarm, "enable-swarm", false, "enable swarm communication between nodes in a skipper fleet") @@ -820,6 +824,7 @@ func (c *Config) ToOptions() skipper.Options { ExpectContinueTimeoutBackend: c.ExpectContinueTimeoutBackend, MaxIdleConnsBackend: c.MaxIdleConnsBackend, DisableHTTPKeepalives: c.DisableHTTPKeepalives, + KubernetesEnableTLS: c.KubernetesEnableTLS, // swarm: EnableSwarm: c.EnableSwarm, diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index 36df600b2f..cc43ecb0d4 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -19,6 +19,7 @@ import ( "github.com/zalando/skipper/dataclients/kubernetes/definitions" "github.com/zalando/skipper/secrets" + "github.com/zalando/skipper/secrets/certregistry" ) const ( @@ -31,12 +32,14 @@ const ( routeGroupClassKey = "zalando.org/routegroup.class" ServicesClusterURI = "/api/v1/services" EndpointsClusterURI = "/api/v1/endpoints" + SecretsClusterURI = "/api/v1/secrets" defaultKubernetesURL = "http://localhost:8001" IngressesNamespaceFmt = "/apis/extensions/v1beta1/namespaces/%s/ingresses" IngressesV1NamespaceFmt = "/apis/networking.k8s.io/v1/namespaces/%s/ingresses" routeGroupsNamespaceFmt = "/apis/zalando.org/v1/namespaces/%s/routegroups" ServicesNamespaceFmt = "/api/v1/namespaces/%s/services" EndpointsNamespaceFmt = "/api/v1/namespaces/%s/endpoints" + SecretsNamespaceFmt = "/api/v1/namespaces/%s/secrets" serviceAccountDir = "/var/run/secrets/kubernetes.io/serviceaccount/" serviceAccountTokenKey = "token" serviceAccountRootCAKey = "ca.crt" @@ -46,12 +49,14 @@ const RouteGroupsNotInstalledMessage = `RouteGroups CRD is not installed in the See: https://opensource.zalando.com/skipper/kubernetes/routegroups/#installation` type clusterClient struct { - ingressesURI string - routeGroupsURI string - servicesURI string - endpointsURI string - tokenProvider secrets.SecretsProvider - apiURL string + ingressesURI string + routeGroupsURI string + servicesURI string + endpointsURI string + secretsURI string + tokenProvider secrets.SecretsProvider + apiURL string + certificateRegistry *certregistry.CertRegistry routeGroupClass *regexp.Regexp ingressClass *regexp.Regexp @@ -137,15 +142,17 @@ func newClusterClient(o Options, apiURL, ingCls, rgCls string, quit <-chan struc ingressURI = IngressesV1ClusterURI } c := &clusterClient{ - ingressV1: o.KubernetesIngressV1, - ingressesURI: ingressURI, - routeGroupsURI: routeGroupsClusterURI, - servicesURI: ServicesClusterURI, - endpointsURI: EndpointsClusterURI, - ingressClass: ingClsRx, - routeGroupClass: rgClsRx, - httpClient: httpClient, - apiURL: apiURL, + ingressV1: o.KubernetesIngressV1, + ingressesURI: ingressURI, + routeGroupsURI: routeGroupsClusterURI, + servicesURI: ServicesClusterURI, + endpointsURI: EndpointsClusterURI, + secretsURI: SecretsClusterURI, + ingressClass: ingClsRx, + routeGroupClass: rgClsRx, + httpClient: httpClient, + apiURL: apiURL, + certificateRegistry: o.CertificateRegistry, } if o.KubernetesInCluster { @@ -179,6 +186,7 @@ func (c *clusterClient) setNamespace(namespace string) { c.routeGroupsURI = fmt.Sprintf(routeGroupsNamespaceFmt, namespace) c.servicesURI = fmt.Sprintf(ServicesNamespaceFmt, namespace) c.endpointsURI = fmt.Sprintf(EndpointsNamespaceFmt, namespace) + c.secretsURI = fmt.Sprintf(SecretsNamespaceFmt, namespace) } func (c *clusterClient) createRequest(uri string, body io.Reader) (*http.Request, error) { @@ -400,6 +408,26 @@ func (c *clusterClient) loadServices() (map[definitions.ResourceID]*service, err return result, nil } +func (c *clusterClient) loadSecrets() (map[definitions.ResourceID]*secret, error) { + var secrets secretList + if err := c.getJSON(c.secretsURI, &secrets); err != nil { + log.Debugf("requesting all secrets failed: %v", err) + return nil, err + } + + log.Debugf("all secrets received: %d", len(secrets.Items)) + result := make(map[definitions.ResourceID]*secret) + for _, secret := range secrets.Items { + if secret == nil || secret.Metadata == nil { + continue + } + + result[secret.Metadata.ToResourceID()] = secret + } + + return result, nil +} + func (c *clusterClient) loadEndpoints() (map[definitions.ResourceID]*endpoint, error) { var endpoints endpointList if err := c.getJSON(c.endpointsURI, &endpoints); err != nil { @@ -430,6 +458,7 @@ func (c *clusterClient) fetchClusterState() (*clusterState, error) { err error ingressesV1 []*definitions.IngressV1Item ingresses []*definitions.IngressItem + secrets map[definitions.ResourceID]*secret ) if c.ingressV1 { ingressesV1, err = c.loadIngressesV1() @@ -462,12 +491,20 @@ func (c *clusterClient) fetchClusterState() (*clusterState, error) { return nil, err } + if c.certificateRegistry != nil { + secrets, err = c.loadSecrets() + if err != nil { + return nil, err + } + } + return &clusterState{ ingresses: ingresses, ingressesV1: ingressesV1, routeGroups: routeGroups, services: services, endpoints: endpoints, + secrets: secrets, cachedEndpoints: make(map[endpointID][]string), }, nil } diff --git a/dataclients/kubernetes/clusterstate.go b/dataclients/kubernetes/clusterstate.go index ba4343d721..da35c439e1 100644 --- a/dataclients/kubernetes/clusterstate.go +++ b/dataclients/kubernetes/clusterstate.go @@ -15,6 +15,7 @@ type clusterState struct { routeGroups []*definitions.RouteGroupItem services map[definitions.ResourceID]*service endpoints map[definitions.ResourceID]*endpoint + secrets map[definitions.ResourceID]*secret cachedEndpoints map[endpointID][]string } diff --git a/dataclients/kubernetes/definitions/ingressv1.go b/dataclients/kubernetes/definitions/ingressv1.go index c2a5b1062f..f6047e875a 100644 --- a/dataclients/kubernetes/definitions/ingressv1.go +++ b/dataclients/kubernetes/definitions/ingressv1.go @@ -23,7 +23,7 @@ type IngressV1Spec struct { DefaultBackend *BackendV1 `json:"defaultBackend,omitempty"` IngressClassName string `json:"ingressClassName,omitempty"` Rules []*RuleV1 `json:"rules"` - // Ingress TLS not supported: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#ingressspec-v1-networking-k8s-io + IngressTLS []*TLSV1 `json:"tls,omitempty"` } // BackendV1 https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#ingressbackend-v1-networking-k8s-io @@ -71,6 +71,11 @@ type PathRuleV1 struct { Backend *BackendV1 `json:"backend"` } +type TLSV1 struct { + Hosts []string `json:"hosts"` + SecretName string `json:"secretName"` +} + // ParseIngressV1JSON parse JSON into an IngressV1List func ParseIngressV1JSON(d []byte) (IngressV1List, error) { var il IngressV1List @@ -111,3 +116,11 @@ func ValidateIngressesV1(ingressList IngressV1List) error { return nil } + +func GetHostsFromIngressRulesV1(ing *IngressV1Item) []string { + hostList := make([]string, 0) + for _, i := range ing.Spec.Rules { + hostList = append(hostList, i.Host) + } + return hostList +} diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index 91fd3cf98f..8687843a08 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -1,6 +1,8 @@ package kubernetes import ( + "crypto/tls" + b64 "encoding/base64" "encoding/json" "errors" "fmt" @@ -12,6 +14,7 @@ import ( "github.com/zalando/skipper/dataclients/kubernetes/definitions" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/predicates" + "github.com/zalando/skipper/secrets/certregistry" ) const ( @@ -25,6 +28,9 @@ const ( skipperBackendProtocolAnnotationKey = "zalando.org/skipper-backend-protocol" pathModeAnnotationKey = "zalando.org/skipper-ingress-path-mode" ingressOriginName = "ingress" + tlsSecretType = "kubernetes.io/tls" + tlsSecretDataCrt = "tls.crt" + tlsSecretDataKey = "tls.key" ) type ingressContext struct { @@ -40,6 +46,7 @@ type ingressContext struct { redirect *redirectInfo hostRoutes map[string][]*eskip.Route defaultFilters defaultFilters + certificateRegistry *certregistry.CertRegistry } type ingress struct { @@ -351,11 +358,32 @@ func hasCatchAllRoutes(routes []*eskip.Route) bool { return false } +// addHostTLSCert adds a TLS certificate to the certificate registry per host when the referenced +// secret is found and is a valid TLS secret. +func addHostTLSCert(ic ingressContext, hosts []string, secretID *definitions.ResourceID) { + secret, ok := ic.state.secrets[*secretID] + if !ok { + log.Errorf("failed to find secret %s in namespace %s", secretID.Name, secretID.Namespace) + return + } + cert, err := generateTLSCertFromSecret(secret) + if err != nil { + log.Error(err) + return + } + for _, host := range hosts { + err := ic.certificateRegistry.ConfigureCertificate(host, cert) + if err != nil { + log.Error(err) + } + } +} + // convert logs if an invalid found, but proceeds with the // valid ones. Reporting failures in Ingress status is not possible, // because Ingress status field is v1beta1.LoadBalancerIngress that only // supports IP and Hostname as string. -func (ing *ingress) convert(state *clusterState, df defaultFilters) ([]*eskip.Route, error) { +func (ing *ingress) convert(state *clusterState, df defaultFilters, r *certregistry.CertRegistry) ([]*eskip.Route, error) { var ewIngInfo map[string][]string // r.Id -> {namespace, name} if ing.kubernetesEnableEastWest { ewIngInfo = make(map[string][]string) @@ -365,7 +393,7 @@ func (ing *ingress) convert(state *clusterState, df defaultFilters) ([]*eskip.Ro redirect := createRedirectInfo(ing.provideHTTPSRedirect, ing.httpsRedirectCode) if ing.ingressV1 { for _, i := range state.ingressesV1 { - r, err := ing.ingressV1Route(i, redirect, state, hostRoutes, df) + r, err := ing.ingressV1Route(i, redirect, state, hostRoutes, df, r) if err != nil { return nil, err } @@ -421,3 +449,25 @@ func (ing *ingress) convert(state *clusterState, df defaultFilters) ([]*eskip.Ro return routes, nil } + +func generateTLSCertFromSecret(secret *secret) (*tls.Certificate, error) { + if secret.Data[tlsSecretDataCrt] == "" || secret.Data[tlsSecretDataKey] == "" { + return nil, fmt.Errorf("secret must contain %s and %s in data field", tlsSecretDataCrt, tlsSecretDataKey) + } + crt, err := b64.StdEncoding.DecodeString(secret.Data[tlsSecretDataCrt]) + if err != nil { + return nil, fmt.Errorf("failed to decode %s from secret %s", tlsSecretDataCrt, secret.Metadata.Name) + } + key, err := b64.StdEncoding.DecodeString(secret.Data[tlsSecretDataKey]) + if err != nil { + return nil, fmt.Errorf("failed to decode %s from secret %s", tlsSecretDataKey, secret.Metadata.Name) + } + cert, err := tls.X509KeyPair([]byte(crt), []byte(key)) + if err != nil { + return nil, fmt.Errorf("failed to create tls certificate from secret %s", secret.Metadata.Name) + } + if secret.Type != tlsSecretType { + return nil, fmt.Errorf("secret %s is not of type %s", secret.Metadata.Name, tlsSecretType) + } + return &cert, nil +} diff --git a/dataclients/kubernetes/ingress_test.go b/dataclients/kubernetes/ingress_test.go index d79c936044..c175160e5b 100644 --- a/dataclients/kubernetes/ingress_test.go +++ b/dataclients/kubernetes/ingress_test.go @@ -27,5 +27,6 @@ func TestIngressV1Fixtures(t *testing.T) { "testdata/ingressV1/eastwestrange", "testdata/ingressV1/service-ports", "testdata/ingressV1/external-name", + "testdata/ingressV1/tls", ) } diff --git a/dataclients/kubernetes/ingressdefinitions.go b/dataclients/kubernetes/ingressdefinitions.go index d273954af2..7e98835d18 100644 --- a/dataclients/kubernetes/ingressdefinitions.go +++ b/dataclients/kubernetes/ingressdefinitions.go @@ -172,3 +172,13 @@ type ClusterResourceList struct { // Items, aka "resources". Items []*ClusterResource `json:"resources"` } + +type secret struct { + Metadata *definitions.Metadata `json:"metadata"` + Type string `json:"type"` + Data map[string]string `json:"data"` +} + +type secretList struct { + Items []*secret `json:"items"` +} diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index 29d2a68fe9..bafc01f76e 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -9,6 +9,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/zalando/skipper/dataclients/kubernetes/definitions" "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/secrets/certregistry" ) func setPathV1(m PathMode, r *eskip.Route, pathType, path string) { @@ -311,6 +312,20 @@ func (ing *ingress) addSpecRuleV1(ic ingressContext, ru *definitions.RuleV1) err return nil } +// addSpecIngressTLSV1 is used to add TLS Certificates from Ingress resources. Certificates will be added +// only if the Ingress rule host matches a host in TLS config +func (ing *ingress) addSpecIngressTLSV1(ic ingressContext, ingtls *definitions.TLSV1) { + // Hosts in the tls section need to explicitly match the host in the rules section. + hostlist := compareStringList(ingtls.Hosts, definitions.GetHostsFromIngressRulesV1(ic.ingressV1)) + if len(hostlist) == 0 { + log.Infof("no matching tls hosts found for ingress %s", ic.ingressV1.Metadata.Name) + return + } + // Secrets should always reside in same namespace as the Ingress + secretID := &definitions.ResourceID{Name: ingtls.SecretName, Namespace: ic.ingressV1.Metadata.Namespace} + addHostTLSCert(ic, hostlist, secretID) +} + // converts the default backend if any func (ing *ingress) convertDefaultBackendV1( state *clusterState, @@ -395,6 +410,7 @@ func (ing *ingress) ingressV1Route( state *clusterState, hostRoutes map[string][]*eskip.Route, df defaultFilters, + r *certregistry.CertRegistry, ) (*eskip.Route, error) { if i.Metadata == nil || i.Metadata.Namespace == "" || i.Metadata.Name == "" || i.Spec == nil { log.Error("invalid ingress item: missing Metadata or Spec") @@ -416,6 +432,7 @@ func (ing *ingress) ingressV1Route( redirect: redirect, hostRoutes: hostRoutes, defaultFilters: df, + certificateRegistry: r, } var route *eskip.Route @@ -430,5 +447,10 @@ func (ing *ingress) ingressV1Route( return nil, err } } + if ic.certificateRegistry != nil { + for _, ingtls := range i.Spec.IngressTLS { + ing.addSpecIngressTLSV1(ic, ingtls) + } + } return route, nil } diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 2ef356a23c..17c7534f42 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -13,6 +13,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/secrets/certregistry" ) const ( @@ -180,6 +181,8 @@ type Options struct { // AllowedExternalNames contains regexp patterns of those domain names that are allowed to be // used with external name services (type=ExternalName). AllowedExternalNames []*regexp.Regexp + + CertificateRegistry *certregistry.CertRegistry } // Client is a Skipper DataClient implementation used to create routes based on Kubernetes Ingress settings. @@ -331,7 +334,7 @@ func (c *Client) loadAndConvert() ([]*eskip.Route, error) { defaultFilters := c.fetchDefaultFilterConfigs() - ri, err := c.ingress.convert(state, defaultFilters) + ri, err := c.ingress.convert(state, defaultFilters, c.ClusterClient.certificateRegistry) if err != nil { return nil, err } @@ -484,3 +487,16 @@ func (c *Client) fetchDefaultFilterConfigs() defaultFilters { log.WithField("#configs", len(filters)).Debug("default filter configurations loaded") return filters } + +func compareStringList(a, b []string) []string { + c := make([]string, 0) + for i := len(a) - 1; i >= 0; i-- { + for _, vD := range b { + if a[i] == vD { + c = append(c, vD) + break + } + } + } + return c +} diff --git a/dataclients/kubernetes/kube_test.go b/dataclients/kubernetes/kube_test.go index 7668882f32..6ae6f13db4 100644 --- a/dataclients/kubernetes/kube_test.go +++ b/dataclients/kubernetes/kube_test.go @@ -31,6 +31,7 @@ import ( "github.com/zalando/skipper/dataclients/kubernetes/definitions" "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/secrets/certregistry" ) type testAPI struct { @@ -38,6 +39,7 @@ type testAPI struct { services *serviceList endpoints *endpointList ingresses *definitions.IngressList + secrets *secretList server *httptest.Server failNext bool } @@ -93,6 +95,17 @@ func testEndpoints(namespace, name, base string, n int, ports map[string]int) [] return eps } +func testSecret(namespace, name string, clusterIP string, t string, data map[string]string) *secret { + return &secret{ + Metadata: &definitions.Metadata{ + Namespace: namespace, + Name: name, + }, + Type: t, + Data: data, + } +} + func testService(namespace, name string, clusterIP string, ports map[string]int) *service { targetPorts := make(map[int]*definitions.BackendPort) for _, v := range ports { @@ -200,6 +213,17 @@ func testIngress(ns, name, defaultService, ratelimitCfg, filterString, predicate return i } +func testSecrets() *secretList { + return &secretList{ + Items: []*secret{ + testSecret("namespace1", "secret1", "1.2.3.4", "Opaque", map[string]string{"foo": "bar"}), + testSecret("namespace1", "secret2", "5.6.7.8", "Opaque", map[string]string{"bar": "foo"}), + testSecret("namespace2", "secret3", "9.0.1.2", "Opaque", map[string]string{"foo": "foo"}), + testSecret("namespace2", "secret4", "10.0.1.2", "Opaque", map[string]string{"bar": "bar"}), + }, + } +} + func testServices() *serviceList { return &serviceList{ Items: []*service{ @@ -334,15 +358,16 @@ func checkHealthcheck(t *testing.T, got []*eskip.Route, expected, reversed bool) } func newTestAPI(t *testing.T, s *serviceList, i *definitions.IngressList) *testAPI { - return newTestAPIWithEndpoints(t, s, i, &endpointList{}) + return newTestAPIWithEndpoints(t, s, i, &endpointList{}, &secretList{}) } -func newTestAPIWithEndpoints(t *testing.T, s *serviceList, i *definitions.IngressList, e *endpointList) *testAPI { +func newTestAPIWithEndpoints(t *testing.T, s *serviceList, i *definitions.IngressList, e *endpointList, sec *secretList) *testAPI { api := &testAPI{ test: t, services: s, ingresses: i, endpoints: e, + secrets: sec, } api.server = httptest.NewServer(api) @@ -382,6 +407,11 @@ func (api *testAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { api.test.Error(err) } return + case SecretsClusterURI: + if err := respondJSON(w, api.secrets); err != nil { + api.test.Error(err) + } + return default: w.WriteHeader(http.StatusNotFound) return @@ -1337,7 +1367,7 @@ func TestConvertPathRuleTraffic(t *testing.T) { }, } { t.Run(tc.msg, func(t *testing.T) { - api := newTestAPIWithEndpoints(t, testServices(), &definitions.IngressList{}, testEndpointList()) + api := newTestAPIWithEndpoints(t, testServices(), &definitions.IngressList{}, testEndpointList(), testSecrets()) defer api.Close() dc, err := New(Options{KubernetesURL: api.server.URL}) if err != nil { @@ -2592,6 +2622,7 @@ func TestSkipperCustomRoutes(t *testing.T) { endpoints []*endpoint services []*service ingresses []*definitions.IngressItem + secrets []*secret expectedRoutes map[string]string }{{ msg: "ingress with 1 host definitions and 1 additional custom route", @@ -2763,7 +2794,7 @@ func TestSkipperCustomRoutes(t *testing.T) { t.Run(ti.msg, func(t *testing.T) { api := newTestAPIWithEndpoints(t, &serviceList{Items: ti.services}, &definitions.IngressList{Items: ti.ingresses}, &endpointList{ Items: ti.endpoints, - }) + }, &secretList{Items: ti.secrets}) defer api.Close() dc, err := New(Options{ KubernetesURL: api.server.URL, @@ -3094,6 +3125,49 @@ func TestSkipperDefaultFilters(t *testing.T) { }) } +func TestCertificateRegistry(t *testing.T) { + api := newTestAPI(t, nil, &definitions.IngressList{}) + defer api.Close() + + t.Run("certificate registry is enabled, secrets are loaded", func(t *testing.T) { + api.secrets = testSecrets() + dc, err := New(Options{KubernetesURL: api.server.URL, CertificateRegistry: &certregistry.CertRegistry{}}) + if err != nil { + t.Error(err) + } + + defer dc.Close() + + state, err := dc.ClusterClient.fetchClusterState() + if err != nil { + t.Error(err) + } + + if len(state.secrets) == 0 { + t.Errorf("no secrets were loaded") + } + }) + + t.Run("certificate registry is disabled, secrets are not loaded", func(t *testing.T) { + api.secrets = testSecrets() + dc, err := New(Options{KubernetesURL: api.server.URL}) + if err != nil { + t.Error(err) + } + + defer dc.Close() + + state, err := dc.ClusterClient.fetchClusterState() + if err != nil { + t.Error(err) + } + + if len(state.secrets) > 0 { + t.Errorf("secrets are loaded but registry is disabled") + } + }) +} + type mockSecretProvider string func (sp mockSecretProvider) GetSecret(string) ([]byte, bool) { diff --git a/dataclients/kubernetes/kubernetestest/api.go b/dataclients/kubernetes/kubernetestest/api.go index 58c37f7608..9e6819da53 100644 --- a/dataclients/kubernetes/kubernetestest/api.go +++ b/dataclients/kubernetes/kubernetestest/api.go @@ -26,6 +26,7 @@ type namespace struct { ingresses []byte routeGroups []byte endpoints []byte + secrets []byte } type api struct { @@ -41,7 +42,7 @@ func NewAPI(o TestAPIOptions, specs ...io.Reader) (*api, error) { a := &api{ namespaces: make(map[string]namespace), pathRx: regexp.MustCompile( - "(/namespaces/([^/]+))?/(services|ingresses|routegroups|endpoints)", + "(/namespaces/([^/]+))?/(services|ingresses|routegroups|endpoints|secrets)", ), } @@ -160,6 +161,8 @@ func (a *api) ServeHTTP(w http.ResponseWriter, r *http.Request) { b = ns.routeGroups case "endpoints": b = ns.endpoints + case "secrets": + b = ns.secrets default: w.WriteHeader(http.StatusNotFound) return @@ -185,6 +188,10 @@ func initNamespace(kinds map[string][]interface{}) (ns namespace, err error) { return } + if err = itemsJSON(&ns.secrets, kinds["Secret"]); err != nil { + return + } + return } diff --git a/dataclients/kubernetes/kubernetestest/api_test.go b/dataclients/kubernetes/kubernetestest/api_test.go index e69f0905d5..1724cfd662 100644 --- a/dataclients/kubernetes/kubernetestest/api_test.go +++ b/dataclients/kubernetes/kubernetestest/api_test.go @@ -113,6 +113,18 @@ subsets: - name: main port: 7878 protocol: TCP +--- +apiVersion: v1 +kind: Secret +metadata: + labels: + application: bar + name: bar + namespace: internal +data: + tls.crt: foo + tls.key: bar +type: kubernetes.io/tls ` func getJSON(u string, o interface{}) error { @@ -193,6 +205,13 @@ func TestTestAPI(t *testing.T) { } check(t, e, 1, "Endpoints") + + var sec map[string]interface{} + if err := get(fmt.Sprintf(kubernetes.SecretsNamespaceFmt, namespace), &sec); err != nil { + t.Fatal(err) + } + + check(t, sec, 1, "Secret") }) t.Run("without namespace", func(t *testing.T) { diff --git a/dataclients/kubernetes/kubernetestest/fixtures.go b/dataclients/kubernetes/kubernetestest/fixtures.go index af9eb81f27..8dce1fe26a 100644 --- a/dataclients/kubernetes/kubernetestest/fixtures.go +++ b/dataclients/kubernetes/kubernetestest/fixtures.go @@ -16,6 +16,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/zalando/skipper/dataclients/kubernetes" "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/secrets/certregistry" "gopkg.in/yaml.v2" ) @@ -42,6 +43,7 @@ type kubeOptionsParser struct { OnlyAllowedExternalNames bool `yaml:"onlyAllowedExternalNames"` AllowedExternalNames []string `yaml:"allowedExternalNames"` IngressClass string `yaml:"kubernetes-ingress-class"` + KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` } func baseNoExt(n string) string { @@ -191,6 +193,7 @@ func testFixture(t *testing.T, f fixtureSet) { }() var o kubernetes.Options + var cr *certregistry.CertRegistry if f.kube != "" { ko, err := os.Open(f.kube) if err != nil { @@ -208,6 +211,10 @@ func testFixture(t *testing.T, f fixtureSet) { t.Fatal(err) } + if kop.KubernetesEnableTLS { + cr = certregistry.NewCertRegistry() + } + o.KubernetesIngressV1 = kop.IngressV1 o.KubernetesEnableEastWest = kop.EastWest o.KubernetesEastWestDomain = kop.EastWestDomain @@ -217,6 +224,7 @@ func testFixture(t *testing.T, f fixtureSet) { o.HTTPSRedirectCode = kop.HTTPSRedirectCode o.BackendNameTracingTag = kop.BackendNameTracingTag o.IngressClass = kop.IngressClass + o.CertificateRegistry = cr aen, err := compileRegexps(kop.AllowedExternalNames) if err != nil { diff --git a/dataclients/kubernetes/orderingbug_test.go b/dataclients/kubernetes/orderingbug_test.go index bec6fa6114..7a85c08e4f 100644 --- a/dataclients/kubernetes/orderingbug_test.go +++ b/dataclients/kubernetes/orderingbug_test.go @@ -149,6 +149,9 @@ func (api apiV1) ServeHTTP(w http.ResponseWriter, r *http.Request) { case EndpointsClusterURI: w.Write([]byte("{}")) return + case SecretsClusterURI: + w.Write([]byte("{}")) + return default: w.WriteHeader(http.StatusNotFound) w.Write(nil) diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.kube b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.kube new file mode 100644 index 0000000000..c19764b796 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.kube @@ -0,0 +1,2 @@ +ingressv1: true +kubernetes-enable-tls: true diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.log b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.log new file mode 100644 index 0000000000..b104e18ac3 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.log @@ -0,0 +1 @@ +.*level=error msg="secret must contain tls.crt and tls.key in data field" diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.yaml b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.yaml new file mode 100644 index 0000000000..c18d535b7b --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-secret.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + tls: + - secretName: myapp-secret + hosts: + - example.org + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + app: myapp-deployment + name: myapp-service +subsets: + - addresses: + - ip: 10.3.0.3 + targetRef: + kind: Pod + name: myapp-deployment-6786bf95fd-fnqnq + ports: + - name: this-is-my-service-port-name + port: 80 + protocol: TCP +--- +apiVersion: v1 +kind: Secret +metadata: + name: myapp-secret + namespace: default +type: Opaque +data: + foo: bar diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.kube b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.kube new file mode 100644 index 0000000000..c19764b796 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.kube @@ -0,0 +1,2 @@ +ingressv1: true +kubernetes-enable-tls: true diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.log b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.log new file mode 100644 index 0000000000..f2aad38803 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.log @@ -0,0 +1 @@ +.*level=error msg="failed to decode tls.crt from secret myapp-secret" diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.yaml b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.yaml new file mode 100644 index 0000000000..68d3c859fc --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-invalid-tls.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + tls: + - secretName: myapp-secret + hosts: + - example.org + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + app: myapp-deployment + name: myapp-service +subsets: + - addresses: + - ip: 10.3.0.3 + targetRef: + kind: Pod + name: myapp-deployment-6786bf95fd-fnqnq + ports: + - name: this-is-my-service-port-name + port: 80 + protocol: TCP +--- +apiVersion: v1 +kind: Secret +metadata: + name: myapp-secret + namespace: default +type: kubernetes.io/tls +data: + tls.crt: thisisnotacert + tls.key: thisisnotakey diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.kube b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.kube new file mode 100644 index 0000000000..c19764b796 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.kube @@ -0,0 +1,2 @@ +ingressv1: true +kubernetes-enable-tls: true diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.log b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.log new file mode 100644 index 0000000000..89585c6ede --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.log @@ -0,0 +1 @@ +.*level=info msg="no matching tls hosts found for ingress myapp-ingress" diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.yaml b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.yaml new file mode 100644 index 0000000000..8e198369a5 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-host.yaml @@ -0,0 +1,93 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + tls: + - secretName: myapp-secret + hosts: + - foo.org + - bar.org + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + app: myapp-deployment + name: myapp-service +subsets: + - addresses: + - ip: 10.3.0.3 + targetRef: + kind: Pod + name: myapp-deployment-6786bf95fd-fnqnq + ports: + - name: this-is-my-service-port-name + port: 80 + protocol: TCP +--- +apiVersion: v1 +kind: Secret +metadata: + name: myapp-secret + namespace: default +type: kubernetes.io/tls +data: + tls.crt: | + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZhekNDQTFPZ0F3SUJBZ0lVZm9WZWI1Y3Y2alZlOC9ZQWFVaGVJejJCSXBNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TWpBek1UQXlNREU0TURSYUZ3MHlNekF6Ck1UQXlNREU0TURSYU1FVXhDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXcKSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0lpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQ0R3QXdnZ0lLQW9JQ0FRRE10ZGpVUUhzUGJDamNQMjJORmpKN3NzOXJYOVEydmloVVpLN2cvbGF4Cm1hMnpmelV6QitKUUNhdTlFZkRQUVpqVC91NWVGL29vaUtqbGI1Q25USEZMbG52eFd6N1pKN0hWYzAzTnZhWEUKUk54VmdPMXNCbkxSME9URTZRampBYW9lU29RSnFDMEI2em5KdTlNaVdUeVVsY2xWNHVocmZERGZUK1hUcHNrVgpLV2pnMG9ORCtFN01zMTlsRTRwMVYrV0dPWVRub0E1a3pvS2Z4aGpIN3R6SWZwbXFTWGJ2RGxkOWNacUJGbEtHCjBGeWFxK2pUS1lkRWtRL2xQekxJQWtaWW5NVVJDcklJYkZ3WHpuV3VqYzNQYVNnQkdCSmowVFlsN1Z6SExzM2sKL2dCdzd4N1FZZk41SGFZckhYN0FqMlQ4b2Z2a2xhcUxDRDFUaS9nM3dNVzgyR0NJVW52OW13Z3F5TFJoSHBscgpiUmV0a3BSU0JueDlOMWtac2VkaFVoR2FuS3I5MVhrajBySlRTcnBNUkZsSjdVWTlzTEMwenRwVGtMTjlvMnZpCmxrVFd0S3dKTzZXTzBsOTNDSitsWXhUcndNU3RLT0JDN2tVOXdhcVN3REQ0MHJtS1c3VTE0TlVhcTVIYlVISTYKMmY3UzBxVlRhb255VUN6OUVhVmJLaTV0SDFuVkJ1SWwzUDFRQS9RMkh1ZWo5TTU5YWlaT0lDVnhmQmJUbUNIeApyeG5pai83MWlDbTcrMWllNUxIMTI4Y2krNW1nRTQvditPZFZwM0RucVkycmFNeUFJMFBJNGVNVFlmc2tRWVFkCjlCRU8rVkI4ZGdtcnlSR2dPaHNSWFFieXFNOERYTXc4S1BrS2IxTjBwR2l1NXVNRVFKaHd4S3N6T0JGWGtyRG8KQndJREFRQUJvMU13VVRBZEJnTlZIUTRFRmdRVUVXTTh0WjRsVng0MlFMeE8xSE03dGRCZUMvOHdId1lEVlIwagpCQmd3Rm9BVUVXTTh0WjRsVng0MlFMeE8xSE03dGRCZUMvOHdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QU5CZ2txCmhraUc5dzBCQVFzRkFBT0NBZ0VBd3UwN2doaHRyUkNMZ0JuNENHbE5vVkxLRkx2SjQ3T21GUUc1eVp5MTEvdzkKaC9oblJMeDVCbk8wb1lZMmw4M0Z3OUozUWVIaThDTk84Ujc4NTRmRk56WGMxSEFZa1RWc1VZbE9wOW4wTTYwSgpKb3MyUFA5TldNTUpCcFg3Q3JQYUZRRjJoU09hb1NqaXZ1dVoxRnQrZVZEY3FWMjM0VFkrK25hYzBRc053RTYrCnpMai91TGFEM0xWUXh6Y2RuNWMrYVpSNjV0K3I0Q0RsWW1MMGVJY2RTeXF5UUtUWDlMaE1lVXQ3RUxJdEpnVkgKZDdSaDRuRU8rRDVhVEszNkZNSk9TM0VUL0Y1RksrT2QzVmgwMW9RTTJwR0dqQ3A3d2dMeWxNNTVaMWhsTnVXMQo4YWp2eHJDNWVVd2RkaTA4WWFBQjlpR1VRLzRmeUFmQkNkNjJZVVRXSUhib1NMKy84MEpySFZIQnhTaWZ0NmRFClI0SVBtbmtoakovOFcvK1g1WThvOFdrVUF3Zm00QWpOL3ZOZGUxWm1NSVFsSEZhQVRuWUJEaXBaOElSUnBndFEKcnc4d044U0NOV0plZHppdlVoYjdXdUdHbndCcDZ1Wjg1TDUzblN3SFBBS2Y3eGNhTXROVnpuZ1VJaXU1bm9PNwpZSFFzcG1xRVhQQzQ4NERmMHhTUWllUUhTWGxPUVFXS285QTQ5ZXM1NnV2ZGw0c0pTbW9uUTZMblgzV21sUGFGCmdxQSs2ZXZHanVqQmppaTBybncvUWpxY3NteHNtWU84alpGY21pZWpJL3AxUE1OalBpRWJjZGhrNFNrdDlxdnkKcDhvTXVLVzFLNHFpRUp0R1VOT3hkUEt2b1V1MWllQUtuY0FtdUhxdWNHWDBva2JrdmZjT0tYQjFoRi9kK1gwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: | + LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpSQUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1M0d2dna3FBZ0VBQW9JQ0FRRE10ZGpVUUhzUGJDamMKUDIyTkZqSjdzczlyWDlRMnZpaFVaSzdnL2xheG1hMnpmelV6QitKUUNhdTlFZkRQUVpqVC91NWVGL29vaUtqbApiNUNuVEhGTGxudnhXejdaSjdIVmMwM052YVhFUk54VmdPMXNCbkxSME9URTZRampBYW9lU29RSnFDMEI2em5KCnU5TWlXVHlVbGNsVjR1aHJmRERmVCtYVHBza1ZLV2pnMG9ORCtFN01zMTlsRTRwMVYrV0dPWVRub0E1a3pvS2YKeGhqSDd0eklmcG1xU1hidkRsZDljWnFCRmxLRzBGeWFxK2pUS1lkRWtRL2xQekxJQWtaWW5NVVJDcklJYkZ3WAp6bld1amMzUGFTZ0JHQkpqMFRZbDdWekhMczNrL2dCdzd4N1FZZk41SGFZckhYN0FqMlQ4b2Z2a2xhcUxDRDFUCmkvZzN3TVc4MkdDSVVudjltd2dxeUxSaEhwbHJiUmV0a3BSU0JueDlOMWtac2VkaFVoR2FuS3I5MVhrajBySlQKU3JwTVJGbEo3VVk5c0xDMHp0cFRrTE45bzJ2aWxrVFd0S3dKTzZXTzBsOTNDSitsWXhUcndNU3RLT0JDN2tVOQp3YXFTd0RENDBybUtXN1UxNE5VYXE1SGJVSEk2MmY3UzBxVlRhb255VUN6OUVhVmJLaTV0SDFuVkJ1SWwzUDFRCkEvUTJIdWVqOU01OWFpWk9JQ1Z4ZkJiVG1DSHhyeG5pai83MWlDbTcrMWllNUxIMTI4Y2krNW1nRTQvditPZFYKcDNEbnFZMnJhTXlBSTBQSTRlTVRZZnNrUVlRZDlCRU8rVkI4ZGdtcnlSR2dPaHNSWFFieXFNOERYTXc4S1BrSwpiMU4wcEdpdTV1TUVRSmh3eEtzek9CRlhrckRvQndJREFRQUJBb0lDQVFDZ1M4enI5MG5sZTdaTE1NZWg4TDI3Ckt2dE1ndzl6aGxlaUxlemFkWTZCSjJ0aTRMdFJxRnpJZTZvbE5RVXg1Wlc4ZXlWQVBOcEFIekxSNWhpSlNFeDIKK2ZFM3YxRnBUYkh0Q0lybURoamRwV1k2OWVmejdPQy85eWtNSDhZN3E3UUFZQzBnT3JaemlEUUtDYTk4ZUEvOQo4WVJrWW5mSW9zaktOSkFzdWE4L2lOdDlJSnAxQU4wRFlYblRkZ2UzZHdwZG5uQzV4eFUrVG83dWVYb3lKSkp4CjFPQ1MvVS9LUlpxd3VlSllMcGlVeEZlZkxmbjBUOUtDR0cwdi85ck56eW95ZlIrN0JucitLWXU3Z0ltYUU2UVQKUTY1MW5OblptNXNnKzhyb2xYZllWaVVXU2NnQk9KSWtSdGsxYmJVeEo1ZldVeWtoaHlpeUVkT2p0amk1djVPVgpzN0o5V1dzcW9qYUdacG8vTUc0VllsRzA4N0VYR1c3ZUt2U2JWL1VCTHEzcEZtUEVYUjFGVUZyL0xFT2M0MmZnCmEvOGgyQ2M2N2p6Z1EzbHpFY0VzSzZJMnZud3FESzlXR1hBd2hXZG9zUG1QVzFKQ2JOMkhXcUxHTHdJTnZCbG0KcnJQUjFQN1Rhc2ZHZE1jaER4QVdKUkJXblNUdzJyZUo0YVp5dlk5Tk1hdHdodHBvQnJrR3pGVjNYZEV6SEZxVwpjaHJDdnZKSURQdzBqaUN2akdSdnRLYWZ2WWJ5dktvaFNTQTNpMjhtYlB1TzBuSEdacEJ6OEZMTzA3d0Fmd1ZyCkdWWWNxSGdVRk5BYUp1Qld4WndMR0NGeU9sWUJadVNRL0ZONjJZQ091Vm1SRVpTWmtqekFuczhqNVBBdnBSUFgKVFJsM3dxakdrV3BlSHg3clM1OXdVUUtDQVFFQTdsZk5NdFc3b1dvNC90b0dvaE1LK3RXOWphN3JPd08wZ0VNdQo5OGV6WitOakJsRGdoOVRkTk1JMFZqekNZeGxIQUM0MnZxcHZ1TG1rSk40Rm8xY0lDaVFjS01FYitxOGRMUzFZClhXRlRKdFdMY1poUXFUampCNWlRZ1QvZFpBS3dHT3ZSQlVuRi9id0VKeEhTLzZBYk1YaWs4bVlpWWNZZkI3bTUKMkZBcUZHUmdhQjJOMXBiMmduU3hDRW12UjhHbk1Bam9MY2x3S0FHYUtwMVFmK1hCL25wVlpFVXpRL3hxSkZrWgppa21YUDZ5ekpZbXF6UnRGVHhTZDVqQ3hVMTBDMDVSREEraFUydTd0VGU4SW5sTmtpYWhud0xLOHlBUkg0Z201CjlXRmZ0KzBCQ1BDVkFHTk00NjdmcEt5VmwzODdsdG5pSkF0RG1oYXJ6Z2dNeVBidXN3S0NBUUVBMitBeStsVTIKMkFURHN2Wmp6OEc0alI3RnN4MUhYK1pmbTdtTVhKajVYMWhVekduRHZzcngwSE16Z0Zxbk9pYTlrNXpBaFhNSQpwZnl5RitPR0RVb3c3eXJzWGtTeWZDWTYyUVBSRkd2Z01qMUhoYUUwQzlkSWVXZmNlOTVlUUFVZ0FsVjlDRkQrCjlaYmtUMzZPZy9VTUVWU0dkdUhFVVFiRW52Qnp2Zm9UOVJkcEQ3eWd2Sk03T0RFR1A2Z1djNkVXOUcrWXpJNG8Ka2k4SEVRbDJRa1N2cTJkVVBjSzNoZVFvbHJCWERUWk9QeS8vaG1jbkpnbEMvUW1nSUdyWHhQN0F3TjkrU2hYSAo4d1RMcVd1cll1eWpQdkkraXlLR0tHaTk2NmhwNW5JVDdjcllFOStJWlpQQWJrbFRXUVg0SGNrYzhtMTFMZkJQCjAxT2NZMit4clp1TFhRS0NBUUVBcEIzVFR3aTdQVWVPWFhZMWtRNTV4Z1M5bER6NC90YnJTRko2bWVWcDFNUlAKWUg3NlRLMjNiK2UxOEJmQVprcDJpRnBLR2ZuMEdnZkNUaHlQVjB6TFhXaEY5NDRaUFFHdG5ua1YycDcwaWM0TApTYm51K01jU0ZSM3Bpd1kxNVBLdzNVZ3IwbTlkSlAxOUFvWVVleTU4NnhDK3k2YW1VQnNETE9lblg1cTdqdlViCktUWUlmOVhOZ2tEbDBlWWpDczcrMTJXYXNrUjl0UjU5VUpDb2FKa0Zmcnd2NW01OEFYbGlnUXJWT2xLNEVnRlMKRGl3QWIyRXkxV1JGNGNadnBBNXNydEh0WDFod2JaeU56TmNtWVJiZEtLak1ZSFR5NXV1RHI5S3d1SlZIT3JlNAp1YlluYzIvczl1NW9VdFQvNEtTY25LQUZSbnAzSHpnekx4aDk3VGVUWHdLQ0FRRUF3TTBRaG5oUWRnMS9lUjhhCm1LTzY2MnZQV2VkVG5lRUpkeWkxenNDSThyVW03blBUcENxYTdma0djUWVNMmEzODBFSkVnd0JDMWlJR0hISnoKS3BZaTRLV1h6SFdhdU1oaEU4aUgvc3MxTlhpTWpiMjBRS25QTUQ0RmxVeUJBc3c3ckRCQVNobVQ1OUFmZFNGNQpZSFp3MVlWenZ5enJFMDNHL2NQRkNoSU9pL3l5TUkxcnVNKzF2dWttSEkyTTJtbW9Fa0VGRUdHYmE4djIrMVo3CnIxSkJaQ0JnT3lQUi80TDRvR0lTZzFCYVBvZ2RIVUs0amw3U3NjVk45djhaSXZGc0hmUWI3bVM1QnZ6dWhTb1gKaDlBT3VYUjdxVTlscW10bUZnMkFod1VET3FHQzViSStEU3dKTWV2MFBQekIrNFJOY0xyUVpLN3pvRkFSc3hQUApEbmQxTlFLQ0FRQXlJM0xCOUQxUlg1NkhEYmI4WTZobk1wM2xTTmU3aWxpNy8zMjNCczB2QTZ6R0hod2o4ajlnCjVHcEJSd2ZXdCszUkRvZlQrL1h3K1MxTmdUTVJxWkw3M3V3VjdIWnhaTkE4NU5TUDk2bG5EbmpXN3NzTFhtY0wKRFRNQy83UVBaOU1xYmt4SXNDV0wwaml2bSs0a0UrSStqWXlHdkx0a2hlU1NHOU5pOGM1SzlwdjF1Z3ZVOUpwSAo0QlRSblhCd2JOUnVMZ3JpcHJwZjJKS2dYQmlPWEFTZUVmbzMvSTRETjZISWxwUFdoRDkwb0MyaWhvU3JrL2lVCmFMSnVCUmhncDJBelFYMWlBMDcxczNJR3ZlUERnN1RYd2Y4ZDM2bFhnVWNTTEJ0N2FmVzNoOGordEVCZ1FQTEwKVk9seXgzYzVNdVpaZDhHWXBKNGUwUDFJYWJzN2ZwZEEKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo= diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.kube b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.kube new file mode 100644 index 0000000000..c19764b796 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.kube @@ -0,0 +1,2 @@ +ingressv1: true +kubernetes-enable-tls: true diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.log b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.log new file mode 100644 index 0000000000..eac8f8e2e2 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.log @@ -0,0 +1 @@ +.*level=error msg="failed to find secret myapp-secret in namespace default" diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.yaml b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.yaml new file mode 100644 index 0000000000..c27d9b5dc5 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-missing-secret.yaml @@ -0,0 +1,80 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + tls: + - secretName: myapp-secret + hosts: + - example.org + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + app: myapp-deployment + name: myapp-service +subsets: + - addresses: + - ip: 10.3.0.3 + targetRef: + kind: Pod + name: myapp-deployment-6786bf95fd-fnqnq + ports: + - name: this-is-my-service-port-name + port: 80 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.kube b/dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.kube new file mode 100644 index 0000000000..c19764b796 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.kube @@ -0,0 +1,2 @@ +ingressv1: true +kubernetes-enable-tls: true diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.yaml b/dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.yaml new file mode 100644 index 0000000000..eed9faf51c --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-multiple-host.yaml @@ -0,0 +1,102 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + tls: + - secretName: myapp-secret + hosts: + - example.org + - foo.org + rules: + - host: foo.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + app: myapp-deployment + name: myapp-service +subsets: + - addresses: + - ip: 10.3.0.3 + targetRef: + kind: Pod + name: myapp-deployment-6786bf95fd-fnqnq + ports: + - name: this-is-my-service-port-name + port: 80 + protocol: TCP +--- +apiVersion: v1 +kind: Secret +metadata: + name: myapp-secret + namespace: default +type: kubernetes.io/tls +data: + tls.crt: | + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZhekNDQTFPZ0F3SUJBZ0lVZm9WZWI1Y3Y2alZlOC9ZQWFVaGVJejJCSXBNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TWpBek1UQXlNREU0TURSYUZ3MHlNekF6Ck1UQXlNREU0TURSYU1FVXhDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXcKSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0lpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQ0R3QXdnZ0lLQW9JQ0FRRE10ZGpVUUhzUGJDamNQMjJORmpKN3NzOXJYOVEydmloVVpLN2cvbGF4Cm1hMnpmelV6QitKUUNhdTlFZkRQUVpqVC91NWVGL29vaUtqbGI1Q25USEZMbG52eFd6N1pKN0hWYzAzTnZhWEUKUk54VmdPMXNCbkxSME9URTZRampBYW9lU29RSnFDMEI2em5KdTlNaVdUeVVsY2xWNHVocmZERGZUK1hUcHNrVgpLV2pnMG9ORCtFN01zMTlsRTRwMVYrV0dPWVRub0E1a3pvS2Z4aGpIN3R6SWZwbXFTWGJ2RGxkOWNacUJGbEtHCjBGeWFxK2pUS1lkRWtRL2xQekxJQWtaWW5NVVJDcklJYkZ3WHpuV3VqYzNQYVNnQkdCSmowVFlsN1Z6SExzM2sKL2dCdzd4N1FZZk41SGFZckhYN0FqMlQ4b2Z2a2xhcUxDRDFUaS9nM3dNVzgyR0NJVW52OW13Z3F5TFJoSHBscgpiUmV0a3BSU0JueDlOMWtac2VkaFVoR2FuS3I5MVhrajBySlRTcnBNUkZsSjdVWTlzTEMwenRwVGtMTjlvMnZpCmxrVFd0S3dKTzZXTzBsOTNDSitsWXhUcndNU3RLT0JDN2tVOXdhcVN3REQ0MHJtS1c3VTE0TlVhcTVIYlVISTYKMmY3UzBxVlRhb255VUN6OUVhVmJLaTV0SDFuVkJ1SWwzUDFRQS9RMkh1ZWo5TTU5YWlaT0lDVnhmQmJUbUNIeApyeG5pai83MWlDbTcrMWllNUxIMTI4Y2krNW1nRTQvditPZFZwM0RucVkycmFNeUFJMFBJNGVNVFlmc2tRWVFkCjlCRU8rVkI4ZGdtcnlSR2dPaHNSWFFieXFNOERYTXc4S1BrS2IxTjBwR2l1NXVNRVFKaHd4S3N6T0JGWGtyRG8KQndJREFRQUJvMU13VVRBZEJnTlZIUTRFRmdRVUVXTTh0WjRsVng0MlFMeE8xSE03dGRCZUMvOHdId1lEVlIwagpCQmd3Rm9BVUVXTTh0WjRsVng0MlFMeE8xSE03dGRCZUMvOHdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QU5CZ2txCmhraUc5dzBCQVFzRkFBT0NBZ0VBd3UwN2doaHRyUkNMZ0JuNENHbE5vVkxLRkx2SjQ3T21GUUc1eVp5MTEvdzkKaC9oblJMeDVCbk8wb1lZMmw4M0Z3OUozUWVIaThDTk84Ujc4NTRmRk56WGMxSEFZa1RWc1VZbE9wOW4wTTYwSgpKb3MyUFA5TldNTUpCcFg3Q3JQYUZRRjJoU09hb1NqaXZ1dVoxRnQrZVZEY3FWMjM0VFkrK25hYzBRc053RTYrCnpMai91TGFEM0xWUXh6Y2RuNWMrYVpSNjV0K3I0Q0RsWW1MMGVJY2RTeXF5UUtUWDlMaE1lVXQ3RUxJdEpnVkgKZDdSaDRuRU8rRDVhVEszNkZNSk9TM0VUL0Y1RksrT2QzVmgwMW9RTTJwR0dqQ3A3d2dMeWxNNTVaMWhsTnVXMQo4YWp2eHJDNWVVd2RkaTA4WWFBQjlpR1VRLzRmeUFmQkNkNjJZVVRXSUhib1NMKy84MEpySFZIQnhTaWZ0NmRFClI0SVBtbmtoakovOFcvK1g1WThvOFdrVUF3Zm00QWpOL3ZOZGUxWm1NSVFsSEZhQVRuWUJEaXBaOElSUnBndFEKcnc4d044U0NOV0plZHppdlVoYjdXdUdHbndCcDZ1Wjg1TDUzblN3SFBBS2Y3eGNhTXROVnpuZ1VJaXU1bm9PNwpZSFFzcG1xRVhQQzQ4NERmMHhTUWllUUhTWGxPUVFXS285QTQ5ZXM1NnV2ZGw0c0pTbW9uUTZMblgzV21sUGFGCmdxQSs2ZXZHanVqQmppaTBybncvUWpxY3NteHNtWU84alpGY21pZWpJL3AxUE1OalBpRWJjZGhrNFNrdDlxdnkKcDhvTXVLVzFLNHFpRUp0R1VOT3hkUEt2b1V1MWllQUtuY0FtdUhxdWNHWDBva2JrdmZjT0tYQjFoRi9kK1gwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: | + LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpSQUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1M0d2dna3FBZ0VBQW9JQ0FRRE10ZGpVUUhzUGJDamMKUDIyTkZqSjdzczlyWDlRMnZpaFVaSzdnL2xheG1hMnpmelV6QitKUUNhdTlFZkRQUVpqVC91NWVGL29vaUtqbApiNUNuVEhGTGxudnhXejdaSjdIVmMwM052YVhFUk54VmdPMXNCbkxSME9URTZRampBYW9lU29RSnFDMEI2em5KCnU5TWlXVHlVbGNsVjR1aHJmRERmVCtYVHBza1ZLV2pnMG9ORCtFN01zMTlsRTRwMVYrV0dPWVRub0E1a3pvS2YKeGhqSDd0eklmcG1xU1hidkRsZDljWnFCRmxLRzBGeWFxK2pUS1lkRWtRL2xQekxJQWtaWW5NVVJDcklJYkZ3WAp6bld1amMzUGFTZ0JHQkpqMFRZbDdWekhMczNrL2dCdzd4N1FZZk41SGFZckhYN0FqMlQ4b2Z2a2xhcUxDRDFUCmkvZzN3TVc4MkdDSVVudjltd2dxeUxSaEhwbHJiUmV0a3BSU0JueDlOMWtac2VkaFVoR2FuS3I5MVhrajBySlQKU3JwTVJGbEo3VVk5c0xDMHp0cFRrTE45bzJ2aWxrVFd0S3dKTzZXTzBsOTNDSitsWXhUcndNU3RLT0JDN2tVOQp3YXFTd0RENDBybUtXN1UxNE5VYXE1SGJVSEk2MmY3UzBxVlRhb255VUN6OUVhVmJLaTV0SDFuVkJ1SWwzUDFRCkEvUTJIdWVqOU01OWFpWk9JQ1Z4ZkJiVG1DSHhyeG5pai83MWlDbTcrMWllNUxIMTI4Y2krNW1nRTQvditPZFYKcDNEbnFZMnJhTXlBSTBQSTRlTVRZZnNrUVlRZDlCRU8rVkI4ZGdtcnlSR2dPaHNSWFFieXFNOERYTXc4S1BrSwpiMU4wcEdpdTV1TUVRSmh3eEtzek9CRlhrckRvQndJREFRQUJBb0lDQVFDZ1M4enI5MG5sZTdaTE1NZWg4TDI3Ckt2dE1ndzl6aGxlaUxlemFkWTZCSjJ0aTRMdFJxRnpJZTZvbE5RVXg1Wlc4ZXlWQVBOcEFIekxSNWhpSlNFeDIKK2ZFM3YxRnBUYkh0Q0lybURoamRwV1k2OWVmejdPQy85eWtNSDhZN3E3UUFZQzBnT3JaemlEUUtDYTk4ZUEvOQo4WVJrWW5mSW9zaktOSkFzdWE4L2lOdDlJSnAxQU4wRFlYblRkZ2UzZHdwZG5uQzV4eFUrVG83dWVYb3lKSkp4CjFPQ1MvVS9LUlpxd3VlSllMcGlVeEZlZkxmbjBUOUtDR0cwdi85ck56eW95ZlIrN0JucitLWXU3Z0ltYUU2UVQKUTY1MW5OblptNXNnKzhyb2xYZllWaVVXU2NnQk9KSWtSdGsxYmJVeEo1ZldVeWtoaHlpeUVkT2p0amk1djVPVgpzN0o5V1dzcW9qYUdacG8vTUc0VllsRzA4N0VYR1c3ZUt2U2JWL1VCTHEzcEZtUEVYUjFGVUZyL0xFT2M0MmZnCmEvOGgyQ2M2N2p6Z1EzbHpFY0VzSzZJMnZud3FESzlXR1hBd2hXZG9zUG1QVzFKQ2JOMkhXcUxHTHdJTnZCbG0KcnJQUjFQN1Rhc2ZHZE1jaER4QVdKUkJXblNUdzJyZUo0YVp5dlk5Tk1hdHdodHBvQnJrR3pGVjNYZEV6SEZxVwpjaHJDdnZKSURQdzBqaUN2akdSdnRLYWZ2WWJ5dktvaFNTQTNpMjhtYlB1TzBuSEdacEJ6OEZMTzA3d0Fmd1ZyCkdWWWNxSGdVRk5BYUp1Qld4WndMR0NGeU9sWUJadVNRL0ZONjJZQ091Vm1SRVpTWmtqekFuczhqNVBBdnBSUFgKVFJsM3dxakdrV3BlSHg3clM1OXdVUUtDQVFFQTdsZk5NdFc3b1dvNC90b0dvaE1LK3RXOWphN3JPd08wZ0VNdQo5OGV6WitOakJsRGdoOVRkTk1JMFZqekNZeGxIQUM0MnZxcHZ1TG1rSk40Rm8xY0lDaVFjS01FYitxOGRMUzFZClhXRlRKdFdMY1poUXFUampCNWlRZ1QvZFpBS3dHT3ZSQlVuRi9id0VKeEhTLzZBYk1YaWs4bVlpWWNZZkI3bTUKMkZBcUZHUmdhQjJOMXBiMmduU3hDRW12UjhHbk1Bam9MY2x3S0FHYUtwMVFmK1hCL25wVlpFVXpRL3hxSkZrWgppa21YUDZ5ekpZbXF6UnRGVHhTZDVqQ3hVMTBDMDVSREEraFUydTd0VGU4SW5sTmtpYWhud0xLOHlBUkg0Z201CjlXRmZ0KzBCQ1BDVkFHTk00NjdmcEt5VmwzODdsdG5pSkF0RG1oYXJ6Z2dNeVBidXN3S0NBUUVBMitBeStsVTIKMkFURHN2Wmp6OEc0alI3RnN4MUhYK1pmbTdtTVhKajVYMWhVekduRHZzcngwSE16Z0Zxbk9pYTlrNXpBaFhNSQpwZnl5RitPR0RVb3c3eXJzWGtTeWZDWTYyUVBSRkd2Z01qMUhoYUUwQzlkSWVXZmNlOTVlUUFVZ0FsVjlDRkQrCjlaYmtUMzZPZy9VTUVWU0dkdUhFVVFiRW52Qnp2Zm9UOVJkcEQ3eWd2Sk03T0RFR1A2Z1djNkVXOUcrWXpJNG8Ka2k4SEVRbDJRa1N2cTJkVVBjSzNoZVFvbHJCWERUWk9QeS8vaG1jbkpnbEMvUW1nSUdyWHhQN0F3TjkrU2hYSAo4d1RMcVd1cll1eWpQdkkraXlLR0tHaTk2NmhwNW5JVDdjcllFOStJWlpQQWJrbFRXUVg0SGNrYzhtMTFMZkJQCjAxT2NZMit4clp1TFhRS0NBUUVBcEIzVFR3aTdQVWVPWFhZMWtRNTV4Z1M5bER6NC90YnJTRko2bWVWcDFNUlAKWUg3NlRLMjNiK2UxOEJmQVprcDJpRnBLR2ZuMEdnZkNUaHlQVjB6TFhXaEY5NDRaUFFHdG5ua1YycDcwaWM0TApTYm51K01jU0ZSM3Bpd1kxNVBLdzNVZ3IwbTlkSlAxOUFvWVVleTU4NnhDK3k2YW1VQnNETE9lblg1cTdqdlViCktUWUlmOVhOZ2tEbDBlWWpDczcrMTJXYXNrUjl0UjU5VUpDb2FKa0Zmcnd2NW01OEFYbGlnUXJWT2xLNEVnRlMKRGl3QWIyRXkxV1JGNGNadnBBNXNydEh0WDFod2JaeU56TmNtWVJiZEtLak1ZSFR5NXV1RHI5S3d1SlZIT3JlNAp1YlluYzIvczl1NW9VdFQvNEtTY25LQUZSbnAzSHpnekx4aDk3VGVUWHdLQ0FRRUF3TTBRaG5oUWRnMS9lUjhhCm1LTzY2MnZQV2VkVG5lRUpkeWkxenNDSThyVW03blBUcENxYTdma0djUWVNMmEzODBFSkVnd0JDMWlJR0hISnoKS3BZaTRLV1h6SFdhdU1oaEU4aUgvc3MxTlhpTWpiMjBRS25QTUQ0RmxVeUJBc3c3ckRCQVNobVQ1OUFmZFNGNQpZSFp3MVlWenZ5enJFMDNHL2NQRkNoSU9pL3l5TUkxcnVNKzF2dWttSEkyTTJtbW9Fa0VGRUdHYmE4djIrMVo3CnIxSkJaQ0JnT3lQUi80TDRvR0lTZzFCYVBvZ2RIVUs0amw3U3NjVk45djhaSXZGc0hmUWI3bVM1QnZ6dWhTb1gKaDlBT3VYUjdxVTlscW10bUZnMkFod1VET3FHQzViSStEU3dKTWV2MFBQekIrNFJOY0xyUVpLN3pvRkFSc3hQUApEbmQxTlFLQ0FRQXlJM0xCOUQxUlg1NkhEYmI4WTZobk1wM2xTTmU3aWxpNy8zMjNCczB2QTZ6R0hod2o4ajlnCjVHcEJSd2ZXdCszUkRvZlQrL1h3K1MxTmdUTVJxWkw3M3V3VjdIWnhaTkE4NU5TUDk2bG5EbmpXN3NzTFhtY0wKRFRNQy83UVBaOU1xYmt4SXNDV0wwaml2bSs0a0UrSStqWXlHdkx0a2hlU1NHOU5pOGM1SzlwdjF1Z3ZVOUpwSAo0QlRSblhCd2JOUnVMZ3JpcHJwZjJKS2dYQmlPWEFTZUVmbzMvSTRETjZISWxwUFdoRDkwb0MyaWhvU3JrL2lVCmFMSnVCUmhncDJBelFYMWlBMDcxczNJR3ZlUERnN1RYd2Y4ZDM2bFhnVWNTTEJ0N2FmVzNoOGordEVCZ1FQTEwKVk9seXgzYzVNdVpaZDhHWXBKNGUwUDFJYWJzN2ZwZEEKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo= diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.kube b/dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.kube new file mode 100644 index 0000000000..c19764b796 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.kube @@ -0,0 +1,2 @@ +ingressv1: true +kubernetes-enable-tls: true diff --git a/dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.yaml b/dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.yaml new file mode 100644 index 0000000000..4d862bd3b3 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/tls/tls-single-host.yaml @@ -0,0 +1,92 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + tls: + - secretName: myapp-secret + hosts: + - example.org + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + app: myapp-deployment + name: myapp-service +subsets: + - addresses: + - ip: 10.3.0.3 + targetRef: + kind: Pod + name: myapp-deployment-6786bf95fd-fnqnq + ports: + - name: this-is-my-service-port-name + port: 80 + protocol: TCP +--- +apiVersion: v1 +kind: Secret +metadata: + name: myapp-secret + namespace: default +type: kubernetes.io/tls +data: + tls.crt: | + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZhekNDQTFPZ0F3SUJBZ0lVZm9WZWI1Y3Y2alZlOC9ZQWFVaGVJejJCSXBNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JURUxNQWtHQTFVRUJoTUNRVlV4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTQpHRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB5TWpBek1UQXlNREU0TURSYUZ3MHlNekF6Ck1UQXlNREU0TURSYU1FVXhDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXcKSHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0lpTUEwR0NTcUdTSWIzRFFFQgpBUVVBQTRJQ0R3QXdnZ0lLQW9JQ0FRRE10ZGpVUUhzUGJDamNQMjJORmpKN3NzOXJYOVEydmloVVpLN2cvbGF4Cm1hMnpmelV6QitKUUNhdTlFZkRQUVpqVC91NWVGL29vaUtqbGI1Q25USEZMbG52eFd6N1pKN0hWYzAzTnZhWEUKUk54VmdPMXNCbkxSME9URTZRampBYW9lU29RSnFDMEI2em5KdTlNaVdUeVVsY2xWNHVocmZERGZUK1hUcHNrVgpLV2pnMG9ORCtFN01zMTlsRTRwMVYrV0dPWVRub0E1a3pvS2Z4aGpIN3R6SWZwbXFTWGJ2RGxkOWNacUJGbEtHCjBGeWFxK2pUS1lkRWtRL2xQekxJQWtaWW5NVVJDcklJYkZ3WHpuV3VqYzNQYVNnQkdCSmowVFlsN1Z6SExzM2sKL2dCdzd4N1FZZk41SGFZckhYN0FqMlQ4b2Z2a2xhcUxDRDFUaS9nM3dNVzgyR0NJVW52OW13Z3F5TFJoSHBscgpiUmV0a3BSU0JueDlOMWtac2VkaFVoR2FuS3I5MVhrajBySlRTcnBNUkZsSjdVWTlzTEMwenRwVGtMTjlvMnZpCmxrVFd0S3dKTzZXTzBsOTNDSitsWXhUcndNU3RLT0JDN2tVOXdhcVN3REQ0MHJtS1c3VTE0TlVhcTVIYlVISTYKMmY3UzBxVlRhb255VUN6OUVhVmJLaTV0SDFuVkJ1SWwzUDFRQS9RMkh1ZWo5TTU5YWlaT0lDVnhmQmJUbUNIeApyeG5pai83MWlDbTcrMWllNUxIMTI4Y2krNW1nRTQvditPZFZwM0RucVkycmFNeUFJMFBJNGVNVFlmc2tRWVFkCjlCRU8rVkI4ZGdtcnlSR2dPaHNSWFFieXFNOERYTXc4S1BrS2IxTjBwR2l1NXVNRVFKaHd4S3N6T0JGWGtyRG8KQndJREFRQUJvMU13VVRBZEJnTlZIUTRFRmdRVUVXTTh0WjRsVng0MlFMeE8xSE03dGRCZUMvOHdId1lEVlIwagpCQmd3Rm9BVUVXTTh0WjRsVng0MlFMeE8xSE03dGRCZUMvOHdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QU5CZ2txCmhraUc5dzBCQVFzRkFBT0NBZ0VBd3UwN2doaHRyUkNMZ0JuNENHbE5vVkxLRkx2SjQ3T21GUUc1eVp5MTEvdzkKaC9oblJMeDVCbk8wb1lZMmw4M0Z3OUozUWVIaThDTk84Ujc4NTRmRk56WGMxSEFZa1RWc1VZbE9wOW4wTTYwSgpKb3MyUFA5TldNTUpCcFg3Q3JQYUZRRjJoU09hb1NqaXZ1dVoxRnQrZVZEY3FWMjM0VFkrK25hYzBRc053RTYrCnpMai91TGFEM0xWUXh6Y2RuNWMrYVpSNjV0K3I0Q0RsWW1MMGVJY2RTeXF5UUtUWDlMaE1lVXQ3RUxJdEpnVkgKZDdSaDRuRU8rRDVhVEszNkZNSk9TM0VUL0Y1RksrT2QzVmgwMW9RTTJwR0dqQ3A3d2dMeWxNNTVaMWhsTnVXMQo4YWp2eHJDNWVVd2RkaTA4WWFBQjlpR1VRLzRmeUFmQkNkNjJZVVRXSUhib1NMKy84MEpySFZIQnhTaWZ0NmRFClI0SVBtbmtoakovOFcvK1g1WThvOFdrVUF3Zm00QWpOL3ZOZGUxWm1NSVFsSEZhQVRuWUJEaXBaOElSUnBndFEKcnc4d044U0NOV0plZHppdlVoYjdXdUdHbndCcDZ1Wjg1TDUzblN3SFBBS2Y3eGNhTXROVnpuZ1VJaXU1bm9PNwpZSFFzcG1xRVhQQzQ4NERmMHhTUWllUUhTWGxPUVFXS285QTQ5ZXM1NnV2ZGw0c0pTbW9uUTZMblgzV21sUGFGCmdxQSs2ZXZHanVqQmppaTBybncvUWpxY3NteHNtWU84alpGY21pZWpJL3AxUE1OalBpRWJjZGhrNFNrdDlxdnkKcDhvTXVLVzFLNHFpRUp0R1VOT3hkUEt2b1V1MWllQUtuY0FtdUhxdWNHWDBva2JrdmZjT0tYQjFoRi9kK1gwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: | + LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpSQUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1M0d2dna3FBZ0VBQW9JQ0FRRE10ZGpVUUhzUGJDamMKUDIyTkZqSjdzczlyWDlRMnZpaFVaSzdnL2xheG1hMnpmelV6QitKUUNhdTlFZkRQUVpqVC91NWVGL29vaUtqbApiNUNuVEhGTGxudnhXejdaSjdIVmMwM052YVhFUk54VmdPMXNCbkxSME9URTZRampBYW9lU29RSnFDMEI2em5KCnU5TWlXVHlVbGNsVjR1aHJmRERmVCtYVHBza1ZLV2pnMG9ORCtFN01zMTlsRTRwMVYrV0dPWVRub0E1a3pvS2YKeGhqSDd0eklmcG1xU1hidkRsZDljWnFCRmxLRzBGeWFxK2pUS1lkRWtRL2xQekxJQWtaWW5NVVJDcklJYkZ3WAp6bld1amMzUGFTZ0JHQkpqMFRZbDdWekhMczNrL2dCdzd4N1FZZk41SGFZckhYN0FqMlQ4b2Z2a2xhcUxDRDFUCmkvZzN3TVc4MkdDSVVudjltd2dxeUxSaEhwbHJiUmV0a3BSU0JueDlOMWtac2VkaFVoR2FuS3I5MVhrajBySlQKU3JwTVJGbEo3VVk5c0xDMHp0cFRrTE45bzJ2aWxrVFd0S3dKTzZXTzBsOTNDSitsWXhUcndNU3RLT0JDN2tVOQp3YXFTd0RENDBybUtXN1UxNE5VYXE1SGJVSEk2MmY3UzBxVlRhb255VUN6OUVhVmJLaTV0SDFuVkJ1SWwzUDFRCkEvUTJIdWVqOU01OWFpWk9JQ1Z4ZkJiVG1DSHhyeG5pai83MWlDbTcrMWllNUxIMTI4Y2krNW1nRTQvditPZFYKcDNEbnFZMnJhTXlBSTBQSTRlTVRZZnNrUVlRZDlCRU8rVkI4ZGdtcnlSR2dPaHNSWFFieXFNOERYTXc4S1BrSwpiMU4wcEdpdTV1TUVRSmh3eEtzek9CRlhrckRvQndJREFRQUJBb0lDQVFDZ1M4enI5MG5sZTdaTE1NZWg4TDI3Ckt2dE1ndzl6aGxlaUxlemFkWTZCSjJ0aTRMdFJxRnpJZTZvbE5RVXg1Wlc4ZXlWQVBOcEFIekxSNWhpSlNFeDIKK2ZFM3YxRnBUYkh0Q0lybURoamRwV1k2OWVmejdPQy85eWtNSDhZN3E3UUFZQzBnT3JaemlEUUtDYTk4ZUEvOQo4WVJrWW5mSW9zaktOSkFzdWE4L2lOdDlJSnAxQU4wRFlYblRkZ2UzZHdwZG5uQzV4eFUrVG83dWVYb3lKSkp4CjFPQ1MvVS9LUlpxd3VlSllMcGlVeEZlZkxmbjBUOUtDR0cwdi85ck56eW95ZlIrN0JucitLWXU3Z0ltYUU2UVQKUTY1MW5OblptNXNnKzhyb2xYZllWaVVXU2NnQk9KSWtSdGsxYmJVeEo1ZldVeWtoaHlpeUVkT2p0amk1djVPVgpzN0o5V1dzcW9qYUdacG8vTUc0VllsRzA4N0VYR1c3ZUt2U2JWL1VCTHEzcEZtUEVYUjFGVUZyL0xFT2M0MmZnCmEvOGgyQ2M2N2p6Z1EzbHpFY0VzSzZJMnZud3FESzlXR1hBd2hXZG9zUG1QVzFKQ2JOMkhXcUxHTHdJTnZCbG0KcnJQUjFQN1Rhc2ZHZE1jaER4QVdKUkJXblNUdzJyZUo0YVp5dlk5Tk1hdHdodHBvQnJrR3pGVjNYZEV6SEZxVwpjaHJDdnZKSURQdzBqaUN2akdSdnRLYWZ2WWJ5dktvaFNTQTNpMjhtYlB1TzBuSEdacEJ6OEZMTzA3d0Fmd1ZyCkdWWWNxSGdVRk5BYUp1Qld4WndMR0NGeU9sWUJadVNRL0ZONjJZQ091Vm1SRVpTWmtqekFuczhqNVBBdnBSUFgKVFJsM3dxakdrV3BlSHg3clM1OXdVUUtDQVFFQTdsZk5NdFc3b1dvNC90b0dvaE1LK3RXOWphN3JPd08wZ0VNdQo5OGV6WitOakJsRGdoOVRkTk1JMFZqekNZeGxIQUM0MnZxcHZ1TG1rSk40Rm8xY0lDaVFjS01FYitxOGRMUzFZClhXRlRKdFdMY1poUXFUampCNWlRZ1QvZFpBS3dHT3ZSQlVuRi9id0VKeEhTLzZBYk1YaWs4bVlpWWNZZkI3bTUKMkZBcUZHUmdhQjJOMXBiMmduU3hDRW12UjhHbk1Bam9MY2x3S0FHYUtwMVFmK1hCL25wVlpFVXpRL3hxSkZrWgppa21YUDZ5ekpZbXF6UnRGVHhTZDVqQ3hVMTBDMDVSREEraFUydTd0VGU4SW5sTmtpYWhud0xLOHlBUkg0Z201CjlXRmZ0KzBCQ1BDVkFHTk00NjdmcEt5VmwzODdsdG5pSkF0RG1oYXJ6Z2dNeVBidXN3S0NBUUVBMitBeStsVTIKMkFURHN2Wmp6OEc0alI3RnN4MUhYK1pmbTdtTVhKajVYMWhVekduRHZzcngwSE16Z0Zxbk9pYTlrNXpBaFhNSQpwZnl5RitPR0RVb3c3eXJzWGtTeWZDWTYyUVBSRkd2Z01qMUhoYUUwQzlkSWVXZmNlOTVlUUFVZ0FsVjlDRkQrCjlaYmtUMzZPZy9VTUVWU0dkdUhFVVFiRW52Qnp2Zm9UOVJkcEQ3eWd2Sk03T0RFR1A2Z1djNkVXOUcrWXpJNG8Ka2k4SEVRbDJRa1N2cTJkVVBjSzNoZVFvbHJCWERUWk9QeS8vaG1jbkpnbEMvUW1nSUdyWHhQN0F3TjkrU2hYSAo4d1RMcVd1cll1eWpQdkkraXlLR0tHaTk2NmhwNW5JVDdjcllFOStJWlpQQWJrbFRXUVg0SGNrYzhtMTFMZkJQCjAxT2NZMit4clp1TFhRS0NBUUVBcEIzVFR3aTdQVWVPWFhZMWtRNTV4Z1M5bER6NC90YnJTRko2bWVWcDFNUlAKWUg3NlRLMjNiK2UxOEJmQVprcDJpRnBLR2ZuMEdnZkNUaHlQVjB6TFhXaEY5NDRaUFFHdG5ua1YycDcwaWM0TApTYm51K01jU0ZSM3Bpd1kxNVBLdzNVZ3IwbTlkSlAxOUFvWVVleTU4NnhDK3k2YW1VQnNETE9lblg1cTdqdlViCktUWUlmOVhOZ2tEbDBlWWpDczcrMTJXYXNrUjl0UjU5VUpDb2FKa0Zmcnd2NW01OEFYbGlnUXJWT2xLNEVnRlMKRGl3QWIyRXkxV1JGNGNadnBBNXNydEh0WDFod2JaeU56TmNtWVJiZEtLak1ZSFR5NXV1RHI5S3d1SlZIT3JlNAp1YlluYzIvczl1NW9VdFQvNEtTY25LQUZSbnAzSHpnekx4aDk3VGVUWHdLQ0FRRUF3TTBRaG5oUWRnMS9lUjhhCm1LTzY2MnZQV2VkVG5lRUpkeWkxenNDSThyVW03blBUcENxYTdma0djUWVNMmEzODBFSkVnd0JDMWlJR0hISnoKS3BZaTRLV1h6SFdhdU1oaEU4aUgvc3MxTlhpTWpiMjBRS25QTUQ0RmxVeUJBc3c3ckRCQVNobVQ1OUFmZFNGNQpZSFp3MVlWenZ5enJFMDNHL2NQRkNoSU9pL3l5TUkxcnVNKzF2dWttSEkyTTJtbW9Fa0VGRUdHYmE4djIrMVo3CnIxSkJaQ0JnT3lQUi80TDRvR0lTZzFCYVBvZ2RIVUs0amw3U3NjVk45djhaSXZGc0hmUWI3bVM1QnZ6dWhTb1gKaDlBT3VYUjdxVTlscW10bUZnMkFod1VET3FHQzViSStEU3dKTWV2MFBQekIrNFJOY0xyUVpLN3pvRkFSc3hQUApEbmQxTlFLQ0FRQXlJM0xCOUQxUlg1NkhEYmI4WTZobk1wM2xTTmU3aWxpNy8zMjNCczB2QTZ6R0hod2o4ajlnCjVHcEJSd2ZXdCszUkRvZlQrL1h3K1MxTmdUTVJxWkw3M3V3VjdIWnhaTkE4NU5TUDk2bG5EbmpXN3NzTFhtY0wKRFRNQy83UVBaOU1xYmt4SXNDV0wwaml2bSs0a0UrSStqWXlHdkx0a2hlU1NHOU5pOGM1SzlwdjF1Z3ZVOUpwSAo0QlRSblhCd2JOUnVMZ3JpcHJwZjJKS2dYQmlPWEFTZUVmbzMvSTRETjZISWxwUFdoRDkwb0MyaWhvU3JrL2lVCmFMSnVCUmhncDJBelFYMWlBMDcxczNJR3ZlUERnN1RYd2Y4ZDM2bFhnVWNTTEJ0N2FmVzNoOGordEVCZ1FQTEwKVk9seXgzYzVNdVpaZDhHWXBKNGUwUDFJYWJzN2ZwZEEKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo= diff --git a/dataclients/kubernetes/update_test.go b/dataclients/kubernetes/update_test.go index a5d36e6436..e9429c8e9e 100644 --- a/dataclients/kubernetes/update_test.go +++ b/dataclients/kubernetes/update_test.go @@ -9,7 +9,7 @@ import ( ) func TestUpdateOnlyChangedRoutes(t *testing.T) { - api := newTestAPIWithEndpoints(t, &serviceList{}, &definitions.IngressList{}, &endpointList{}) + api := newTestAPIWithEndpoints(t, &serviceList{}, &definitions.IngressList{}, &endpointList{}, &secretList{}) defer api.Close() k, err := New(Options{ diff --git a/metricsinit_test.go b/metricsinit_test.go index 8d508a6784..13e7293599 100644 --- a/metricsinit_test.go +++ b/metricsinit_test.go @@ -36,7 +36,7 @@ func mustAvailablePort(t *testing.T) int { func TestInitOrderAndDefault(t *testing.T) { const ( ringMetricsUpdatePeriod = time.Millisecond - testTimeout = 120 * time.Millisecond + testTimeout = 5 * time.Second ) port := mustAvailablePort(t) diff --git a/secrets/certregistry/certregistry.go b/secrets/certregistry/certregistry.go new file mode 100644 index 0000000000..945735bafe --- /dev/null +++ b/secrets/certregistry/certregistry.go @@ -0,0 +1,71 @@ +package certregistry + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "sync" + + log "github.com/sirupsen/logrus" +) + +// CertRegistry object holds TLS certificates to be used to terminate TLS connections +// ensuring syncronized access to them. +type CertRegistry struct { + lookup map[string]*tls.Certificate + mx *sync.Mutex +} + +// NewCertRegistry initializes the certificate registry. +func NewCertRegistry() *CertRegistry { + l := make(map[string]*tls.Certificate) + + return &CertRegistry{ + lookup: l, + mx: &sync.Mutex{}, + } +} + +// Configures certificate for the host if no configuration exists or +// if certificate is valid (`NotBefore` field) after previously configured certificate. +func (r *CertRegistry) ConfigureCertificate(host string, cert *tls.Certificate) error { + if cert == nil { + return fmt.Errorf("cannot configure nil certificate") + } + // loading parsed leaf certificate to certificate + leaf, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return fmt.Errorf("failed parsing leaf certificate: %w", err) + } + cert.Leaf = leaf + + r.mx.Lock() + defer r.mx.Unlock() + + curr, found := r.lookup[host] + if found { + if cert.Leaf.NotBefore.After(curr.Leaf.NotBefore) { + log.Infof("updating certificate in registry - %s", host) + r.lookup[host] = cert + return nil + } else { + return nil + } + } else { + log.Infof("adding certificate to registry - %s", host) + r.lookup[host] = cert + return nil + } +} + +// GetCertFromHello reads the SNI from a TLS client and returns the appropriate certificate. +// If no certificate is found for the host it will return nil. +func (r *CertRegistry) GetCertFromHello(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { + r.mx.Lock() + cert, found := r.lookup[hello.ServerName] + r.mx.Unlock() + if found { + return cert, nil + } + return nil, nil +} diff --git a/secrets/certregistry/certregistry_test.go b/secrets/certregistry/certregistry_test.go new file mode 100644 index 0000000000..55f5d61b74 --- /dev/null +++ b/secrets/certregistry/certregistry_test.go @@ -0,0 +1,230 @@ +package certregistry + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "reflect" + "sync" + "testing" + "time" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +const ( + tenYears = time.Hour * 24 * 365 * 10 +) + +type caInfra struct { + sync.Once + err error + chainKey *rsa.PrivateKey + chainCert *x509.Certificate +} + +var ca = caInfra{} + +func createDummyCertDetail(t *testing.T, arn string, altNames []string, notBefore, notAfter time.Time) *tls.Certificate { + ca.Do(func() { + caKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + ca.err = fmt.Errorf("unable to generate CA key: %v", err) + return + } + + caCert := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Testing CA"}, + }, + NotBefore: time.Time{}, + NotAfter: time.Now().Add(tenYears), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + + IsCA: true, + } + caBody, err := x509.CreateCertificate(rand.Reader, &caCert, &caCert, caKey.Public(), caKey) + if err != nil { + ca.err = fmt.Errorf("unable to generate CA certificate: %v", err) + return + } + caReparsed, err := x509.ParseCertificate(caBody) + if err != nil { + ca.err = fmt.Errorf("unable to parse CA certificate: %v", err) + return + } + roots := x509.NewCertPool() + roots.AddCert(caReparsed) + + chainKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + ca.err = fmt.Errorf("unable to generate sub-CA key: %v", err) + return + } + chainCert := x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{ + Organization: []string{"Testing Sub-CA"}, + }, + NotBefore: time.Time{}, + NotAfter: time.Now().Add(tenYears), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + + IsCA: true, + } + chainBody, err := x509.CreateCertificate(rand.Reader, &chainCert, caReparsed, chainKey.Public(), caKey) + if err != nil { + ca.err = fmt.Errorf("unable to generate sub-CA certificate: %v", err) + return + } + chainReparsed, err := x509.ParseCertificate(chainBody) + if err != nil { + ca.err = fmt.Errorf("unable to parse sub-CA certificate: %v", err) + return + } + + ca.chainKey = chainKey + ca.chainCert = chainReparsed + }) + + certKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + require.NoErrorf(t, err, "unable to generate certificate key") + } + cert := x509.Certificate{ + SerialNumber: big.NewInt(3), + DNSNames: altNames, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + body, err := x509.CreateCertificate(rand.Reader, &cert, ca.chainCert, certKey.Public(), ca.chainKey) + if err != nil { + require.NoErrorf(t, err, "unable to generate certificate") + } + + crt := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: body}) + + key := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(certKey)}) + + certificate, err := tls.X509KeyPair([]byte(crt), []byte(key)) + if err != nil { + log.Errorf("failed to generate fake serial number: %v", err) + } + + return &certificate +} + +func TestCertRegistry(t *testing.T) { + + domain := "example.org" + validHostname := "foo." + domain + + now := time.Now().Truncate(time.Millisecond) + + old := now.Add(-time.Hour * 48 * 7) + new := now.Add(-time.Hour * 24 * 7) + after := now.Add(time.Hour*24*7 + 1*time.Second) + dummyArn := "DUMMY" + + // simple cert + validCert := createDummyCertDetail(t, dummyArn, []string{validHostname}, old, after) + newValidCert := createDummyCertDetail(t, dummyArn, []string{validHostname}, new, after) + + validCert.Leaf, _ = x509.ParseCertificate(validCert.Certificate[0]) + newValidCert.Leaf, _ = x509.ParseCertificate(newValidCert.Certificate[0]) + + hello := &tls.ClientHelloInfo{ + ServerName: "foo.example.org", + } + + t.Run("sync new certificate", func(t *testing.T) { + cr := NewCertRegistry() + cr.ConfigureCertificate(validHostname, validCert) + cert, found := cr.lookup[validHostname] + if !found { + t.Error("failed to read certificate") + } + if cert.Leaf == nil { + t.Error("synced cert should have a parsed leaf") + } + }) + + t.Run("sync a nil certificate", func(t *testing.T) { + cr := NewCertRegistry() + cr.ConfigureCertificate(validHostname, nil) + _, found := cr.lookup[validHostname] + if found { + t.Error("nil certificate should not sync") + } + }) + + t.Run("sync existing certificate", func(t *testing.T) { + + cr := NewCertRegistry() + cr.ConfigureCertificate(validHostname, validCert) + cert1 := cr.lookup[validHostname] + cr.ConfigureCertificate(validHostname, newValidCert) + cert2 := cr.lookup[validHostname] + if equalCert(cert1, cert2) { + t.Error("host cert was not updated") + } + + }) + + t.Run("get non existent cert", func(t *testing.T) { + cr := NewCertRegistry() + _, found := cr.lookup["foo"] + if found { + t.Error("non existent certificate was found") + } + }) + + t.Run("get cert from hello", func(t *testing.T) { + cr := NewCertRegistry() + cr.ConfigureCertificate(validHostname, validCert) + crt, _ := cr.GetCertFromHello(hello) + if crt == nil { + t.Error("failed to read certificate from hello") + } else { + if !reflect.DeepEqual(crt.Certificate, validCert.Certificate) { + t.Error("failed to read correct certificate from hello") + } + } + }) + + t.Run("get nil cert from unknown hello", func(t *testing.T) { + cr := NewCertRegistry() + cert, _ := cr.GetCertFromHello(hello) + if cert != nil { + t.Error("should return nil when cert not found") + } + }) +} + +func equalCert(l *tls.Certificate, r *tls.Certificate) bool { + if !reflect.DeepEqual(l.Certificate, r.Certificate) { + return false + } + + if !reflect.DeepEqual(l.PrivateKey, r.PrivateKey) { + return false + } + + return true +} diff --git a/skipper.go b/skipper.go index 03f403bf28..6428fe410a 100644 --- a/skipper.go +++ b/skipper.go @@ -58,6 +58,7 @@ import ( "github.com/zalando/skipper/routing" "github.com/zalando/skipper/scheduler" "github.com/zalando/skipper/secrets" + "github.com/zalando/skipper/secrets/certregistry" "github.com/zalando/skipper/swarm" "github.com/zalando/skipper/tracing" ) @@ -804,6 +805,9 @@ type Options struct { // ClusterRatelimitMaxGroupShards specifies the maximum number of group shards for the clusterRatelimit filter ClusterRatelimitMaxGroupShards int + // KubernetesEnableTLS enables kubernetes to use resources to terminate tls + KubernetesEnableTLS bool + testOptions } @@ -825,7 +829,7 @@ func newServerErrorLog() *stdlog.Logger { return stdlog.New(&serverErrorLogWriter{}, "", 0) } -func createDataClients(o Options, auth innkeeper.Authentication) ([]routing.DataClient, error) { +func createDataClients(o Options, auth innkeeper.Authentication, cr *certregistry.CertRegistry) ([]routing.DataClient, error) { var clients []routing.DataClient if o.RoutesFile != "" { @@ -929,6 +933,7 @@ func createDataClients(o Options, auth innkeeper.Authentication) ([]routing.Data ReverseSourcePredicate: o.ReverseSourcePredicate, RouteGroupClass: o.KubernetesRouteGroupClass, WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR, + CertificateRegistry: cr, }) if err != nil { return nil, err @@ -988,15 +993,24 @@ func initLog(o Options) error { return nil } -func (o *Options) tlsConfig() (*tls.Config, error) { +func (o *Options) tlsConfig(cr *certregistry.CertRegistry) (*tls.Config, error) { + if o.ProxyTLS != nil { return o.ProxyTLS, nil } - if o.CertPathTLS == "" && o.KeyPathTLS == "" { + if o.CertPathTLS == "" && o.KeyPathTLS == "" && !o.KubernetesEnableTLS { return nil, nil } + if o.CertPathTLS == "" && o.KeyPathTLS == "" { + config := &tls.Config{ + MinVersion: o.TLSMinVersion, + GetCertificate: cr.GetCertFromHello, + } + return config, nil + } + crts := strings.Split(o.CertPathTLS, ",") keys := strings.Split(o.KeyPathTLS, ",") @@ -1008,6 +1022,10 @@ func (o *Options) tlsConfig() (*tls.Config, error) { MinVersion: o.TLSMinVersion, } + if o.KubernetesEnableTLS { + config.GetCertificate = cr.GetCertFromHello + } + for i := 0; i < len(crts); i++ { crt, key := crts[i], keys[i] keypair, err := tls.LoadX509KeyPair(crt, key) @@ -1075,8 +1093,9 @@ func listenAndServeQuit( sigs chan os.Signal, idleConnsCH chan struct{}, mtr metrics.Metrics, + cr *certregistry.CertRegistry, ) error { - tlsConfig, err := o.tlsConfig() + tlsConfig, err := o.tlsConfig(cr) if err != nil { return err } @@ -1152,7 +1171,7 @@ func listenAndServeQuit( } func listenAndServe(proxy http.Handler, o *Options) error { - return listenAndServeQuit(proxy, o, nil, nil, nil) + return listenAndServeQuit(proxy, o, nil, nil, nil, nil) } func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { @@ -1237,8 +1256,13 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { return err } + var cr *certregistry.CertRegistry + if o.KubernetesEnableTLS { + cr = certregistry.NewCertRegistry() + } + // *DEPRECATED* innkeeper - create data clients - dataClients, err := createDataClients(o, inkeeperAuth) + dataClients, err := createDataClients(o, inkeeperAuth, cr) if err != nil { return err } @@ -1679,7 +1703,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { // wait for the first route configuration to be loaded if enabled: <-routing.FirstLoad() - return listenAndServeQuit(o.CustomHttpHandlerWrap(proxy), &o, sig, idleConnsCH, mtr) + return listenAndServeQuit(o.CustomHttpHandlerWrap(proxy), &o, sig, idleConnsCH, mtr, cr) } // Run skipper. diff --git a/skipper_test.go b/skipper_test.go index 326d2ccfeb..4f4c85072f 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -18,6 +18,7 @@ import ( "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/ratelimit" "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/secrets/certregistry" "github.com/stretchr/testify/require" ) @@ -74,6 +75,8 @@ func findAddress() (string, error) { } func TestOptionsTLSConfig(t *testing.T) { + cr := certregistry.NewCertRegistry() + cert, err := tls.LoadX509KeyPair("fixtures/test.crt", "fixtures/test.key") require.NoError(t, err) @@ -82,38 +85,46 @@ func TestOptionsTLSConfig(t *testing.T) { // empty o := &Options{} - c, err := o.tlsConfig() + c, err := o.tlsConfig(cr) require.NoError(t, err) require.Nil(t, c) + // enable kubernetes tls + o = &Options{KubernetesEnableTLS: true} + c, err = o.tlsConfig(cr) + require.NoError(t, err) + require.NotNil(t, c.GetCertificate) + // proxy tls config o = &Options{ProxyTLS: &tls.Config{}} - c, err = o.tlsConfig() + c, err = o.tlsConfig(cr) require.NoError(t, err) require.Equal(t, &tls.Config{}, c) // proxy tls config priority o = &Options{ProxyTLS: &tls.Config{}, CertPathTLS: "fixtures/test.crt", KeyPathTLS: "fixtures/test.key"} - c, err = o.tlsConfig() + c, err = o.tlsConfig(cr) require.NoError(t, err) require.Equal(t, &tls.Config{}, c) // cert key path o = &Options{TLSMinVersion: tls.VersionTLS12, CertPathTLS: "fixtures/test.crt", KeyPathTLS: "fixtures/test.key"} - c, err = o.tlsConfig() + c, err = o.tlsConfig(cr) require.NoError(t, err) require.Equal(t, uint16(tls.VersionTLS12), c.MinVersion) require.Equal(t, []tls.Certificate{cert}, c.Certificates) // multiple cert key paths o = &Options{TLSMinVersion: tls.VersionTLS13, CertPathTLS: "fixtures/test.crt,fixtures/test2.crt", KeyPathTLS: "fixtures/test.key,fixtures/test2.key"} - c, err = o.tlsConfig() + c, err = o.tlsConfig(cr) require.NoError(t, err) require.Equal(t, uint16(tls.VersionTLS13), c.MinVersion) require.Equal(t, []tls.Certificate{cert, cert2}, c.Certificates) } func TestOptionsTLSConfigInvalidPaths(t *testing.T) { + cr := certregistry.NewCertRegistry() + for _, tt := range []struct { name string options *Options @@ -127,7 +138,7 @@ func TestOptionsTLSConfigInvalidPaths(t *testing.T) { {"multiple cert key mismatch", &Options{CertPathTLS: "fixtures/test.crt,fixtures/test2.crt", KeyPathTLS: "fixtures/test2.key,fixtures/test.key"}}, } { t.Run(tt.name, func(t *testing.T) { - _, err := tt.options.tlsConfig() + _, err := tt.options.tlsConfig(cr) t.Logf("tlsConfig error: %v", err) require.Error(t, err) }) @@ -252,7 +263,7 @@ func testServerShutdown(t *testing.T, o *Options, scheme string) { sigs := make(chan os.Signal, 1) go func() { - err := listenAndServeQuit(proxy, o, sigs, nil, nil) + err := listenAndServeQuit(proxy, o, sigs, nil, nil, nil) require.NoError(t, err) }() From e587e51abe2211292bce2f9347fc4990b65f31b3 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 15 Apr 2022 03:25:03 +0200 Subject: [PATCH 0022/1047] proxy: disable default net/http user agent (#1995) Similar to https://github.com/golang/go/issues/15524 Fixes #1994 Signed-off-by: Alexander Yastrebov --- proxy/proxy.go | 4 ++++ proxy/proxy_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index e04f510d3f..f7cf6b8bfd 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -501,6 +501,10 @@ func mapRequest(ctx *context, requestContext stdlibcontext.Context, removeHopHea } else { rr.Header = cloneHeader(r.Header) } + // Disable default net/http user agent when user agent is not specified + if _, ok := rr.Header["User-Agent"]; !ok { + rr.Header["User-Agent"] = []string{""} + } rr.Host = host // If there is basic auth configured in the URL we add them as headers diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 0c123207f3..35ece602a4 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -2062,6 +2062,42 @@ func TestHopHeaderRemovalDisabled(t *testing.T) { } } +func TestUserAgent(t *testing.T) { + for _, tc := range []struct { + name string + userAgent string + }{ + {name: "no user agent"}, + {name: "with user agent", userAgent: "test ua"}, + } { + t.Run(tc.name, func(t *testing.T) { + s := startTestServer(nil, 0, func(r *http.Request) { + if got := r.Header.Get("User-Agent"); tc.userAgent != got { + t.Errorf("user agent mismatch: expected %q, got %q", tc.userAgent, got) + } + }) + defer s.Close() + + r := httptest.NewRequest("GET", "http://example.com/foo", nil) + if tc.userAgent != "" { + r.Header.Set("User-Agent", tc.userAgent) + } + + w := httptest.NewRecorder() + + doc := fmt.Sprintf(`* -> "%s"`, s.URL) + + tp, err := newTestProxy(doc, FlagsNone) + if err != nil { + t.Fatal(err) + } + defer tp.close() + + tp.proxy.ServeHTTP(w, r) + }) + } +} + func thisOneWillPanic() { panic("oops") } From 494ca96bc8f0fb1f6cf699a6fa22defd4d30b8dd Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 15 Apr 2022 03:28:47 +0200 Subject: [PATCH 0023/1047] Install `gover` via go install (#1996) Follow up on #1991 Signed-off-by: Alexander Yastrebov --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6a8bb79073..80d872b998 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,6 @@ clean: deps: go env ./etcd/install.sh $(TEST_ETCD_VERSION) - mkdir -p .bin @go install honnef.co/go/tools/cmd/staticcheck@latest @go install github.com/securego/gosec/v2/cmd/gosec@latest @@ -165,7 +164,7 @@ precommit: fmt build vet staticcheck check-race shortcheck '{{if len .TestGoFiles}}"GO111MODULE=on go test -tags=redis -coverprofile={{.Dir}}/.coverprofile {{.ImportPath}}"{{end}}' \ $$p | xargs -i sh -c {}; \ done - go get github.com/modocache/gover + go install github.com/modocache/gover@latest gover . .coverprofile-all cover: .coverprofile-all From ceea0867459a1077097101368472dfbd841347fd Mon Sep 17 00:00:00 2001 From: Samuel Lang Date: Tue, 19 Apr 2022 22:40:24 +0200 Subject: [PATCH 0024/1047] clear invalid cookies (#1965) * clear invalid cookies If OIDC cookies are invalid, the client is redirected but the cookies are not cleared. In case that the new cookie will produce smaller set of chunks, an invalid chunk is not overwritten and kept in the browser and Skipper will try to attach this junk to the new set. This changes the behaviour in that regard that it clears cookies with relevant names in case of a new auth session. Signed-off-by: Samuel Lang * refactor and fix createOidcCookie Signed-off-by: Icereed Co-authored-by: Icereed --- filters/auth/oidc.go | 79 ++++++++++++++++++++++++++------------- filters/auth/oidc_test.go | 22 ++++++++++- 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index e4c66092a2..10cbeca6d9 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -327,7 +327,7 @@ func (f *tokenOidcFilter) internalServerError(ctx filters.FilterContext) { // https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowSteps // 1. Client prepares an Authentication Request containing the desired request parameters. // 2. Client sends the request to the Authorization Server. -func (f *tokenOidcFilter) doOauthRedirect(ctx filters.FilterContext) { +func (f *tokenOidcFilter) doOauthRedirect(ctx filters.FilterContext, cookies []*http.Cookie) { nonce, err := f.encrypter.CreateNonce() if err != nil { log.Errorf("Failed to create nonce: %v.", err) @@ -368,6 +368,9 @@ func (f *tokenOidcFilter) doOauthRedirect(ctx filters.FilterContext) { StatusCode: http.StatusTemporaryRedirect, Status: "Moved Temporarily", } + for _, cookie := range cookies { + rsp.Header.Add("Set-Cookie", cookie.String()) + } log.Debugf("serve redirect: plaintextState:%s to Location: %s", statePlain, rsp.Header.Get("Location")) ctx.Serve(rsp) } @@ -401,30 +404,49 @@ func getHost(request *http.Request) string { } } -func chunkCookie(cookie http.Cookie) (cookies []http.Cookie) { +func (f *tokenOidcFilter) createOidcCookie(ctx filters.FilterContext, name string, value string, maxAge int) (cookie *http.Cookie) { + return &http.Cookie{ + Name: name, + Value: value, + Path: "/", + Secure: true, + HttpOnly: true, + MaxAge: maxAge, + Domain: extractDomainFromHost(getHost(ctx.Request()), f.subdomainsToRemove), + } +} + +func (f *tokenOidcFilter) deleteOidcCookie(ctx filters.FilterContext, name string) (cookie *http.Cookie) { + return f.createOidcCookie(ctx, name, "", -1) +} + +func chunkCookie(cookie *http.Cookie) (cookies []*http.Cookie) { + // We need to dereference the cookie to avoid modifying the original cookie. + cookieCopy := *cookie + for index := 'a'; index <= 'z'; index++ { - cookieSize := len(cookie.String()) + cookieSize := len(cookieCopy.String()) if cookieSize < cookieMaxSize { - cookie.Name += string(index) - return append(cookies, cookie) + cookieCopy.Name += string(index) + return append(cookies, &cookieCopy) } - newCookie := cookie + newCookie := cookieCopy newCookie.Name += string(index) // non-deterministic approach support signature changes - cut := len(cookie.Value) - (cookieSize - cookieMaxSize) - 1 - newCookie.Value, cookie.Value = cookie.Value[:cut], cookie.Value[cut:] - cookies = append(cookies, newCookie) + cut := len(cookieCopy.Value) - (cookieSize - cookieMaxSize) - 1 + newCookie.Value, cookieCopy.Value = cookieCopy.Value[:cut], cookieCopy.Value[cut:] + cookies = append(cookies, &newCookie) } log.Error("unsupported amount of chunked cookies") return } -func mergerCookies(cookies []http.Cookie) (cookie http.Cookie) { +func mergerCookies(cookies []*http.Cookie) *http.Cookie { if len(cookies) == 0 { - return + return nil } - cookie = cookies[0] + cookie := *(cookies[0]) cookie.Name = cookie.Name[:len(cookie.Name)-1] cookie.Value = "" // potentially shuffeled @@ -434,7 +456,7 @@ func mergerCookies(cookies []http.Cookie) (cookie http.Cookie) { for _, ck := range cookies { cookie.Value += ck.Value } - return + return &cookie } func (f *tokenOidcFilter) doDownstreamRedirect(ctx filters.FilterContext, oidcState []byte, maxAge time.Duration, redirectUrl string) { @@ -445,16 +467,14 @@ func (f *tokenOidcFilter) doDownstreamRedirect(ctx filters.FilterContext, oidcSt "Location": {redirectUrl}, }, } - - oidcCookies := chunkCookie(http.Cookie{ - Name: f.cookiename, - Value: base64.StdEncoding.EncodeToString(oidcState), - Path: "/", - Secure: true, - HttpOnly: true, - MaxAge: int(maxAge.Seconds()), - Domain: extractDomainFromHost(getHost(ctx.Request()), f.subdomainsToRemove), - }) + oidcCookies := chunkCookie( + f.createOidcCookie( + ctx, + f.cookiename, + base64.StdEncoding.EncodeToString(oidcState), + int(maxAge.Seconds()), + ), + ) for _, cookie := range oidcCookies { r.Header.Add("Set-Cookie", cookie.String()) } @@ -676,20 +696,20 @@ func (f *tokenOidcFilter) getMaxAge(claimsMap map[string]interface{}) time.Durat func (f *tokenOidcFilter) Request(ctx filters.FilterContext) { var ( allowed bool - cookies []http.Cookie + cookies []*http.Cookie container tokenContainer ) r := ctx.Request() for _, cookie := range r.Cookies() { if strings.HasPrefix(cookie.Name, f.cookiename) { - cookies = append(cookies, *cookie) + cookies = append(cookies, cookie) } } sessionCookie := mergerCookies(cookies) log.Debugf("Request: Cookie merged, %d chunks, len: %d", len(cookies), len(sessionCookie.String())) - cookie, ok := f.validateCookie(&sessionCookie) + cookie, ok := f.validateCookie(sessionCookie) log.Debugf("Request: Cookie Validation: %v", ok) if !ok { // 5. Authorization Server sends the End-User back to the Client with an Authorization Code. @@ -698,7 +718,12 @@ func (f *tokenOidcFilter) Request(ctx filters.FilterContext) { return } // 1. Client prepares an Authentication Request containing the desired request parameters. - f.doOauthRedirect(ctx) + // clear existing, invalid cookies + var purgeCookies = make([]*http.Cookie, len(cookies)) + for i, c := range cookies { + purgeCookies[i] = f.deleteOidcCookie(ctx, c.Name) + } + f.doOauthRedirect(ctx, purgeCookies) return } diff --git a/filters/auth/oidc_test.go b/filters/auth/oidc_test.go index 09247a2404..79b982dd40 100644 --- a/filters/auth/oidc_test.go +++ b/filters/auth/oidc_test.go @@ -644,6 +644,7 @@ func TestOIDCSetup(t *testing.T) { expectRequest string expectNoCookies bool expectCookieDomain string + filterCookies []string }{{ msg: "wrong provider", filter: `oauthOidcAnyClaims("no url", "", "", "{{ .RedirectURL }}", "", "")`, @@ -740,6 +741,13 @@ func TestOIDCSetup(t *testing.T) { filter: `oauthOidcAnyClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "uid email", "", "", "", "3")`, expected: 200, expectCookieDomain: "bar.foo.skipper.test", + }, { + msg: "remove unverified cookies", + hostname: "bar.foo.skipper.test", + filter: `oauthOidcAnyClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "uid email", "", "", "", "3")`, + expected: 200, + expectCookieDomain: "bar.foo.skipper.test", + filterCookies: []string{"badheader", "malformed"}, }} { t.Run(tc.msg, func(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -816,6 +824,10 @@ func TestOIDCSetup(t *testing.T) { } req.Header.Set(authHeaderName, authHeaderPrefix+testToken) + for _, v := range tc.filterCookies { + req.Header.Set("Set-Cookie", v) + } + // client with cookie handling to support 127.0.0.1 with ports client := http.Client{ Timeout: 1 * time.Second, @@ -853,6 +865,12 @@ func TestOIDCSetup(t *testing.T) { left, right := tokenExp-time.Minute, tokenExp maxAge := time.Duration(c.MaxAge) * time.Second assert.True(t, left <= maxAge && maxAge <= right, "maxAge has to be within [%v, %v]", left, right) + + for _, v := range tc.filterCookies { + if v == c.Name { + assert.True(t, c.Value == "") + } + } } } }) @@ -914,7 +932,7 @@ func TestChunkAndMergerCookie(t *testing.T) { } { t.Run(fmt.Sprintf("test:%v", ht.name), func(t *testing.T) { assert := assert.New(t) - got := chunkCookie(ht.given) + got := chunkCookie(&ht.given) assert.NotNil(t, got, "it should not be empty") // shuffle the order of response cookies rand.Shuffle(len(got), func(i, j int) { @@ -923,7 +941,7 @@ func TestChunkAndMergerCookie(t *testing.T) { assert.Len(got, ht.num, "should result in a different number of chunks") mergedCookie := mergerCookies(got) assert.NotNil(mergedCookie, "should receive a valid cookie") - assert.Equal(ht.given, mergedCookie, "after chunking and remerging the content must be equal") + assert.Equal(ht.given, *mergedCookie, "after chunking and remerging the content must be equal") // verify no cookie exceeds limits for _, ck := range got { assert.True(func() bool { From 8e74af2bee1ae5c61b091732fd8a02187aed4f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 20 Apr 2022 14:11:52 +0200 Subject: [PATCH 0025/1047] fix ipv6 concat port to ip address string (#1998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix ipv6 handling in endpoints conversion Signed-off-by: Sandor Szücs * use net.JoinHostPort to fix ipv4/ipv6 Signed-off-by: Sandor Szücs --- dataclients/kubernetes/ingressdefinitions.go | 3 +- .../routegroup-with-ipv6-backend.eskip | 3 ++ .../convert/routegroup-with-ipv6-backend.yaml | 38 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.yaml diff --git a/dataclients/kubernetes/ingressdefinitions.go b/dataclients/kubernetes/ingressdefinitions.go index 7e98835d18..431c57ba94 100644 --- a/dataclients/kubernetes/ingressdefinitions.go +++ b/dataclients/kubernetes/ingressdefinitions.go @@ -2,6 +2,7 @@ package kubernetes import ( "fmt" + "net" "strconv" "github.com/zalando/skipper/dataclients/kubernetes/definitions" @@ -83,7 +84,7 @@ type endpointList struct { } func formatEndpoint(a *address, p *port, protocol string) string { - return fmt.Sprintf("%s://%s:%d", protocol, a.IP, p.Port) + return protocol + "://" + net.JoinHostPort(a.IP, strconv.Itoa(p.Port)) } func formatEndpointsForSubsetAddresses(addresses []*address, port *port, protocol string) []string { diff --git a/dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.eskip b/dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.eskip new file mode 100644 index 0000000000..85d4bdb07b --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.eskip @@ -0,0 +1,3 @@ +kube_rg__default__myapp__all__0_0: + Host("^(example[.]org[.]?(:[0-9]+)?)$") + -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.yaml b/dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.yaml new file mode 100644 index 0000000000..7afb4fd1c4 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/convert/routegroup-with-ipv6-backend.yaml @@ -0,0 +1,38 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp +spec: + hosts: + - example.org + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + defaultBackends: + - backendName: myapp +--- +apiVersion: v1 +kind: Service +metadata: + name: myapp +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: myapp +subsets: +- addresses: + - ip: 10::8 + - ip: 10:ab::8 + ports: + - port: 80 From ed9fdf742afd4811ddf1321851b900b51af80b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 22 Apr 2022 18:29:43 +0200 Subject: [PATCH 0026/1047] deprecate ingress v1beta1 and change all the docs, examples and deployment files (#2000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- dataclients/kubernetes/kube.go | 5 +- docs/kubernetes/deploy/daemonset/rbac.yaml | 13 +- docs/kubernetes/deploy/deployment/rbac.yaml | 13 +- docs/kubernetes/east-west-usage.md | 25 +- docs/kubernetes/ingress-controller.md | 105 +++++- docs/kubernetes/ingress-usage.md | 348 +++++++++++++------- docs/kubernetes/routegroups.md | 78 +++-- docs/tutorials/operations.md | 20 +- 8 files changed, 432 insertions(+), 175 deletions(-) diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 17c7534f42..492197c195 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -88,7 +88,7 @@ type Options struct { // in the cluster-scope. KubernetesNamespace string - // KubernetesIngressV1 is used to switch between v1beta1 and v1. Kubernetes version 1.22 stopped support + // *DEPRECATED* KubernetesIngressV1 is used to switch between v1beta1 and v1. Kubernetes version 1.22 stopped support // for v1beta1, so we have to provide a migration path and this will someday become the default. KubernetesIngressV1 bool @@ -201,6 +201,9 @@ type Client struct { // New creates and initializes a Kubernetes DataClient. func New(o Options) (*Client, error) { + if !o.KubernetesIngressV1 { + log.Warning("You are using a deprecated version of ingress. Skipper will remove support in the next couple of weeks. Kubernetes version 1.18 deprecated v1beta1 and 1.22 stopped support v1beta1. We will increase skipper's minor version to highlight the deletion. For more details please check https://opensource.zalando.com/skipper/kubernetes/ingress-controller/#upgrades") + } if o.OriginMarker { log.Warning("OriginMarker is deprecated") } diff --git a/docs/kubernetes/deploy/daemonset/rbac.yaml b/docs/kubernetes/deploy/daemonset/rbac.yaml index 361f378983..c0e448a4ff 100644 --- a/docs/kubernetes/deploy/daemonset/rbac.yaml +++ b/docs/kubernetes/deploy/daemonset/rbac.yaml @@ -37,11 +37,18 @@ metadata: name: skipper-ingress namespace: kube-system --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: skipper-ingress rules: +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list - apiGroups: - extensions resources: @@ -66,7 +73,7 @@ rules: - get - list --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: skipper-ingress @@ -79,7 +86,7 @@ subjects: name: skipper-ingress namespace: kube-system --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: skipper-ingress-hostnetwork-psp diff --git a/docs/kubernetes/deploy/deployment/rbac.yaml b/docs/kubernetes/deploy/deployment/rbac.yaml index 361f378983..c0e448a4ff 100644 --- a/docs/kubernetes/deploy/deployment/rbac.yaml +++ b/docs/kubernetes/deploy/deployment/rbac.yaml @@ -37,11 +37,18 @@ metadata: name: skipper-ingress namespace: kube-system --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: skipper-ingress rules: +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list - apiGroups: - extensions resources: @@ -66,7 +73,7 @@ rules: - get - list --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: skipper-ingress @@ -79,7 +86,7 @@ subjects: name: skipper-ingress namespace: kube-system --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: skipper-ingress-hostnetwork-psp diff --git a/docs/kubernetes/east-west-usage.md b/docs/kubernetes/east-west-usage.md index 1fd009cc10..fc9f21ce9d 100644 --- a/docs/kubernetes/east-west-usage.md +++ b/docs/kubernetes/east-west-usage.md @@ -10,7 +10,7 @@ ingress configuration. Example: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo @@ -21,8 +21,11 @@ spec: http: paths: - backend: - serviceName: example - servicePort: 80 + service: + name: example + port: + number: 80 + pathType: ImplementationSpecific ``` Or as a [RouteGroup](./routegroups.md): @@ -57,7 +60,7 @@ You can also use the same ingress or RouteGroup object to accept internal and external traffic: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo @@ -68,14 +71,20 @@ spec: http: paths: - backend: - serviceName: example - servicePort: 80 + service: + name: example + port: + number: 80 + pathType: ImplementationSpecific - host: demo.skipper.cluster.local http: paths: - backend: - serviceName: example - servicePort: 80 + service: + name: example + port: + number: 80 + pathType: ImplementationSpecific ``` Or, again, as a [RouteGroup](./routegroups.md): diff --git a/docs/kubernetes/ingress-controller.md b/docs/kubernetes/ingress-controller.md index ccba372b77..fd7bb7c633 100644 --- a/docs/kubernetes/ingress-controller.md +++ b/docs/kubernetes/ingress-controller.md @@ -345,7 +345,7 @@ the application with Ingress to the external network: ```bash # cat demo-ing.yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: skipper-demo @@ -355,8 +355,11 @@ spec: http: paths: - backend: - serviceName: skipper-demo - servicePort: 80 + service: + name: skipper-demo + port: + number: 80 + pathType: ImplementationSpecific ``` To deploy this ingress, you have to run: @@ -404,7 +407,7 @@ serve the traffic. Example ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -416,8 +419,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ## Scoping Skipper Deployments to a Single Namespace @@ -542,7 +548,7 @@ If the setup is correct, skipper will protect the following ingress example with the `ClientIP` predicate: ``` -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo @@ -553,8 +559,11 @@ spec: http: paths: - backend: - serviceName: example - servicePort: 80 + service: + name: example + port: + number: 80 + pathType: ImplementationSpecific ``` Your clients inside the cluster should call this example with @@ -696,3 +705,81 @@ the communication work with TCP and UDP to the specified `swarm-port`: name: swarm-port protocol: TCP ``` + +## Upgrades + +### =v0.14.0 + +Kubernetes dataclient removes support for ingress v1beta1. +What does it mean for you? + +1. If you run with enabled `-kubernetes-ingress-v1`, you won't need to + do anything and you can safely delete the flag while updating to + `>=0.14.0`. +2. If you use skipper as library and pass `KubernetesIngressV1: true` + via `kubernetes.Options` into `kubernetes.New()`, then you won't need to + do anything and you can safely delete passing the option while updating to + `>=0.14.0`. +3. If you use Ingress v1beta1 and run Kubernetes cluster version that + does not support ingress v1, then you can't update skipper to + `>=0.14.0`, before you upgrade your Kubernetes cluster. +4. If you use Ingress v1beta1 and run Kubernetes cluster version that + support ingress v1, then you need to allow skipper to access the new + APIs with a changed RBAC. See the guide below. + + +If you are in case 4., you have to apply a change in your RBAC, please +check the diff or the full rendered file. + +Diff view (same for deployment and daemonset): +```diff +diff --git docs/kubernetes/deploy/deployment/rbac.yaml docs/kubernetes/deploy/deployment/rbac.yaml +index 361f3789..c0e448a4 100644 +--- docs/kubernetes/deploy/deployment/rbac.yaml ++++ docs/kubernetes/deploy/deployment/rbac.yaml +@@ -37,11 +37,18 @@ metadata: + name: skipper-ingress + namespace: kube-system + --- +-apiVersion: rbac.authorization.k8s.io/v1beta1 ++apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: skipper-ingress + rules: ++- apiGroups: ++ - networking.k8s.io ++ resources: ++ - ingresses ++ verbs: ++ - get ++ - list + - apiGroups: + - extensions + resources: +@@ -66,7 +73,7 @@ rules: + - get + - list + --- +-apiVersion: rbac.authorization.k8s.io/v1beta1 ++apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: skipper-ingress +@@ -79,7 +86,7 @@ subjects: + name: skipper-ingress + namespace: kube-system + --- +-apiVersion: rbac.authorization.k8s.io/v1beta1 ++apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: skipper-ingress-hostnetwork-psp +``` + +Full rendered RBAC files (same for deployment and daemonset): + +```yaml +# cat docs/kubernetes/deploy/deployment/rbac.yaml +{!kubernetes/deploy/deployment/rbac.yaml!} +``` diff --git a/docs/kubernetes/ingress-usage.md b/docs/kubernetes/ingress-usage.md index e7249a52a9..12e75c5c50 100644 --- a/docs/kubernetes/ingress-usage.md +++ b/docs/kubernetes/ingress-usage.md @@ -24,7 +24,7 @@ zalando.org/skipper-ingress-redirect | `"true"` | change the default HTTPS redir zalando.org/skipper-ingress-redirect-code | `301` | change the default HTTPS redirect code for specific ingresses zalando.org/skipper-loadbalancer | `consistentHash` | defaults to `roundRobin`, [see available choices](../reference/backends.md#load-balancer-backend) zalando.org/skipper-backend-protocol | `fastcgi` | (*experimental*) defaults to `http`, [see available choices](../reference/backends.md#backend-protocols) -zalando.org/skipper-ingress-path-mode | `path-prefix` | defaults to `kubernetes-ingress`, [see available choices](#ingress-path-handling), to change the default use `-kubernetes-path-mode` +zalando.org/skipper-ingress-path-mode | `path-prefix` | (*deprecated*) please use [Ingress version 1 pathType option](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types), which defaults to ImplementationSpecific and does not change the behavior. Skipper's path-mode defaults to `kubernetes-ingress`, [see available choices](#ingress-path-handling), to change the default use `-kubernetes-path-mode`. ## Supported Service types @@ -46,7 +46,7 @@ route will match by http `Host: app-default.example.org` and route to endpoints selected by the Kubernetes service `app-svc` on port `80`. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app @@ -56,8 +56,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` To have 2 routes with different `Host` headers serving the same @@ -70,7 +73,7 @@ GCP migration, GCP to bare metal migration or bare metal to Alibaba Cloud migration. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app @@ -80,14 +83,20 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific - host: foo.example.org http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ### Multiple Ingresses defining the same route @@ -107,48 +116,62 @@ endpoints. (Most likely service-x was renamed to service-x-live and the old ingress was forgot). ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: service-x spec: rules: - host: service-x.example.org - http: - paths: - - backend: - serviceName: service-x # this service has 0 endpoints - servicePort: 80 + http: + paths: + - backend: + service: + name: service-x # this service has 0 endpoints + port: + number: 80 + pathType: ImplementationSpecific ``` ​ ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: service-x-live spec: rules: - host: service-x.example.org - http: - paths: - - backend: - serviceName: service-x-live - servicePort: 80 + http: + paths: + - backend: + service: + name: service-x-live + port: + number: 80 + pathType: ImplementationSpecific ``` ## Ingress path handling -Ingress paths can be interpreted in four different modes: +Skipper supports all [Kubernetes +path-types](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) +as documented in Kubernetes documentation. + +Ingress paths can be interpreted in five different modes: -1. based on the kubernetes ingress specification -2. as plain regular expression -3. as a path prefix +1. `pathType: Prefix` results in [PathSubtree predicate](../reference/predicates.md#pathsubtree)) +2. `pathType: Exact` results in [Path predicate](../reference/predicates.md#path)) +3. `pathType: ImplementationSpecific` + 1. based on the kubernetes ingress specification + 2. as plain regular expression + 3. as a path prefix (same as `pathType: Prefix` and results in [PathSubtree](../reference/predicates.md#pathsubtree)) -The default is the kubernetes ingress mode. It can be changed by a startup option +The default is 3.1 the kubernetes ingress mode. It can be changed by a startup option to any of the other modes, and the individual ingress rules can also override the -default behavior with the zalando.org/skipper-ingress-path-mode annotation. +default behavior with the zalando.org/skipper-ingress-path-mode annotation. You can +also set for each path rule a different Kubernetes `pathType` like `Prefix` and `Exact`. E.g.: @@ -189,7 +212,7 @@ path. This example shows how to add predicates and filters: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -202,12 +225,18 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ## Custom Routes +Please consider using [RouteGroups](routegroups.md), instead of custom +routes! + Custom routes is a way of extending the default routes configured for an ingress resource. @@ -257,7 +286,7 @@ instead of the 2 Predicates created for the ingress backend. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app @@ -271,9 +300,12 @@ spec: http: paths: - path: / + pathType: Prefix backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 ``` #### Redirect a specific path from ingress @@ -289,7 +321,7 @@ redirected and passed to the backend selected by `serviceName=app-svc` and `servicePort=80`: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app @@ -303,9 +335,12 @@ spec: http: paths: - path: / + pathType: Prefix backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 ``` ### Return static content @@ -328,7 +363,7 @@ route definition from the ingress object for safety reasons. This example shows how to add a custom route for handling `OPTIONS` requests. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -345,8 +380,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` This will generate a custom route for the ingress which looks like this: @@ -378,7 +416,7 @@ predicates in ingress, if there are no paths rules defined. For example this will **not** work: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: skipper-ingress @@ -386,22 +424,28 @@ metadata: kubernetes.io/ingress.class: skipper zalando.org/skipper-routes: | redirect1: Path("/foo/") -> redirectTo(308, "/bar/") -> ; - spec: - rules: - - host: foo.bar - http: - paths: - - path: /something/ - backend: - serviceName: something - servicePort: 80 - - path: /else/ - backend: - serviceName: else - servicePort: 80 +spec: + rules: + - host: foo.bar + http: + paths: + - path: /something + pathType: Prefix + backend: + service: + name: something + port: + number: 80 + - path: /else + pathType: Prefix + backend: + service: + name: else + port: + number: 80 ``` -A possible solution will be a skipper route CRD: https://github.com/zalando/skipper/issues/660 +A possible solution is to use skipper's [RouteGroups](routegroups.md). ## Filters - Basic HTTP manipulations @@ -552,7 +596,7 @@ documented. The ingress spec would look like this: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -564,8 +608,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` #### Rate Breaker @@ -582,7 +629,7 @@ documented. The ingress spec would look like this: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -594,8 +641,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` @@ -626,7 +676,7 @@ X-Forwarded-For header or IP incase there is no X-Forwarded-For header set, are allowed to each skipper instance for the given ingress. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -638,8 +688,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` If you need to rate limit service to service communication and @@ -648,7 +701,7 @@ clients, then you can pass a 3 parameter to group clients by "Authorization Header": ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -660,8 +713,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` @@ -671,7 +727,7 @@ The example shows 50 calls per minute are allowed to each skipper instance for the given ingress. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -683,8 +739,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` #### Cluster Ratelimits @@ -698,7 +757,7 @@ The example shows 50 calls per minute are allowed to pass this ingress rule to the backend. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -710,8 +769,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ##### Client @@ -720,7 +782,7 @@ The example shows 10 calls per hour are allowed per client, X-Forwarded-For header, to pass this ingress rule to the backend. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -732,8 +794,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` #### Path ratelimit @@ -741,7 +806,7 @@ spec: To ratelimit a specific path use a second ingress definition like ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-default @@ -751,10 +816,13 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific --- -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-login @@ -767,8 +835,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` or use [RouteGroups](routegroups.md). @@ -780,7 +851,7 @@ and ignore the responses from your new backend. This can be done by the [tee()](../reference/filters.md#tee) and [teenf()](../reference/filters.md#teenf) filters. ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -792,8 +863,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ## Predicates @@ -823,7 +897,7 @@ query string in the URL has `version=alpha` set, for example alpha-svc: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -835,14 +909,17 @@ spec: http: paths: - backend: - serviceName: alpha-svc - servicePort: 80 + service: + name: alpha-svc + port: + number: 80 + pathType: ImplementationSpecific ``` prod-svc: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: prod-app @@ -852,8 +929,11 @@ spec: http: paths: - backend: - serviceName: prod-svc - servicePort: 80 + service: + name: prod-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ### IP Whitelisting @@ -861,7 +941,7 @@ spec: This ingress route will only allow traffic from networks 1.2.3.0/24 and 195.168.0.0/17 ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -873,8 +953,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` @@ -894,7 +977,7 @@ shows to have 10% traffic using A and the rest using B. 10% choice of setting the Cookie "flavor" to "A": ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -907,14 +990,17 @@ spec: http: paths: - backend: - serviceName: a-app-svc - servicePort: 80 + service: + name: a-app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` Rest is setting Cookie "flavor" to "B": ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -926,15 +1012,18 @@ spec: http: paths: - backend: - serviceName: b-app-svc - servicePort: 80 + service: + name: b-app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` To be sticky, you have to create 2 ingress with predicate to match routes with the cookie we set before. For "A" this would be: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -946,14 +1035,17 @@ spec: http: paths: - backend: - serviceName: a-app-svc - servicePort: 80 + service: + name: a-app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` For "B" this would be: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -965,8 +1057,11 @@ spec: http: paths: - backend: - serviceName: b-app-svc - servicePort: 80 + service: + name: b-app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ## Blue-Green deployments @@ -984,7 +1079,7 @@ In the following example **my-app-1** service will get **80%** of the traffic and **my-app-2** will get **20%** of the traffic: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app @@ -999,12 +1094,18 @@ spec: http: paths: - backend: - serviceName: my-app-1 - servicePort: http + service: + name: my-app + port: + name: http + pathType: Prefix path: / - backend: - serviceName: my-app-2 - servicePort: http + service: + name: my-app-2 + port: + name: http + pathType: Prefix path: / ``` @@ -1015,7 +1116,7 @@ For more advanced blue-green deployments, check out our [stackset-controller](ht You can set multiple filters in a chain similar to the [eskip format](https://godoc.org/github.com/zalando/skipper/eskip). ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -1028,8 +1129,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ## Controlling HTTPS redirect @@ -1050,7 +1154,7 @@ Annotations: Example: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -1063,8 +1167,11 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` ## Load Balancer Algorithm @@ -1086,7 +1193,7 @@ Annotations: Example: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: @@ -1098,6 +1205,9 @@ spec: http: paths: - backend: - serviceName: app-svc - servicePort: 80 + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific ``` diff --git a/docs/kubernetes/routegroups.md b/docs/kubernetes/routegroups.md index b309ae579d..b320817a75 100644 --- a/docs/kubernetes/routegroups.md +++ b/docs/kubernetes/routegroups.md @@ -127,14 +127,16 @@ spec: This is equivalent to the ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: - backend: - serviceName: my-service - servicePort: 80 + defaultBackend: + service: + name: my-service + port: + number: 80 ``` Notice that the route group contains a list of actual backends, and the defined service backend is then @@ -322,14 +324,16 @@ fields. Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: - backend: - serviceName: my-service - servicePort: 80 + defaultBackend: + service: + name: my-service + port: + number: 80 ``` RouteGroup: @@ -354,7 +358,7 @@ spec: Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress @@ -364,9 +368,12 @@ spec: http: paths: - path: /api + pathType: Prefix backend: - serviceName: my-service - servicePort: 80 + service: + name: my-service + port: + number: 80 ``` RouteGroup: @@ -393,7 +400,7 @@ spec: Ingress (we need to define two rules): ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress @@ -403,16 +410,22 @@ spec: http: paths: - path: /api + pathType: Prefix backend: - serviceName: my-service - servicePort: 80 + service: + name: my-service + port: + number: 80 - host: legacy-name.example.org http: paths: - path: /api + pathType: Prefix backend: - serviceName: my-service - servicePort: 80 + service: + name: my-service + port: + number: 80 ``` RouteGroup (we just define an additional host): @@ -441,7 +454,7 @@ For those cases when using multiple hostnames in the same ingress with different small workaround for the equivalent route group spec. Ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress @@ -451,16 +464,22 @@ spec: http: paths: - path: /api + pathType: Prefix backend: - serviceName: my-service - servicePort: 80 + service: + name: my-service + port: + number: 80 - host: legacy-name.example.org http: paths: - path: /application + pathType: Prefix backend: - serviceName: my-service - servicePort: 80 + service: + name: my-service + port: + number: 80 ``` RouteGroup (we need to use additional host predicates): @@ -514,7 +533,7 @@ for different routes. and backends without limitations. E.g where an ingress annotation's metadata may look like this: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress @@ -522,8 +541,10 @@ metadata: Method("OPTIONS") -> status(200) -> spec: backend: - serviceName: my-service - servicePort: 80 + service: + name: my-service + port: + number: 80 ``` the equivalent RouteGroup would look like this: @@ -616,7 +637,12 @@ The route objects support the different path lookup modes, by using the path, pa pathRegexp field. See also the [route matching](../reference/architecture.md#route-matching) explained for the internals. The mapping is as follows: -Ingress: | RouteGroup: +Ingress pathType: | RouteGroup: +--- | --- +`Exact` and `/foo` | path: `/foo` +`Prefix` and `/foo` | pathSubtree: `/foo` + +Ingress (`pathType: ImplementationSpecific`): | RouteGroup: --- | --- `kubernetes-ingress` and `/foo` | pathRegexp: `^/foo` `path-regexp` and `/foo` | pathRegexp: `/foo` @@ -631,7 +657,7 @@ the flag `--kubernetes-routegroup-class=` to only select RouteGroup objects that have the annotation `zalando.org/routegroup.class` set to ``. Skipper will only create routes for RouteGroup objects with it's annotation or RouteGroup objects that do not have this annotation. The -default class is `skipper`, if not set. +default class is `skipper`, if not set. Example RouteGroup: diff --git a/docs/tutorials/operations.md b/docs/tutorials/operations.md index d4c953e36f..808e866504 100644 --- a/docs/tutorials/operations.md +++ b/docs/tutorials/operations.md @@ -48,14 +48,19 @@ ratelimits, a wide range of Prometheus metrics, websockets and a better HTTP path routing than the default Kubernetes Ingress spec supports. -The Kubernetes Ingress spec defines a -[path](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#httpingresspath-v1beta1-networking-k8s-io) +The former Kubernetes Ingress v1beta1 spec defined a path as regular expression, which is not what most people would expect, nor want. Skipper defaults in Kubernetes to use the [PathRegexp predicate](../reference/predicates.md#pathregexp) for routing, because of the spec. We believe the better default is the path prefix mode, that uses [PathSubtree predicate](../reference/predicates.md#pathsubtree), instead. Path prefix search is much more scalable and can not lead to unexpected results by not so experienced regular expressions users. +Since Kubernetes v1.18, [Ingress v1 path definition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#httpingresspath-v1-networking-k8s-io) +supports all path matching modes that are common in skipper: + +- `pathType: Exact` maps to [`Path()`](../reference/predicates.md#path) +- `pathType: Prefix` maps to [`PathSubtree()`](../reference/predicates.md#pathsubtree) +- `pathType: ImplementationSpecific` is defined as you set path prefix mode. To find more information about Metrics, including formats and example Prometheus queries you find in the [metrics @@ -143,7 +148,7 @@ skipper \ and the following ingress is defined: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: qux @@ -154,9 +159,12 @@ spec: http: paths: - path: "/" + pathType: Prefix backend: - serviceName: qux - servicePort: baz + service: + name: qux + port: + name: baz ``` Skipper will secure this route adding the predicate `ClientIP("10.2.0.0/16")`. @@ -165,7 +173,7 @@ The same ingress might be used for internal and external hostnames. For example, given a slightly modified version of the ingress: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: ... From 52bd67c484b1e7e5911004319131a020af0d421e Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 26 Apr 2022 20:45:27 +0200 Subject: [PATCH 0027/1047] docs: fixes weight predicate link anchor (#2001) Follows up on #1338 Signed-off-by: Alexander Yastrebov --- docs/reference/architecture.md | 2 +- docs/tutorials/basics.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/architecture.md b/docs/reference/architecture.md index 9712a3a34e..bba28a913e 100644 --- a/docs/reference/architecture.md +++ b/docs/reference/architecture.md @@ -106,7 +106,7 @@ The route matching logic can be summed up as follows: 2. _If_ step #1 matches multiple routes, which means there are multiple routes in the same position of the path tree, and all other predicates match the request, too, then the route with the highest - [weight](predicates.md#weight) is matched. + [weight](predicates.md#weight-priority) is matched. * this is an O(n) lookup, but only on the same leaf diff --git a/docs/tutorials/basics.md b/docs/tutorials/basics.md index f79e110faf..eb838b5bd4 100644 --- a/docs/tutorials/basics.md +++ b/docs/tutorials/basics.md @@ -222,7 +222,7 @@ The route matching logic can be summed up as follows: 2. _If_ step #1 matches multiple routes, which means there are multiple routes in the same position of the path tree, and all other predicates match the request, too, then the route with the highest - [weight](../reference/predicates.md#weight) is matched. + [weight](../reference/predicates.md#weight-priority) is matched. * this is an O(n) lookup, but only on the same leaf From d4574734a50d3f4dc304495e24d2129c4ca478a7 Mon Sep 17 00:00:00 2001 From: Samuel Lang Date: Wed, 4 May 2022 17:55:05 +0200 Subject: [PATCH 0028/1047] resolve Aggregated and Distributed Claims (#1962) fixes: #1955 This solution is scoped to Azure behaviour, taking into account the specs from https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims There are some Azure related API calls included but trying to support other providers, which is though unknown at this time. it transforms a distributed claim ```json { "_claim_names": { "groups": "src1" }, "_claim_sources": { "src1": { "endpoint": "https://graph.windows.net/.../getMemberObjects" } } } ``` into a full populated token, which is saved in `statebag` and in the `cookie` for follow up processing ```json { "_claim_names": { "groups": "src1" }, "_claim_sources": { "src1": { "endpoint": "https://graph.windows.net/.../getMemberObjects" } }, "groups": [ "group1", "group2", ... ] } ``` Signed-off-by: Samuel Lang --- config/config.go | 3 + config/config_test.go | 1 + filters/auth/oidc.go | 213 +++++++++++++++++++++++- filters/auth/oidc_introspection_test.go | 3 +- filters/auth/oidc_test.go | 113 ++++++++++--- skipper.go | 15 +- 6 files changed, 318 insertions(+), 30 deletions(-) diff --git a/config/config.go b/config/config.go index 29aac1a800..40a7103aa9 100644 --- a/config/config.go +++ b/config/config.go @@ -192,6 +192,7 @@ type Config struct { Oauth2TokenCookieName string `yaml:"oauth2-token-cookie-name"` WebhookTimeout time.Duration `yaml:"webhook-timeout"` OidcSecretsFile string `yaml:"oidc-secrets-file"` + OidcDistributedClaimsTimeout time.Duration `yaml:"oidc-distributed-claims-timeout"` CredentialPaths *listFlag `yaml:"credentials-paths"` CredentialsUpdateInterval time.Duration `yaml:"credentials-update-interval"` @@ -447,6 +448,7 @@ func NewConfig() *Config { flag.StringVar(&cfg.Oauth2TokenCookieName, "oauth2-token-cookie-name", "oauth2-grant", "sets the name of the cookie where the encrypted token is stored") flag.DurationVar(&cfg.WebhookTimeout, "webhook-timeout", 2*time.Second, "sets the webhook request timeout duration") flag.StringVar(&cfg.OidcSecretsFile, "oidc-secrets-file", "", "file storing the encryption key of the OID Connect token") + flag.DurationVar(&cfg.OidcDistributedClaimsTimeout, "oidc-distributed-claims-timeout", 2*time.Second, "sets the default OIDC distributed claims request timeout duration to 2000ms") flag.Var(cfg.CredentialPaths, "credentials-paths", "directories or files to watch for credentials to use by bearerinjector filter") flag.DurationVar(&cfg.CredentialsUpdateInterval, "credentials-update-interval", 10*time.Minute, "sets the interval to update secrets") @@ -800,6 +802,7 @@ func (c *Config) ToOptions() skipper.Options { OAuth2TokenCookieName: c.Oauth2TokenCookieName, WebhookTimeout: c.WebhookTimeout, OIDCSecretsFile: c.OidcSecretsFile, + OIDCDistributedClaimsTimeout: c.OidcDistributedClaimsTimeout, CredentialsPaths: c.CredentialPaths.values, CredentialsUpdateInterval: c.CredentialsUpdateInterval, diff --git a/config/config_test.go b/config/config_test.go index 57a2bc297a..c3dd217f5c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -126,6 +126,7 @@ func Test_NewConfig(t *testing.T) { Oauth2TokeninfoSubjectKey: "uid", Oauth2TokenCookieName: "oauth2-grant", WebhookTimeout: 2 * time.Second, + OidcDistributedClaimsTimeout: 2 * time.Second, CredentialPaths: commaListFlag(), CredentialsUpdateInterval: 10 * time.Minute, ApiUsageMonitoringClientKeys: "sub", diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index 10cbeca6d9..50b60e30d8 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -7,6 +7,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net" @@ -19,9 +20,11 @@ import ( "time" "github.com/coreos/go-oidc" + "github.com/opentracing/opentracing-go" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "github.com/zalando/skipper/filters" + snet "github.com/zalando/skipper/net" "github.com/zalando/skipper/secrets" "golang.org/x/oauth2" ) @@ -38,8 +41,34 @@ const ( stateValidity = 1 * time.Minute oidcInfoHeader = "Skipper-Oidc-Info" cookieMaxSize = 4093 // common cookie size limit http://browsercookielimits.squawky.net/ + + // Deprecated: The host of the Azure Active Directory (AAD) graph API + azureADGraphHost = "graph.windows.net" +) + +var ( + distributedClaimsClients = sync.Map{} + microsoftGraphHost = "graph.microsoft.com" // global for testing ) +type distributedClaims struct { + ClaimNames map[string]string `json:"_claim_names"` + ClaimSources map[string]claimSource `json:"_claim_sources"` +} + +type claimSource struct { + Endpoint string `json:"endpoint"` + AccessToken string `json:"access_token,omitempty"` +} + +type azureGraphGroups struct { + OdataNextLink string `json:"@odata.nextLink,omitempty"` + Value []struct { + DisplayName string `json:"displayName"` + ID string `json:"id"` + } `json:"value"` +} + // Filter parameter: // // oauthOidc...("https://oidc-provider.example.com", "client_id", "client_secret", @@ -57,11 +86,18 @@ const ( paramSubdomainsToRemove ) +type OidcOptions struct { + MaxIdleConns int + Timeout time.Duration + Tracer opentracing.Tracer +} + type ( tokenOidcSpec struct { typ roleCheckType SecretsFile string secretsRegistry secrets.EncrypterCreator + options OidcOptions } tokenOidcFilter struct { @@ -79,6 +115,7 @@ type ( compressor cookieCompression upstreamHeaders map[string]string subdomainsToRemove int + oidcOptions OidcOptions } tokenContainer struct { @@ -98,21 +135,36 @@ type ( } ) -// NewOAuthOidcUserInfos creates filter spec which tests user info. +// NewOAuthOidcUserInfosWithOptions creates filter spec which tests user info. +func NewOAuthOidcUserInfosWithOptions(secretsFile string, secretsRegistry secrets.EncrypterCreator, o OidcOptions) filters.Spec { + return &tokenOidcSpec{typ: checkOIDCUserInfo, SecretsFile: secretsFile, secretsRegistry: secretsRegistry, options: o} +} + +// Deprecated: use NewOAuthOidcUserInfosWithOptions instead. func NewOAuthOidcUserInfos(secretsFile string, secretsRegistry secrets.EncrypterCreator) filters.Spec { - return &tokenOidcSpec{typ: checkOIDCUserInfo, SecretsFile: secretsFile, secretsRegistry: secretsRegistry} + return NewOAuthOidcUserInfosWithOptions(secretsFile, secretsRegistry, OidcOptions{}) } -// NewOAuthOidcAnyClaims creates a filter spec which verifies that the token +// NewOAuthOidcAnyClaimsWithOptions creates a filter spec which verifies that the token // has one of the claims specified +func NewOAuthOidcAnyClaimsWithOptions(secretsFile string, secretsRegistry secrets.EncrypterCreator, o OidcOptions) filters.Spec { + return &tokenOidcSpec{typ: checkOIDCAnyClaims, SecretsFile: secretsFile, secretsRegistry: secretsRegistry, options: o} +} + +// Deprecated: use NewOAuthOidcAnyClaimsWithOptions instead. func NewOAuthOidcAnyClaims(secretsFile string, secretsRegistry secrets.EncrypterCreator) filters.Spec { - return &tokenOidcSpec{typ: checkOIDCAnyClaims, SecretsFile: secretsFile, secretsRegistry: secretsRegistry} + return NewOAuthOidcAnyClaimsWithOptions(secretsFile, secretsRegistry, OidcOptions{}) } -// NewOAuthOidcAllClaims creates a filter spec which verifies that the token +// NewOAuthOidcAllClaimsWithOptions creates a filter spec which verifies that the token // has all the claims specified +func NewOAuthOidcAllClaimsWithOptions(secretsFile string, secretsRegistry secrets.EncrypterCreator, o OidcOptions) filters.Spec { + return &tokenOidcSpec{typ: checkOIDCAllClaims, SecretsFile: secretsFile, secretsRegistry: secretsRegistry, options: o} +} + +// Deprecated: use NewOAuthOidcAllClaimsWithOptions instead. func NewOAuthOidcAllClaims(secretsFile string, secretsRegistry secrets.EncrypterCreator) filters.Spec { - return &tokenOidcSpec{typ: checkOIDCAllClaims, SecretsFile: secretsFile, secretsRegistry: secretsRegistry} + return NewOAuthOidcAllClaimsWithOptions(secretsFile, secretsRegistry, OidcOptions{}) } // CreateFilter creates an OpenID Connect authorization filter. @@ -198,6 +250,7 @@ func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) encrypter: encrypter, compressor: newDeflatePoolCompressor(flate.BestCompression), subdomainsToRemove: subdomainsToRemove, + oidcOptions: s.options, } // user defined scopes @@ -820,6 +873,10 @@ func (f *tokenOidcFilter) tokenClaims(ctx filters.FilterContext, oauth2Token *oa return nil, "", requestErrorf("claims do not contain sub") } + if err = f.handleDistributedClaims(r.Context(), idToken, oauth2Token, tokenMap); err != nil { + return nil, "", requestErrorf("failed to handle distributed claims: %v", err) + } + return tokenMap, sub, nil } @@ -885,6 +942,150 @@ func (f *tokenOidcFilter) getTokenWithExchange(state *OauthState, ctx filters.Fi return oauth2Token, err } +// handleDistributedClaims handles if user has a distributed / overage token. +// https://docs.microsoft.com/en-us/azure/active-directory/develop/id-tokens#groups-overage-claim +// In Azure, if you are indirectly member of more than 200 groups, they will +// send _claim_names and _claim_sources instead of the groups, per OIDC Core 1.0, section 5.6.2: +// https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims +// Example: +// +// { +// "_claim_names": { +// "groups": "src1" +// }, +// "_claim_sources": { +// "src1": { +// "endpoint": "https://graph.windows.net/.../getMemberObjects" +// } +// } +// } +// +func (f *tokenOidcFilter) handleDistributedClaims(ctx context.Context, idToken *oidc.IDToken, oauth2Token *oauth2.Token, claimsMap map[string]interface{}) error { + // https://github.com/coreos/go-oidc/issues/171#issuecomment-1044286153 + var distClaims distributedClaims + err := idToken.Claims(&distClaims) + if err != nil { + return err + } + if len(distClaims.ClaimNames) == 0 || len(distClaims.ClaimSources) == 0 { + log.Debugf("No distributed claims found") + return nil + } + + for claim, ref := range distClaims.ClaimNames { + source, ok := distClaims.ClaimSources[ref] + if !ok { + return fmt.Errorf("invalid distributed claims: missing claim source for %s", claim) + } + uri, err := url.Parse(source.Endpoint) + if err != nil { + return fmt.Errorf("failed to parse distributed claim endpoint: %w", err) + } + + var results []interface{} + + switch uri.Host { + case azureADGraphHost, microsoftGraphHost: + results, err = f.handleDistributedClaimsAzure(uri, oauth2Token, claimsMap) + if err != nil { + return fmt.Errorf("failed to get distributed Azure claim: %w", err) + } + default: + return fmt.Errorf("unsupported distributed claims endpoint '%s', please create an issue at https://github.com/zalando/skipper/issues/new/choose", uri.Host) + } + + claimsMap[claim] = results + } + return nil +} + +// Azure customizations https://docs.microsoft.com/en-us/graph/migrate-azure-ad-graph-overview +// If the endpoints provided in _claim_source is pointed to the deprecated "graph.windows.net" api +// replace with handcrafted url to graph.microsoft.com +func (f *tokenOidcFilter) handleDistributedClaimsAzure(url *url.URL, oauth2Token *oauth2.Token, claimsMap map[string]interface{}) (values []interface{}, err error) { + url.Host = microsoftGraphHost + // transitiveMemberOf for group names + userID, ok := claimsMap["oid"].(string) + if !ok { + return nil, fmt.Errorf("oid claim not found in claims map") + } + url.Path = fmt.Sprintf("/v1.0/users/%s/transitiveMemberOf", userID) + q := url.Query() + q.Set("$select", "displayName,id") + url.RawQuery = q.Encode() + return f.resolveDistributedClaimAzure(url, oauth2Token) +} + +func (f *tokenOidcFilter) initClient() *snet.Client { + newCli := snet.NewClient(snet.Options{ + ResponseHeaderTimeout: f.oidcOptions.Timeout, + TLSHandshakeTimeout: f.oidcOptions.Timeout, + MaxIdleConnsPerHost: f.oidcOptions.MaxIdleConns, + Tracer: f.oidcOptions.Tracer, + OpentracingComponentTag: "skipper", + OpentracingSpanName: "distributedClaims", + }) + return newCli +} + +func (f *tokenOidcFilter) resolveDistributedClaimAzure(url *url.URL, oauth2Token *oauth2.Token) (values []interface{}, err error) { + var target azureGraphGroups + req, err := http.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, fmt.Errorf("error constructing groups endpoint request: %w", err) + } + oauth2Token.SetAuthHeader(req) + + cli, ok := distributedClaimsClients.Load(url.Host) + if !ok { + var loaded bool + newCli := f.initClient() + cli, loaded = distributedClaimsClients.LoadOrStore(url.Host, newCli) + if loaded { + newCli.Close() + } + } + + client, ok := cli.(*snet.Client) + if !ok { + return nil, errors.New("invalid distributed claims client type") + } + + res, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("unable to call API: %w", err) + } + body, err := io.ReadAll(res.Body) + res.Body.Close() // closing for connection reuse + if err != nil { + return nil, fmt.Errorf("failed to read API response: %w", err) + } + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API returned error: %s", string(body)) + } + + err = json.Unmarshal(body, &target) + if err != nil { + return nil, fmt.Errorf("unabled to decode response: %w", err) + } + for _, v := range target.Value { + values = append(values, v.DisplayName) + } + // recursive pagination + if target.OdataNextLink != "" { + nextURL, err := url.Parse(target.OdataNextLink) + if err != nil { + return nil, fmt.Errorf("failed to parse next link: %w", err) + } + vs, err := f.resolveDistributedClaimAzure(nextURL, oauth2Token) + if err != nil { + return nil, err + } + values = append(values, vs...) + } + return +} + func newDeflatePoolCompressor(level int) *deflatePoolCompressor { return &deflatePoolCompressor{ poolWriter: &sync.Pool{ diff --git a/filters/auth/oidc_introspection_test.go b/filters/auth/oidc_introspection_test.go index b1aa359810..a7ea9540f6 100644 --- a/filters/auth/oidc_introspection_test.go +++ b/filters/auth/oidc_introspection_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + jwt "github.com/golang-jwt/jwt/v4" "github.com/stretchr/testify/assert" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" @@ -289,7 +290,7 @@ func TestOIDCQueryClaimsFilter(t *testing.T) { t.Errorf("Failed to parse url %s: %v", proxy.URL, err) } reqURL.Path = tc.path - oidcServer := createOIDCServer(proxy.URL+"/redirect", validClient, "mysec") + oidcServer := createOIDCServer(proxy.URL+"/redirect", validClient, "mysec", jwt.MapClaims{"groups": []string{"CD-Administrators", "Purchasing-Department", "AppX-Test-Users", "white space"}}) defer oidcServer.Close() t.Logf("oidc/auth server URL: %s", oidcServer.URL) // create filter diff --git a/filters/auth/oidc_test.go b/filters/auth/oidc_test.go index 79b982dd40..69d29c8d00 100644 --- a/filters/auth/oidc_test.go +++ b/filters/auth/oidc_test.go @@ -128,7 +128,7 @@ var testOpenIDConfig = `{ // returns a localhost instance implementation of an OpenID Connect // server with configendpoint, tokenendpoint, authenticationserver endpoint, userinfor // endpoint, jwks endpoint -func createOIDCServer(cb, client, clientsecret string) *httptest.Server { +func createOIDCServer(cb, client, clientsecret string, extraClaims jwt.MapClaims) *httptest.Server { var oidcServer *httptest.Server oidcServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { @@ -222,21 +222,19 @@ func createOIDCServer(cb, client, clientsecret string) *httptest.Server { w.Header().Set("Cache-Control", "no-store") w.Header().Set("Pragma", "no-cache") - token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ + claims := jwt.MapClaims{ testKey: testValue, // claims to check "iss": oidcServer.URL, "sub": testSub, "aud": validClient, "iat": time.Now().Add(-time.Minute).UTC().Unix(), "exp": time.Now().Add(tokenExp).UTC().Unix(), - "groups": []string{ - "CD-Administrators", - "Purchasing-Department", - "AppX-Test-Users", - "white space", - }, "email": "someone@example.org", - }) + } + for k, v := range extraClaims { + claims[k] = v + } + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) privKey, err := os.ReadFile(keyPath) if err != nil { @@ -317,7 +315,43 @@ func createOIDCServer(cb, client, clientsecret string) *httptest.Server { if err != nil { log.Fatalf("Failed to write: %v", err) } - + case "/v1.0/users/me/transitiveMemberOf": + if r.Header.Get(authHeaderName) == authHeaderPrefix+validAccessToken && + r.URL.Query().Get("$select") == "displayName,id" { + body, err := json.Marshal(azureGraphGroups{ + OdataNextLink: fmt.Sprintf("http://%s/v1.0/users/paginatedresponse", r.Host), + Value: []struct { + DisplayName string `json:"displayName"` + ID string `json:"id"` + }{ + {DisplayName: "CD-Administrators", ID: "1"}, + {DisplayName: "Purchasing-Department", ID: "2"}, + }}) + if err != nil { + log.Fatalf("Failed to marshal to json: %v", err) + } + w.Write(body) + } else { + w.WriteHeader(401) + } + case "/v1.0/users/paginatedresponse": + if r.Header.Get(authHeaderName) == authHeaderPrefix+validAccessToken { + body, err := json.Marshal(azureGraphGroups{ + OdataNextLink: "", + Value: []struct { + DisplayName string `json:"displayName"` + ID string `json:"id"` + }{ + {DisplayName: "AppX-Test-Users", ID: "3"}, + {DisplayName: "white space", ID: "4"}, + }}) + if err != nil { + log.Fatalf("Failed to marshal to json: %v", err) + } + w.Write(body) + } else { + w.WriteHeader(401) + } default: w.WriteHeader(http.StatusNotFound) } @@ -460,30 +494,30 @@ func TestNewOidc(t *testing.T) { for _, tt := range []struct { name string args string - f func(string, secrets.EncrypterCreator) filters.Spec + f func(string, secrets.EncrypterCreator, OidcOptions) filters.Spec want *tokenOidcSpec }{ { name: "test UserInfo", args: "/foo", - f: NewOAuthOidcUserInfos, + f: NewOAuthOidcUserInfosWithOptions, want: &tokenOidcSpec{typ: checkOIDCUserInfo, SecretsFile: "/foo", secretsRegistry: reg}, }, { name: "test AnyClaims", args: "/foo", - f: NewOAuthOidcAnyClaims, + f: NewOAuthOidcAnyClaimsWithOptions, want: &tokenOidcSpec{typ: checkOIDCAnyClaims, SecretsFile: "/foo", secretsRegistry: reg}, }, { name: "test AllClaims", args: "/foo", - f: NewOAuthOidcAllClaims, + f: NewOAuthOidcAllClaimsWithOptions, want: &tokenOidcSpec{typ: checkOIDCAllClaims, SecretsFile: "/foo", secretsRegistry: reg}, }, } { t.Run(tt.name, func(t *testing.T) { - if got := tt.f(tt.args, reg); !reflect.DeepEqual(got, tt.want) { + if got := tt.f(tt.args, reg, OidcOptions{}); !reflect.DeepEqual(got, tt.want) { t.Errorf("Failed to create object: Want %v, got %v", tt.want, got) } }) @@ -492,7 +526,7 @@ func TestNewOidc(t *testing.T) { } func TestCreateFilterOIDC(t *testing.T) { - oidcServer := createOIDCServer("", "", "") + oidcServer := createOIDCServer("", "", "", nil) defer oidcServer.Close() for _, tt := range []struct { @@ -645,6 +679,7 @@ func TestOIDCSetup(t *testing.T) { expectNoCookies bool expectCookieDomain string filterCookies []string + extraClaims jwt.MapClaims }{{ msg: "wrong provider", filter: `oauthOidcAnyClaims("no url", "", "", "{{ .RedirectURL }}", "", "")`, @@ -704,8 +739,42 @@ func TestOIDCSetup(t *testing.T) { filter: `oauthOidcAllClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "uid", "sub uid", "", "x-auth-email:claims.email x-auth-something:claims.sub x-auth-groups:claims.groups.#[%\"*-Users\"]" )`, + extraClaims: jwt.MapClaims{"groups": []string{"CD-Administrators", "Purchasing-Department", "AppX-Test-Users", "white space"}}, expected: 200, expectRequest: "X-Auth-Email: someone@example.org\r\nX-Auth-Groups: AppX-Test-Users\r\nX-Auth-Something: somesub", + }, { + msg: "distributed Azure claims looked up in Microsoft Graph ", + filter: `oauthOidcAllClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "uid", "sub uid", "", + "x-auth-email:claims.email x-auth-something:claims.sub x-auth-groups:claims.groups.#[%\"*-Users\"]")`, + extraClaims: jwt.MapClaims{ + "oid": "me", + "_claim_names": map[string]string{"groups": "src1"}, + "_claim_sources": map[string]map[string]string{"src1": {"endpoint": "http://graph.windows.net/distributedClaims/getMemberObjects"}}, + }, + expected: 200, + expectRequest: "X-Auth-Email: someone@example.org\r\nX-Auth-Groups: AppX-Test-Users\r\nX-Auth-Something: somesub\r\n\r\n", + }, { + msg: "distributed Azure claims with pagination resolved", + filter: `oauthOidcAllClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "uid", "sub uid", "", + "x-auth-groups:claims.groups")`, + extraClaims: jwt.MapClaims{ + "oid": "me", + "_claim_names": map[string]string{"groups": "src1"}, + "_claim_sources": map[string]map[string]string{"src1": {"endpoint": "http://graph.windows.net/distributedClaims/getMemberObjects"}}, + }, + expected: 200, + expectRequest: "X-Auth-Groups: [\"CD-Administrators\",\"Purchasing-Department\",\"AppX-Test-Users\",\"white space\"]\r\n\r\n", + }, { + msg: "distributed claims on unsupported IdP no groups claim returned", + filter: `oauthOidcAllClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "uid", "sub uid", "", + "x-auth-email:claims.email x-auth-something:claims.sub x-auth-groups:claims.groups.#[%\"*-Users\"]")`, + extraClaims: jwt.MapClaims{ + "oid": "me", + "_claim_names": map[string]string{"groups": "src1"}, + "_claim_sources": map[string]map[string]string{"src1": {"endpoint": "http://unknown.com/someendpoint"}}, + }, + expected: 401, + expectNoCookies: true, }, { msg: "auth code with a placeholder and a regular option", filter: `oauthOidcAnyClaims("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "", "", "foo=skipper-request-query bar=baz")`, @@ -768,9 +837,9 @@ func TestOIDCSetup(t *testing.T) { secretsRegistry := secrettest.NewTestRegistry() fr := make(filters.Registry) - fr.Register(NewOAuthOidcUserInfos(secretsFile, secretsRegistry)) - fr.Register(NewOAuthOidcAnyClaims(secretsFile, secretsRegistry)) - fr.Register(NewOAuthOidcAllClaims(secretsFile, secretsRegistry)) + fr.Register(NewOAuthOidcUserInfosWithOptions(secretsFile, secretsRegistry, OidcOptions{})) + fr.Register(NewOAuthOidcAnyClaimsWithOptions(secretsFile, secretsRegistry, OidcOptions{})) + fr.Register(NewOAuthOidcAllClaimsWithOptions(secretsFile, secretsRegistry, OidcOptions{})) dc := testdataclient.New(nil) proxy := proxytest.WithRoutingOptions(fr, routing.Options{ @@ -788,10 +857,14 @@ func TestOIDCSetup(t *testing.T) { t.Logf("redirect URL: %s", redirectURL.String()) - oidcServer := createOIDCServer(redirectURL.String(), "valid-client", "mysec") + oidcServer := createOIDCServer(redirectURL.String(), "valid-client", "mysec", tc.extraClaims) defer oidcServer.Close() t.Logf("oidc server URL: %s", oidcServer.URL) + oidcsrv, err := url.Parse(oidcServer.URL) + assert.NoError(t, err) + microsoftGraphHost = oidcsrv.Host + if tc.queries != nil { q := reqURL.Query() for _, rq := range tc.queries { diff --git a/skipper.go b/skipper.go index 6428fe410a..9e8fbaac0f 100644 --- a/skipper.go +++ b/skipper.go @@ -748,6 +748,9 @@ type Options struct { // OIDCSecretsFile path to the file containing key to encrypt OpenID token OIDCSecretsFile string + // OIDCDistributedClaimsTimeout sets timeout duration while calling Distributed Claims endpoint. + OIDCDistributedClaimsTimeout time.Duration + // SecretsRegistry to store and load secretsencrypt SecretsRegistry *secrets.Registry @@ -1323,6 +1326,12 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { Tracer: tracer, } + oo := auth.OidcOptions{ + Timeout: o.OIDCDistributedClaimsTimeout, + MaxIdleConns: o.IdleConnectionsPerHost, + Tracer: tracer, + } + who := auth.WebhookOptions{ Timeout: o.WebhookTimeout, MaxIdleConns: o.IdleConnectionsPerHost, @@ -1342,9 +1351,9 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { auth.TokenintrospectionWithOptions(auth.NewSecureOAuthTokenintrospectionAnyKV, tio), auth.TokenintrospectionWithOptions(auth.NewSecureOAuthTokenintrospectionAllKV, tio), auth.WebhookWithOptions(who), - auth.NewOAuthOidcUserInfos(o.OIDCSecretsFile, o.SecretsRegistry), - auth.NewOAuthOidcAnyClaims(o.OIDCSecretsFile, o.SecretsRegistry), - auth.NewOAuthOidcAllClaims(o.OIDCSecretsFile, o.SecretsRegistry), + auth.NewOAuthOidcUserInfosWithOptions(o.OIDCSecretsFile, o.SecretsRegistry, oo), + auth.NewOAuthOidcAnyClaimsWithOptions(o.OIDCSecretsFile, o.SecretsRegistry, oo), + auth.NewOAuthOidcAllClaimsWithOptions(o.OIDCSecretsFile, o.SecretsRegistry, oo), auth.NewOIDCQueryClaimsFilter(), apiusagemonitoring.NewApiUsageMonitoring( o.ApiUsageMonitoringEnable, From f9b12961e4ee0cf10df91eca162db9185fc9783f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 17 May 2022 12:23:07 +0200 Subject: [PATCH 0029/1047] docs: predicate should have fragment by name to reference easily (#2005) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- docs/reference/architecture.md | 2 +- docs/reference/predicates.md | 4 ++-- docs/tutorials/basics.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/reference/architecture.md b/docs/reference/architecture.md index bba28a913e..9712a3a34e 100644 --- a/docs/reference/architecture.md +++ b/docs/reference/architecture.md @@ -106,7 +106,7 @@ The route matching logic can be summed up as follows: 2. _If_ step #1 matches multiple routes, which means there are multiple routes in the same position of the path tree, and all other predicates match the request, too, then the route with the highest - [weight](predicates.md#weight-priority) is matched. + [weight](predicates.md#weight) is matched. * this is an O(n) lookup, but only on the same leaf diff --git a/docs/reference/predicates.md b/docs/reference/predicates.md index 1383e0ae52..76b277065b 100644 --- a/docs/reference/predicates.md +++ b/docs/reference/predicates.md @@ -149,7 +149,7 @@ Uses standardized Forwarded header ([RFC 7239](https://tools.ietf.org/html/rfc72 More info about the header: [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded) -If multiple proxies chain values in the header, as a comma separated list, the predicates below will only match +If multiple proxies chain values in the header, as a comma separated list, the predicates below will only match the last value in the chain for each part of the header. Example: Forwarded: host=example.com;proto=https, host=example.org @@ -192,7 +192,7 @@ ForwardedProtocol("http") ForwardedProtocol("https") ``` -## Weight (priority) +## Weight By default, the weight (priority) of a route is determined by the number of defined predicates. diff --git a/docs/tutorials/basics.md b/docs/tutorials/basics.md index eb838b5bd4..f79e110faf 100644 --- a/docs/tutorials/basics.md +++ b/docs/tutorials/basics.md @@ -222,7 +222,7 @@ The route matching logic can be summed up as follows: 2. _If_ step #1 matches multiple routes, which means there are multiple routes in the same position of the path tree, and all other predicates match the request, too, then the route with the highest - [weight](../reference/predicates.md#weight-priority) is matched. + [weight](../reference/predicates.md#weight) is matched. * this is an O(n) lookup, but only on the same leaf From 611a8357a7f9ea96486e1fdf0c299ede4790ee3b Mon Sep 17 00:00:00 2001 From: Mustafa Saber Date: Tue, 17 May 2022 16:06:52 +0200 Subject: [PATCH 0030/1047] chore: add slack in badges for more reachability (#2007) Signed-off-by: Mustafa Abdelrahman --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 0792d78186..92bf925179 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,7 @@ [![codecov](https://codecov.io/gh/zalando/skipper/branch/master/graph/badge.svg)](https://codecov.io/gh/zalando/skipper) [![GitHub release](https://img.shields.io/github/release/zalando/skipper.svg)](https://github.com/zalando/skipper/releases) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2461/badge)](https://bestpractices.coreinfrastructure.org/en/projects/2461) +[![Slack](https://img.shields.io/badge/Gopher%20Slack-%23skipper-green.svg)](https://invite.slack.golangbridge.org/) ![CodeQL](https://github.com/zalando/skipper/actions/workflows/codeql-analysis.yml/badge.svg) From 57b41e3a31ab059112ed43c8c0d4e610f7351b9c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 30 May 2022 13:00:51 +0200 Subject: [PATCH 0031/1047] Implements cluster leaky bucket rate limit filter (#1590) * Implements cluster leaky bucket rate limit filter The `clusterLeakyBucketRatelimit` filter implements ratelimiting _meter_ variant of [Leaky Bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket) and uses Redis as a storage. Leaky Bucket algorithm is also known as [Generic cell rate algorithm](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm). Prior art --- * https://github.com/throttled/throttled - implements CGRA algorithm, uses optimistic locking i.e. two queries (get and set) to the storage per request * https://github.com/brandur/redis-cell - implements GCRA as a Redis module * https://github.com/rwz/redis-gcra - implements GCRA as Redis lua script, uses Redis time, microsecond-only precision, floating point, divisions and rounding. * https://github.com/go-redis/redis_rate - for go-redis v7 currently used by skipper implements [basic rate limiter](https://redis.io/commands/incr#pattern-rate-limiter) as a counter, since v8 uses lua script implementation from `rwz`. This implementation uses Redis lua script and makes single Redis query per request. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 100 ++++++++++++++ filters/filters.go | 1 + filters/ratelimit/leakybucket.go | 119 +++++++++++++++++ filters/ratelimit/leakybucket_test.go | 185 ++++++++++++++++++++++++++ net/redisclient.go | 13 ++ ratelimit/leakybucket.go | 124 +++++++++++++++++ ratelimit/leakybucket.lua | 32 +++++ ratelimit/leakybucket_test.go | 149 +++++++++++++++++++++ skipper.go | 4 + 9 files changed, 727 insertions(+) create mode 100644 filters/ratelimit/leakybucket.go create mode 100644 filters/ratelimit/leakybucket_test.go create mode 100644 ratelimit/leakybucket.go create mode 100644 ratelimit/leakybucket.lua create mode 100644 ratelimit/leakybucket_test.go diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 99f7fbe8f6..a1cb4a34ea 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1748,6 +1748,106 @@ foo: Path("/baz") Configures rate limit of 100 requests per second for each `backend1` and `backend2` and responds with `429 Too Many Requests` when limit is reached. +## clusterLeakyBucketRatelimit + +Implements leaky bucket rate limit algorithm that uses Redis as a storage. +Requires command line flags `-enable-ratelimits`, `-enable-swarm` and `-swarm-redis-urls` to be set. + +The leaky bucket is an algorithm based on an analogy of how a bucket with a constant leak will overflow if either +the average rate at which water is poured in exceeds the rate at which the bucket leaks or if more water than +the capacity of the bucket is poured in all at once, see https://en.wikipedia.org/wiki/Leaky_bucket + +Parameters: + +* label (string) +* leak rate volume (int) +* leak rate period (time.Duration) +* capacity (int) +* increment (int) + +The bucket label, leak rate (volume/period) and capacity uniquely identify the bucket. + +Label supports [template placeholders](#template-placeholders). +If a template placeholder can't be resolved then request is allowed and does not add to any bucket. + +Leak rate (divided by increment) defines a maximum average allowed request rate. +The rate is configured by two parameters for convenience and consistency with other filters but is actually a single number, +e.g. the rate of 2 per second equals to the rate of 20 per 10 seconds or 120 per minute. + +Capacity defines the maximum request burst size or an allowed jitter. + +Each passing request adds increment amount to the bucket, different routes may add different amounts to the same bucket. + +Configuration with equal capacity and increment allows no jitter: first request fills up the bucket full and +subsequent request will be rejected if it arrives earlier than `emission interval = 1/leak rate`. + +Real requests always have a jitter which can be demonstrated by the configuration having capacity and increment of one: +``` +r1: * -> clusterLeakyBucketRatelimit("1rps", 1, "1s", 1, 1) -> status(200) -> ; +``` +it does not allow jitter and therefore rejects ~half of the requests coming at rate of 1 rps: +``` +$ echo "GET http://localhost:9090" | vegeta attack -rate=1/s -duration=1m | vegeta report +Requests [total, rate, throughput] 60, 1.02, 0.58 +Duration [total, attack, wait] 59.001991855s, 59.000310522s, 1.681333ms +Latencies [mean, 50, 95, 99, max] 1.721207ms, 1.555227ms, 1.943115ms, 10.689486ms, 11.538278ms +Bytes In [total, mean] 0, 0.00 +Bytes Out [total, mean] 0, 0.00 +Success [ratio] 56.67% +Status Codes [code:count] 200:34 429:26 +Error Set: +429 Too Many Requests +``` + +On the other hand the configuration with capacity greater than increment: +``` +r1: * -> clusterLeakyBucketRatelimit("1rps2", 1, "1s", 2, 1) -> status(200) -> ; +``` +allows all requests: +``` +~$ echo "GET http://localhost:9090" | vegeta attack -rate=1/s -duration=1m | vegeta report +Requests [total, rate, throughput] 60, 1.02, 1.02 +Duration [total, attack, wait] 59.00023518s, 58.999779118s, 456.062µs +Latencies [mean, 50, 95, 99, max] 1.410641ms, 1.585908ms, 1.859727ms, 8.285963ms, 8.997149ms +Bytes In [total, mean] 0, 0.00 +Bytes Out [total, mean] 0, 0.00 +Success [ratio] 100.00% +Status Codes [code:count] 200:60 +Error Set: +``` +and even if rate is greater than 1 rps the average allowed request rate is still equal to the leak rate of 1 rps: +``` +$ echo "GET http://localhost:9090" | vegeta attack -rate=11/10s -duration=1m | vegeta report +Requests [total, rate, throughput] 66, 1.12, 1.03 +Duration [total, attack, wait] 59.091880389s, 59.089985762s, 1.894627ms +Latencies [mean, 50, 95, 99, max] 1.709568ms, 1.60613ms, 1.925731ms, 10.601822ms, 12.10052ms +Bytes In [total, mean] 0, 0.00 +Bytes Out [total, mean] 0, 0.00 +Success [ratio] 92.42% +Status Codes [code:count] 200:61 429:5 +Error Set: +429 Too Many Requests +``` + +Therefore the capacity should be configured greater than increment unless strict request interval needs to be enforced. +Configuration having capacity below increment rejects all requests. + +Examples: +``` +// allow each unique Authorization header once in five seconds +clusterLeakyBucketRatelimit("auth-${request.header.Authorization}", 1, "5s", 2, 1) + +// allow 60 requests per hour (each subsequent request allowed not earlied than after 1h/60 = 1m) for all clients +clusterLeakyBucketRatelimit("hourly", 60, "1h", 1, 1) + +// allow 10 requests per minute for each unique PHPSESSID cookie with bursts of up to 5 requests +clusterLeakyBucketRatelimit("session-${request.cookie.PHPSESSID}", 10, "1m", 5, 1) + +// use the same bucket but add different amount (i.e. one /expensive request counts as two /cheap) +Path("/cheap") -> clusterLeakyBucketRatelimit("user-${request.cookie.Authorization}", 1, "1s", 5, 1) -> ... +Path("/expensive") -> clusterLeakyBucketRatelimit("user-${request.cookie.Authorization}", 1, "1s", 5, 2) -> ... +``` + ## lua See [the scripts page](scripts.md) diff --git a/filters/filters.go b/filters/filters.go index fd30558683..ef805b59f2 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -268,6 +268,7 @@ const ( RatelimitName = "ratelimit" ClusterClientRatelimitName = "clusterClientRatelimit" ClusterRatelimitName = "clusterRatelimit" + ClusterLeakyBucketRatelimitName = "clusterLeakyBucketRatelimit" BackendRateLimitName = "backendRatelimit" LuaName = "lua" CorsOriginName = "corsOrigin" diff --git a/filters/ratelimit/leakybucket.go b/filters/ratelimit/leakybucket.go new file mode 100644 index 0000000000..e674899aeb --- /dev/null +++ b/filters/ratelimit/leakybucket.go @@ -0,0 +1,119 @@ +package ratelimit + +import ( + "context" + "fmt" + "net/http" + "strconv" + "time" + + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/ratelimit" +) + +type leakyBucket interface { + Add(ctx context.Context, label string, increment int) (added bool, retry time.Duration, err error) +} + +type leakyBucketSpec struct { + create func(capacity int, emission time.Duration) leakyBucket +} + +type leakyBucketFilter struct { + label *eskip.Template + bucket leakyBucket + increment int +} + +// NewClusterLeakyBucketRatelimit creates a filter Spec, whose instances implement rate limiting using leaky bucket algorithm. +// +// The leaky bucket is an algorithm based on an analogy of how a bucket with a constant leak will overflow if either +// the average rate at which water is poured in exceeds the rate at which the bucket leaks or if more water than +// the capacity of the bucket is poured in all at once. +// See https://en.wikipedia.org/wiki/Leaky_bucket +// +// Example to allow each unique Authorization header once in five seconds: +// +// clusterLeakyBucketRatelimit("auth-${request.header.Authorization}", 1, "5s", 2, 1) +// +func NewClusterLeakyBucketRatelimit(registry *ratelimit.Registry) filters.Spec { + return &leakyBucketSpec{ + create: func(capacity int, emission time.Duration) leakyBucket { + return ratelimit.NewClusterLeakyBucket(registry, capacity, emission) + }, + } +} + +func (s *leakyBucketSpec) Name() string { + return filters.ClusterLeakyBucketRatelimitName +} + +func (s *leakyBucketSpec) CreateFilter(args []interface{}) (filters.Filter, error) { + if len(args) != 5 { + return nil, filters.ErrInvalidFilterParameters + } + + label, ok := args[0].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + leakVolume, err := natural(args[1]) + if err != nil { + return nil, err + } + + leakPeriod, err := getDurationArg(args[2]) + if err != nil { + return nil, err + } + if leakPeriod <= 0 { + return nil, filters.ErrInvalidFilterParameters + } + + capacity, err := natural(args[3]) + if err != nil { + return nil, err + } + + increment, err := natural(args[4]) + if err != nil { + return nil, err + } + + // emission is the reciprocal of the leak rate + emission := leakPeriod / time.Duration(leakVolume) + + return &leakyBucketFilter{eskip.NewTemplate(label), s.create(capacity, emission), increment}, nil +} + +func (f *leakyBucketFilter) Request(ctx filters.FilterContext) { + label, ok := f.label.ApplyContext(ctx) + if !ok { + return // allow on missing placeholders + } + added, retry, err := f.bucket.Add(ctx.Request().Context(), label, f.increment) + if err != nil { + return // allow on error + } + if added { + return // allow if successfully added + } + + header := http.Header{} + if retry > 0 { + header.Set("Retry-After", strconv.Itoa(int(retry/time.Second))) + } + ctx.Serve(&http.Response{StatusCode: http.StatusTooManyRequests, Header: header}) +} + +func (*leakyBucketFilter) Response(filters.FilterContext) {} + +func natural(arg interface{}) (n int, err error) { + n, err = getIntArg(arg) + if err == nil && n <= 0 { + err = fmt.Errorf(`number %d must be positive`, n) + } + return +} diff --git a/filters/ratelimit/leakybucket_test.go b/filters/ratelimit/leakybucket_test.go new file mode 100644 index 0000000000..0417d5c824 --- /dev/null +++ b/filters/ratelimit/leakybucket_test.go @@ -0,0 +1,185 @@ +package ratelimit + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/filtertest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLeakyBucketFilterInvalidArgs(t *testing.T) { + spec := &leakyBucketSpec{ + create: func(_ int, _ time.Duration) leakyBucket { + t.Fatal("unexpected call to create a bucket") + return nil + }, + } + assert.Equal(t, filters.ClusterLeakyBucketRatelimitName, spec.Name()) + + for i, test := range []struct { + args []interface{} + }{ + {[]interface{}{"missing args"}}, + {[]interface{}{123, 1, "1s", 1, 1}}, + {[]interface{}{"alabel", "invalid volume", "1s", 1, 1}}, + {[]interface{}{"alabel", 1, "invalid period", 1, 1}}, + {[]interface{}{"alabel", 1, "1s", "invalid capacity", 1}}, + {[]interface{}{"alabel", 1, "1s", 1, "invalid increment"}}, + {[]interface{}{"zero volume", 0, "1s", 1, 1}}, + {[]interface{}{"zero period", 1, "0s", 1, 1}}, + {[]interface{}{"zero capacity", 1, "1s", 0, 1}}, + {[]interface{}{"zero increment", 1, "1s", 1, 0}}, + } { + t.Run(fmt.Sprintf("test#%d", i), func(t *testing.T) { + _, err := spec.CreateFilter(test.args) + + assert.Error(t, err) + }) + } +} + +func TestLeakyBucketFilterValidArgs(t *testing.T) { + for i, test := range []struct { + args []interface{} + expectCapacity int + expectEmission time.Duration + expectIncrement int + }{ + { + args: []interface{}{"alabel", 4, "1s", 2, 1}, + expectCapacity: 2, + expectEmission: 250 * time.Millisecond, + expectIncrement: 1, + }, + { + args: []interface{}{"floatargs", 4.0, "1s", 2.0, 1.0}, + expectCapacity: 2, + expectEmission: 250 * time.Millisecond, + expectIncrement: 1, + }, + } { + t.Run(fmt.Sprintf("test#%d", i), func(t *testing.T) { + spec := &leakyBucketSpec{ + create: func(capacity int, emission time.Duration) leakyBucket { + assert.Equal(t, test.expectCapacity, capacity) + assert.Equal(t, test.expectEmission, emission) + return nil + }, + } + + f, err := spec.CreateFilter(test.args) + + assert.NoError(t, err) + assert.Equal(t, test.expectIncrement, f.(*leakyBucketFilter).increment) + }) + } +} + +type leakyBucketFunc func(context.Context, string, int) (bool, time.Duration, error) + +func (b leakyBucketFunc) Add(ctx context.Context, label string, increment int) (added bool, retry time.Duration, err error) { + return b(ctx, label, increment) +} + +func TestLeakyBucketFilterRequest(t *testing.T) { + for _, test := range []struct { + name string + args []interface{} + add func(*testing.T, string, int) (bool, time.Duration, error) + served bool + status int + retryAfter string + }{ + { + name: "allow on missing placeholder", + args: []interface{}{"alabel-${missing}", 3, "1s", 2, 1}, + add: func(t *testing.T, _ string, _ int) (bool, time.Duration, error) { + t.Error("unexpected call on missing placeholder") + return false, 0, nil + }, + }, + { + name: "allow on error", + args: []interface{}{"alabel", 3, "1s", 2, 1}, + add: func(*testing.T, string, int) (bool, time.Duration, error) { + return false, 0, fmt.Errorf("oops") + }, + }, + { + name: "allow on added", + args: []interface{}{"alabel", 3, "1s", 2, 1}, + add: func(t *testing.T, label string, increment int) (bool, time.Duration, error) { + assert.Equal(t, "alabel", label) + assert.Equal(t, 1, increment) + return true, 0, nil + }, + }, + { + name: "allow with a placeholder", + args: []interface{}{"alabel-${request.header.X-Foo}", 3, "1s", 2, 1}, + add: func(t *testing.T, label string, increment int) (bool, time.Duration, error) { + assert.Equal(t, "alabel-bar", label) + assert.Equal(t, 1, increment) + return true, 0, nil + }, + }, + { + name: "deny", + args: []interface{}{"alabel", 3, "1s", 2, 1}, + add: func(t *testing.T, label string, increment int) (bool, time.Duration, error) { + assert.Equal(t, "alabel", label) + assert.Equal(t, 1, increment) + return false, 3 * time.Second, nil + }, + served: true, + status: 429, + retryAfter: "3", + }, + { + name: "deny with a placeholder", + args: []interface{}{"alabel-${request.header.X-Foo}", 3, "1s", 2, 1}, + add: func(t *testing.T, label string, increment int) (bool, time.Duration, error) { + assert.Equal(t, "alabel-bar", label) + assert.Equal(t, 1, increment) + return false, 3 * time.Second, nil + }, + served: true, + status: 429, + retryAfter: "3", + }, + } { + t.Run(test.name, func(t *testing.T) { + spec := &leakyBucketSpec{ + create: func(capacity int, emission time.Duration) leakyBucket { + return leakyBucketFunc(func(_ context.Context, label string, increment int) (bool, time.Duration, error) { + return test.add(t, label, increment) + }) + }, + } + + f, err := spec.CreateFilter(test.args) + require.NoError(t, err) + + ctx := &filtertest.Context{ + FRequest: &http.Request{Header: http.Header{"X-Foo": []string{"bar"}}}, + } + + f.Request(ctx) + + if test.served { + assert.True(t, ctx.FServed) + assert.Equal(t, test.status, ctx.FResponse.StatusCode) + assert.Equal(t, test.retryAfter, ctx.FResponse.Header.Get("Retry-After")) + } else { + assert.False(t, ctx.FServed) + } + }) + } +} diff --git a/net/redisclient.go b/net/redisclient.go index 9ffe0ff60d..42c221d4c0 100644 --- a/net/redisclient.go +++ b/net/redisclient.go @@ -82,6 +82,10 @@ type RedisRingClient struct { quit chan struct{} } +type RedisScript struct { + script *redis.Script +} + const ( // DefaultReadTimeout is the default socket read timeout DefaultReadTimeout = 25 * time.Millisecond @@ -277,6 +281,7 @@ func (r *RedisRingClient) Get(ctx context.Context, key string) (string, error) { res := r.ring.Get(ctx, key) return res.Val(), res.Err() } + func (r *RedisRingClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) (string, error) { res := r.ring.Set(ctx, key, value, expiration) return res.Result() @@ -325,3 +330,11 @@ func (r *RedisRingClient) ZRangeByScoreWithScoresFirst(ctx context.Context, key return zs[0].Member, nil } + +func (r *RedisRingClient) NewScript(source string) *RedisScript { + return &RedisScript{redis.NewScript(source)} +} + +func (r *RedisRingClient) RunScript(ctx context.Context, s *RedisScript, keys []string, args ...interface{}) (interface{}, error) { + return s.script.Run(ctx, r.ring, keys, args...).Result() +} diff --git a/ratelimit/leakybucket.go b/ratelimit/leakybucket.go new file mode 100644 index 0000000000..7a432d1fac --- /dev/null +++ b/ratelimit/leakybucket.go @@ -0,0 +1,124 @@ +package ratelimit + +import ( + "context" + _ "embed" + "fmt" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/zalando/skipper/metrics" + "github.com/zalando/skipper/net" +) + +type ClusterLeakyBucket struct { + capacity int + emission time.Duration + labelPrefix string + script *net.RedisScript + ringClient *net.RedisRingClient + metrics metrics.Metrics + now func() time.Time +} + +const ( + leakyBucketRedisKeyPrefix = "lkb." + leakyBucketMetricPrefix = "leakybucket.redis." + leakyBucketMetricLatency = leakyBucketMetricPrefix + "latency" + leakyBucketSpanName = "redis_leakybucket" +) + +// Implements leaky bucket algorithm as a Redis lua script. +// Redis guarantees that a script is executed in an atomic way: +// no other script or Redis command will be executed while a script is being executed. +// +// Possible optimization: substitute capacity and emission in script source code +// on script creation in order not to send them over the wire on each call. +// This way every distinct bucket configuration will get its own script. +// +// See https://redis.io/commands/eval +// +//go:embed leakybucket.lua +var leakyBucketScript string + +// NewClusterLeakyBucket creates a class of leaky buckets of a given capacity and emission. +// Emission is the reciprocal of the leak rate and equals the time to leak one unit. +// +// The leaky bucket is an algorithm based on an analogy of how a bucket with a constant leak will overflow if either +// the average rate at which water is poured in exceeds the rate at which the bucket leaks or if more water than +// the capacity of the bucket is poured in all at once. +// See https://en.wikipedia.org/wiki/Leaky_bucket +func NewClusterLeakyBucket(r *Registry, capacity int, emission time.Duration) *ClusterLeakyBucket { + return newClusterLeakyBucket(r.redisRing, capacity, emission, time.Now) +} + +func newClusterLeakyBucket(ringClient *net.RedisRingClient, capacity int, emission time.Duration, now func() time.Time) *ClusterLeakyBucket { + return &ClusterLeakyBucket{ + capacity: capacity, + emission: emission, + labelPrefix: fmt.Sprintf("%d-%v-", capacity, emission), + script: ringClient.NewScript(leakyBucketScript), + ringClient: ringClient, + metrics: metrics.Default, + now: now, + } +} + +// Add adds an increment amount to the bucket identified by the label. +// It returns true if the amount was successfully added to the bucket or a time to wait for the next attempt. +// It also returns any error occurred during the attempt. +func (b *ClusterLeakyBucket) Add(ctx context.Context, label string, increment int) (added bool, retry time.Duration, err error) { + if increment > b.capacity { + // not allowed to add more than capacity and retry is not possible + return false, 0, nil + } + + now := b.now() + span := b.startSpan(ctx) + defer span.Finish() + defer b.metrics.MeasureSince(leakyBucketMetricLatency, now) + + added, retry, err = b.add(ctx, label, increment, now) + if err != nil { + ext.Error.Set(span, true) + } + return +} + +func (b *ClusterLeakyBucket) add(ctx context.Context, label string, increment int, now time.Time) (added bool, retry time.Duration, err error) { + r, err := b.ringClient.RunScript(ctx, b.script, + []string{b.getBucketId(label)}, + b.capacity, + b.emission.Microseconds(), + increment, + now.UnixMicro(), + ) + + if err == nil { + x := r.(int64) + if x >= 0 { + added, retry = true, 0 + } else { + added, retry = false, -time.Duration(x)*time.Microsecond + } + } + return +} + +func (b *ClusterLeakyBucket) getBucketId(label string) string { + return leakyBucketRedisKeyPrefix + getHashedKey(b.labelPrefix+label) +} + +func (b *ClusterLeakyBucket) startSpan(ctx context.Context) (span opentracing.Span) { + parent := opentracing.SpanFromContext(ctx) + if parent != nil { + span = b.ringClient.StartSpan(leakyBucketSpanName, opentracing.ChildOf(parent.Context())) + } else { + span = opentracing.NoopTracer{}.StartSpan("") + } + ext.Component.Set(span, "skipper") + ext.SpanKind.Set(span, "client") + return +} diff --git a/ratelimit/leakybucket.lua b/ratelimit/leakybucket.lua new file mode 100644 index 0000000000..d15e005784 --- /dev/null +++ b/ratelimit/leakybucket.lua @@ -0,0 +1,32 @@ +local bucket_id = KEYS[1] -- bucket id +local capacity = tonumber(ARGV[1]) -- bucket capacity in units (increment <= capacity) +local emission = tonumber(ARGV[2]) -- time to leak one unit in microseconds (emission > 0) +local increment = tonumber(ARGV[3]) -- increment in units (increment <= capacity) +local now = tonumber(ARGV[4]) -- current time in microseconds (now >= 0) + +-- Redis stores the timestamp when bucket drains out. +-- Lua uses double floating-point as a number type which can precisely represent integers only up to 2^53. +-- The timestamp is stored in microseconds (and not nanoseconds) to keep values below 2^53. +-- If bucket does not exist or is drained out, consider it empty now. +local empty_at = redis.call("GET", bucket_id) +if not empty_at then + empty_at = now +else + empty_at = tonumber(empty_at) + if empty_at < now then + empty_at = now + end +end + +-- bucket level == time to drain / emission == (empty_at - now) / emission +-- free capacity left after increment == capacity - bucket level - increment +-- If free capacity is negative then retry is possible after -(free capacity * emission) +-- Calculate and check the value of x == free capacity * emission +local x = (capacity - increment) * emission - (empty_at - now) +if x >= 0 then + empty_at = empty_at + increment * emission + + redis.call("SET", bucket_id, empty_at, "PX", math.ceil((empty_at - now) / 1000)) +end + +return x diff --git a/ratelimit/leakybucket_test.go b/ratelimit/leakybucket_test.go new file mode 100644 index 0000000000..dc8407e4c9 --- /dev/null +++ b/ratelimit/leakybucket_test.go @@ -0,0 +1,149 @@ +package ratelimit + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/zalando/skipper/net" + "github.com/zalando/skipper/net/redistest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type attempt struct { + tplus int + added bool + retry int +} + +func TestLeakyBucketAdd(t *testing.T) { + verifyAttempts(t, 3, time.Minute, 1, []attempt{ + // initial burst of three units fills up capacity + {+0, true, 0}, // just added unit leaks after 60s + {+1, true, 0}, // after 59 + {+2, true, 0}, // after 58 + // the bucket is full + {+3, false, 57}, + {+4, false, 56}, + // ... + {+58, false, 2}, + {+59, false, 1}, + // by this point one unit has leaked + {+60, true, 0}, + // the bucket is full again + {+61, false, 59}, + {+62, false, 58}, + {+63, false, 57}, + // ... wait two minutes to add two units + {+180, true, 0}, + {+181, true, 0}, + // the bucket is full again + {+182, false, 58}, + {+183, false, 57}, + // ... + }) +} + +func TestLeakyBucketAddMoreThanCapacity(t *testing.T) { + verifyAttempts(t, 1, time.Minute, 2, []attempt{ + {+0, false, 0}, // not allowed and no retry possible + {+61, false, 0}, // even after a minute + }) +} + +func TestLeakyBucketAddAtSlowRate(t *testing.T) { + verifyAttempts(t, 1, time.Second/2, 1, []attempt{ + {+0, true, 0}, + {+1, true, 0}, + {+2, true, 0}, + }) +} + +func verifyAttempts(t *testing.T, capacity int, emission time.Duration, increment int, attempts []attempt) { + redisAddr, done := redistest.NewTestRedis(t) + defer done() + + ringClient := net.NewRedisRingClient( + &net.RedisOptions{ + Addrs: []string{redisAddr}, + }, + ) + defer ringClient.Close() + + now := time.Now() + bucket := newClusterLeakyBucket(ringClient, capacity, emission, func() time.Time { return now }) + + t0 := now + for _, a := range attempts { + now = t0.Add(time.Duration(a.tplus) * time.Second) + added, retry, err := bucket.Add(context.Background(), "alabel", increment) + if err != nil { + t.Fatal(err) + } + if a.added != added { + t.Errorf("error at %+d: added mismatch, expected %v, got %v", a.tplus, a.added, added) + } + expectedRetry := time.Duration(a.retry) * time.Second + if expectedRetry != retry { + t.Errorf("error at %+d: retry mismatch, expected %v, got %v", a.tplus, expectedRetry, retry) + } + } +} + +func TestLeakyBucketRedisError(t *testing.T) { + ringClient := net.NewRedisRingClient( + &net.RedisOptions{ + Addrs: []string{"no-such-host.test:123"}, + }, + ) + defer ringClient.Close() + + bucket := newClusterLeakyBucket(ringClient, 1, time.Minute, time.Now) + _, _, err := bucket.Add(context.Background(), "alabel", 1) + + assert.Error(t, err) +} + +func TestLeakyBucketId(t *testing.T) { + const label = "alabel" + + b1 := newClusterLeakyBucket(nil, 1, time.Minute, time.Now) + b2 := newClusterLeakyBucket(nil, 2, time.Minute, time.Now) + + assert.NotEqual(t, b1.getBucketId(label), b2.getBucketId(label)) +} + +func TestLeakyBucketRedisStoredNumberPrecision(t *testing.T) { + redisAddr, done := redistest.NewTestRedis(t) + defer done() + + ringClient := net.NewRedisRingClient( + &net.RedisOptions{ + Addrs: []string{redisAddr}, + }, + ) + defer ringClient.Close() + + const ( + capacity = 10 + emission = time.Second + label = "alabel" + increment = 2 + ) + + now := time.Now() + b := newClusterLeakyBucket(ringClient, capacity, emission, func() time.Time { return now }) + + _, _, err := b.Add(context.Background(), label, increment) + require.NoError(t, err) + + v, err := ringClient.Get(context.Background(), b.getBucketId(label)) + require.NoError(t, err) + + expected := now.UnixMicro() + (increment * emission).Microseconds() + + assert.Equal(t, fmt.Sprintf("%d", expected), v) +} diff --git a/skipper.go b/skipper.go index 9e8fbaac0f..e64b43991c 100644 --- a/skipper.go +++ b/skipper.go @@ -1452,6 +1452,10 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ratelimitfilters.NewDisableRatelimit(provider), ratelimitfilters.NewBackendRatelimit(), ) + + if redisOptions != nil { + o.CustomFilters = append(o.CustomFilters, ratelimitfilters.NewClusterLeakyBucketRatelimit(ratelimitRegistry)) + } } if o.TLSMinVersion == 0 { From 325d1bb2eacc9083952089c922e72d702dcd031c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Mon, 30 May 2022 23:12:04 +0200 Subject: [PATCH 0032/1047] fix dependeny alerts (#2010) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix dependeny alerts - https://github.com/zalando/skipper/security/dependabot/5 Unhandled exception in gopkg.in/yaml.v3 - https://github.com/zalando/skipper/security/dependabot/4 Default inheritable capabilities for linux container should be empty Signed-off-by: Sandor Szücs * run go mod tidy Signed-off-by: Sandor Szücs --- go.mod | 6 +++--- go.sum | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 9377bbe06f..4ed0ed42f2 100644 --- a/go.mod +++ b/go.mod @@ -100,7 +100,7 @@ require ( github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/opencontainers/runc v1.0.3 // indirect + github.com/opencontainers/runc v1.1.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -115,7 +115,7 @@ require ( go.opencensus.io v0.22.4 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 // indirect + golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect @@ -124,7 +124,7 @@ require ( google.golang.org/grpc v1.43.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.8.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect ) diff --git a/go.sum b/go.sum index c543ec33f3..8a0503dae4 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,7 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -147,6 +148,7 @@ github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmE github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 h1:rdWOzitWlNYeUsXmz+IQfa9NkGEq3gA/qQ3mOEqBU6o= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9/go.mod h1:X97UjDTXp+7bayQSFZk2hPvCTmTZIicUjZQRtkwgAKY= github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 h1:cJ1E8ZwZLfercTX3dywnCAQDilbbi+m2cw3+8tCFpRo= @@ -176,6 +178,7 @@ github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -252,6 +255,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= @@ -397,6 +401,7 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -664,8 +669,8 @@ github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= -github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= +github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -676,6 +681,7 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0= github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -748,6 +754,7 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.21.2 h1:fIOk3hyqV1oGKogfGNjUZa0lUbtlkx3+ZT0IoJth2uM= github.com/shirou/gopsutil/v3 v3.21.2/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= @@ -1099,10 +1106,12 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 h1:T6tyxxvHMj2L1R2kZg0uNMpS8ZhB9lRa9XRGTCSA65w= golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= @@ -1327,8 +1336,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= From 4f31cd7cf465166dff0075ba9e251ace4d00128a Mon Sep 17 00:00:00 2001 From: bocytko Date: Tue, 31 May 2022 02:04:17 +0200 Subject: [PATCH 0033/1047] Fixes link to ratelimiter docs (#2011) Signed-off-by: Bartosz Ocytko --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 92bf925179..aea761c85f 100644 --- a/readme.md +++ b/readme.md @@ -179,7 +179,7 @@ and detailed information on these topics: - [Proxy Packages](https://godoc.org/github.com/zalando/skipper/proxy) - [Logging](https://godoc.org/github.com/zalando/skipper/logging) and [Metrics](https://godoc.org/github.com/zalando/skipper/metrics) - Performance Considerations -- [Rate Limiters](https://godoc.org/github.com/zalando/skipper/reference/filters/#ratelimit) +- [Rate Limiters](https://godoc.org/github.com/zalando/skipper/filters/ratelimit) - [Opentracing plugin](https://github.com/skipper-plugins/opentracing/) or extend [create your own](https://opensource.zalando.com/skipper/reference/plugins/#opentracing-plugins) - [WAF plugin](https://github.com/jptosso/coraza-waf) - Web Application Firewall filter for Skipper routes From b1f7979e79c9fdfd6dd9401351099757a274f49c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 13 Jun 2022 11:08:14 +0200 Subject: [PATCH 0034/1047] Makefile: remove GO111MODULE (#2014) Signed-off-by: Alexander Yastrebov --- Makefile | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 80d872b998..46832e3206 100644 --- a/Makefile +++ b/Makefile @@ -10,27 +10,26 @@ TEST_PLUGINS = _test_plugins/filter_noop.so \ _test_plugins/dataclient_noop.so \ _test_plugins/multitype_noop.so \ _test_plugins_fail/fail.so -GO111 ?= on default: build lib: $(SOURCES) - GO111MODULE=$(GO111) go build $(PACKAGES) + go build $(PACKAGES) bindir: mkdir -p bin skipper: $(SOURCES) bindir - GO111MODULE=$(GO111) go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/skipper ./cmd/skipper/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/skipper ./cmd/skipper/*.go eskip: $(SOURCES) bindir - GO111MODULE=$(GO111) go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/eskip ./cmd/eskip/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/eskip ./cmd/eskip/*.go webhook: $(SOURCES) bindir - GO111MODULE=$(GO111) go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/webhook ./cmd/webhook/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/webhook ./cmd/webhook/*.go routesrv: $(SOURCES) bindir - GO111MODULE=$(GO111) go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/routesrv ./cmd/routesrv/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o bin/routesrv ./cmd/routesrv/*.go fixlimits: ifeq (LIMIT_FDS, 256) @@ -40,23 +39,23 @@ endif build: $(SOURCES) lib skipper eskip webhook routesrv build.linux.armv8: - GOOS=linux GOARCH=arm64 CGO_ENABLED=0 GO111MODULE=$(GO111) go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper build.linux.armv7: - GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 GO111MODULE=$(GO111) go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper build.linux: - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=$(GO111) go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper build.osx: - GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=$(GO111) go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper build.windows: - GOOS=windows GOARCH=amd64 CGO_ENABLED=0 GO111MODULE=$(GO111) go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper install: $(SOURCES) - GO111MODULE=$(GO111) go install -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper - GO111MODULE=$(GO111) go install -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/eskip + go install -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + go install -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/eskip check: build check-plugins # go test $(PACKAGES) @@ -64,7 +63,7 @@ check: build check-plugins # due to vendoring and how go test ./... is not the same as go test ./a/... ./b/... # probably can be reverted once etcd is fully mocked away for tests # - for p in $(PACKAGES); do GO111MODULE=$(GO111) go test $$p || break; done + for p in $(PACKAGES); do go test $$p || break; done shortcheck: build check-plugins fixlimits # go test -test.short -run ^Test $(PACKAGES) @@ -72,7 +71,7 @@ shortcheck: build check-plugins fixlimits # due to vendoring and how go test ./... is not the same as go test ./a/... ./b/... # probably can be reverted once etcd is fully mocked away for tests # - for p in $(PACKAGES); do GO111MODULE=$(GO111) go test -test.short -run ^Test $$p || break -1; done + for p in $(PACKAGES); do go test -test.short -run ^Test $$p || break -1; done cicheck: build check-plugins # go test -test.short -run ^Test $(PACKAGES) @@ -80,7 +79,7 @@ cicheck: build check-plugins # due to vendoring and how go test ./... is not the same as go test ./a/... ./b/... # probably can be reverted once etcd is fully mocked away for tests # - for p in $(PACKAGES); do GO111MODULE=$(GO111) go test -tags=redis -test.short -run ^Test $$p || break -1; done + for p in $(PACKAGES); do go test -tags=redis -test.short -run ^Test $$p || break -1; done check-race: build # go test -race -test.short -run ^Test $(PACKAGES) @@ -88,16 +87,16 @@ check-race: build # due to vendoring and how go test ./... is not the same as go test ./a/... ./b/... # probably can be reverted once etcd is fully mocked away for tests # - for p in $(PACKAGES); do GO111MODULE=$(GO111) go test -race -test.short -run ^Test $$p || break -1; done + for p in $(PACKAGES); do go test -race -test.short -run ^Test $$p || break -1; done check-plugins: $(TEST_PLUGINS) - GO111MODULE=$(GO111) go test -run LoadPlugins + go test -run LoadPlugins _test_plugins/%.so: _test_plugins/%.go - GO111MODULE=$(GO111) go build -buildmode=plugin -o $@ $< + go build -buildmode=plugin -o $@ $< _test_plugins_fail/%.so: _test_plugins_fail/%.go - GO111MODULE=$(GO111) go build -buildmode=plugin -o $@ $< + go build -buildmode=plugin -o $@ $< bench: build $(TEST_PLUGINS) # go test -bench . $(PACKAGES) @@ -105,7 +104,7 @@ bench: build $(TEST_PLUGINS) # due to vendoring and how go test ./... is not the same as go test ./a/... ./b/... # probably can be reverted once etcd is fully mocked away for tests # - for p in $(PACKAGES); do GO111MODULE=$(GO111) go test -bench . $$p; done + for p in $(PACKAGES); do go test -bench . $$p; done lint: build staticcheck @@ -123,7 +122,7 @@ deps: @go install github.com/securego/gosec/v2/cmd/gosec@latest vet: $(SOURCES) - GO111MODULE=$(GO111) go vet $(PACKAGES) + go vet $(PACKAGES) # TODO(sszuecs) review disabling these checks, f.e.: # -ST1000 missing package doc in many packages @@ -133,7 +132,7 @@ vet: $(SOURCES) # -ST1021 too many wrong comments on exported functions to fix right away # -ST1022 too many wrong comments on exported functions to fix right away staticcheck: $(SOURCES) - GO111MODULE=$(GO111) staticcheck -checks "all,-ST1000,-ST1003,-ST1012,-ST1020,-ST1021" $(PACKAGES) + staticcheck -checks "all,-ST1000,-ST1003,-ST1012,-ST1020,-ST1021" $(PACKAGES) # TODO(sszuecs) review disabling these checks, f.e.: # G101 find by variable name match "oauth" are not hardcoded credentials @@ -141,7 +140,7 @@ staticcheck: $(SOURCES) # G304 reading kubernetes secret filepaths are not a file inclusions # G402 See https://github.com/securego/gosec/issues/551 and https://github.com/securego/gosec/issues/528 gosec: $(SOURCES) - GO111MODULE=$(GO111) gosec -quiet -exclude="G101,G104,G304,G402" ./... + gosec -quiet -exclude="G101,G104,G304,G402" ./... fmt: $(SOURCES) @gofmt -w -s $(SOURCES) @@ -161,7 +160,7 @@ precommit: fmt build vet staticcheck check-race shortcheck # for p in $(PACKAGES); do \ go list -f \ - '{{if len .TestGoFiles}}"GO111MODULE=on go test -tags=redis -coverprofile={{.Dir}}/.coverprofile {{.ImportPath}}"{{end}}' \ + '{{if len .TestGoFiles}}"go test -tags=redis -coverprofile={{.Dir}}/.coverprofile {{.ImportPath}}"{{end}}' \ $$p | xargs -i sh -c {}; \ done go install github.com/modocache/gover@latest From 05b968e3a3d40aee1c49e842e7de0f92d4bcb0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 14 Jun 2022 02:07:23 +0200 Subject: [PATCH 0035/1047] feature: add xff headers that are required by authelia see also https://github.com/authelia/authelia/issues/3492 (#2013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- config/config.go | 6 ++++++ net/headers.go | 22 +++++++++++++++++----- net/headers_test.go | 46 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/config/config.go b/config/config.go index 40a7103aa9..d3a32745b9 100644 --- a/config/config.go +++ b/config/config.go @@ -397,6 +397,8 @@ func NewConfig() *Config { flag.Var(cfg.ForwardedHeadersList, "forwarded-headers", "comma separated list of headers to add to the incoming request before routing\n"+ "X-Forwarded-For sets or appends with comma the remote IP of the request to the X-Forwarded-For header value\n"+ "X-Forwarded-Host sets X-Forwarded-Host value to the request host\n"+ + "X-Forwarded-Method sets X-Forwarded-Method value to the request method\n"+ + "X-Forwarded-Uri sets X-Forwarded-Uri value to the requestURI\n"+ "X-Forwarded-Port= sets X-Forwarded-Port value\n"+ "X-Forwarded-Proto= sets X-Forwarded-Proto value") flag.Var(cfg.ForwardedHeadersExcludeCIDRList, "forwarded-headers-exclude-cidrs", "disables addition of forwarded headers for the remote host IPs from the comma separated list of CIDRs") @@ -964,6 +966,10 @@ func (c *Config) parseForwardedHeaders() error { c.ForwardedHeaders.For = true case header == "X-Forwarded-Host": c.ForwardedHeaders.Host = true + case header == "X-Forwarded-Method": + c.ForwardedHeaders.Method = true + case header == "X-Forwarded-Uri": + c.ForwardedHeaders.Uri = true case strings.HasPrefix(header, "X-Forwarded-Port="): c.ForwardedHeaders.Port = strings.TrimPrefix(header, "X-Forwarded-Port=") case header == "X-Forwarded-Proto=http": diff --git a/net/headers.go b/net/headers.go index cf83f5aa75..87482e7b4f 100644 --- a/net/headers.go +++ b/net/headers.go @@ -5,15 +5,19 @@ import ( "net/http" ) -// Sets non-standard X-Forwarded-* Headers -// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#proxies +// ForwardedHeaders sets non-standard X-Forwarded-* Headers +// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#proxies and https://github.com/authelia/authelia type ForwardedHeaders struct { - // Sets or appends request remote IP to the X-Forwarded-For header + // For sets or appends request remote IP to the X-Forwarded-For header For bool - // Sets or prepends request remote IP to the X-Forwarded-For header, overrides For + // PrependFor sets or prepends request remote IP to the X-Forwarded-For header, overrides For PrependFor bool - // Sets X-Forwarded-Host to the request host + // Host sets X-Forwarded-Host to the request host Host bool + // Method sets the http method as X-Forwarded-Method to the request header + Method bool + // Uri sets the path and query as X-Forwarded-Uri header to the request header + Uri bool // Sets X-Forwarded-Port value Port string // Sets X-Forwarded-Proto value @@ -42,6 +46,14 @@ func (h *ForwardedHeaders) Set(req *http.Request) { req.Header.Set("X-Forwarded-Host", req.Host) } + if h.Method { + req.Header.Set("X-Forwarded-Method", req.Method) + } + + if h.Uri { + req.Header.Set("X-Forwarded-Uri", req.RequestURI) + } + if h.Port != "" { req.Header.Set("X-Forwarded-Port", h.Port) } diff --git a/net/headers_test.go b/net/headers_test.go index 28e40d0060..738468974e 100644 --- a/net/headers_test.go +++ b/net/headers_test.go @@ -4,12 +4,16 @@ import ( "net/http" "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestForwardedHeaders(t *testing.T) { for _, ti := range []struct { name string remoteAddr string + method string + requestURI string header http.Header forwarded ForwardedHeaders expected http.Header @@ -79,15 +83,35 @@ func TestForwardedHeaders(t *testing.T) { }, }, { - name: "set xff, host, port, proto", + name: "set xff, host, port, proto, method, uri", remoteAddr: "1.2.3.4:56", header: http.Header{}, - forwarded: ForwardedHeaders{For: true, Host: true, Port: "443", Proto: "https"}, + forwarded: ForwardedHeaders{For: true, Host: true, Method: true, Uri: true, Port: "443", Proto: "https"}, + method: "POST", + requestURI: "/foo?bar=baz", expected: http.Header{ - "X-Forwarded-For": []string{"1.2.3.4"}, - "X-Forwarded-Host": []string{"example.com"}, - "X-Forwarded-Port": []string{"443"}, - "X-Forwarded-Proto": []string{"https"}, + "X-Forwarded-For": []string{"1.2.3.4"}, + "X-Forwarded-Host": []string{"example.com"}, + "X-Forwarded-Method": []string{"POST"}, + "X-Forwarded-Uri": []string{"/foo?bar=baz"}, + "X-Forwarded-Port": []string{"443"}, + "X-Forwarded-Proto": []string{"https"}, + }, + }, + { + name: "set xff, host, port, proto, method, uri with non well known port", + remoteAddr: "1.2.3.4:56", + header: http.Header{}, + forwarded: ForwardedHeaders{For: true, Host: true, Method: true, Uri: true, Port: "4444", Proto: "https"}, + method: "POST", + requestURI: "/foo?bar=baz", + expected: http.Header{ + "X-Forwarded-For": []string{"1.2.3.4"}, + "X-Forwarded-Host": []string{"example.com"}, + "X-Forwarded-Method": []string{"POST"}, + "X-Forwarded-Uri": []string{"/foo?bar=baz"}, + "X-Forwarded-Port": []string{"4444"}, + "X-Forwarded-Proto": []string{"https"}, }, }, { @@ -108,12 +132,18 @@ func TestForwardedHeaders(t *testing.T) { }, } { t.Run(ti.name, func(t *testing.T) { - r := &http.Request{Host: "example.com", RemoteAddr: ti.remoteAddr, Header: ti.header} + r := &http.Request{ + Host: "example.com", + RemoteAddr: ti.remoteAddr, + Header: ti.header, + Method: ti.method, + RequestURI: ti.requestURI, + } ti.forwarded.Set(r) if !reflect.DeepEqual(ti.expected, r.Header) { - t.Errorf("header mismatch, expected: %v, got: %v", ti.expected, r.Header) + t.Errorf("header mismatch:\n%v", cmp.Diff(ti.expected, r.Header)) } }) } From dba156771ee00e20029bdf03ba9e934a159d37d7 Mon Sep 17 00:00:00 2001 From: Belmin Bedak Date: Tue, 14 Jun 2022 16:14:24 +0200 Subject: [PATCH 0036/1047] replace strings.Split/SplitN with strings.Cut (#2012) Signed-off-by: Belmin Bedak Co-authored-by: Belmin Bedak --- config/breakerflags.go | 20 ++++---- config/mapflags.go | 9 ++-- config/ratelimiterflags.go | 14 +++--- dataclients/kubernetes/defaultfilters.go | 7 ++- etcd/etcd_test.go | 8 +-- filters/apiusagemonitoring/pathinfo.go | 9 ++-- filters/auth/oidc.go | 8 +-- filters/auth/oidc_introspection.go | 8 +-- filters/auth/oidc_test.go | 4 +- filters/builtin/compress.go | 3 +- go.mod | 2 +- go.sum | 7 --- net/net.go | 2 +- predicates/forwarded/forwarded.go | 11 +++-- proxy/breaker_test.go | 3 +- script/script.go | 7 +-- tracing/tracers/basic/basic.go | 26 +++++----- tracing/tracers/instana/instana.go | 8 +-- tracing/tracers/jaeger/jaeger.go | 62 ++++++++++++------------ tracing/tracers/lightstep/lightstep.go | 56 ++++++++++----------- 20 files changed, 134 insertions(+), 140 deletions(-) diff --git a/config/breakerflags.go b/config/breakerflags.go index 4064a0e1e4..ad8c61a493 100644 --- a/config/breakerflags.go +++ b/config/breakerflags.go @@ -40,14 +40,14 @@ func (b *breakerFlags) Set(value string) error { vs := strings.Split(value, ",") for _, vi := range vs { - kv := strings.Split(vi, "=") - if len(kv) != 2 { + k, v, found := strings.Cut(vi, "=") + if !found { return errInvalidBreakerConfig } - switch kv[0] { + switch k { case "type": - switch kv[1] { + switch v { case "consecutive": s.Type = circuit.ConsecutiveFailures case "rate": @@ -58,37 +58,37 @@ func (b *breakerFlags) Set(value string) error { return errInvalidBreakerConfig } case "host": - s.Host = kv[1] + s.Host = v case "window": - i, err := strconv.Atoi(kv[1]) + i, err := strconv.Atoi(v) if err != nil { return err } s.Window = i case "failures": - i, err := strconv.Atoi(kv[1]) + i, err := strconv.Atoi(v) if err != nil { return err } s.Failures = i case "timeout": - d, err := time.ParseDuration(kv[1]) + d, err := time.ParseDuration(v) if err != nil { return err } s.Timeout = d case "half-open-requests": - i, err := strconv.Atoi(kv[1]) + i, err := strconv.Atoi(v) if err != nil { return err } s.HalfOpenRequests = i case "idle-ttl": - d, err := time.ParseDuration(kv[1]) + d, err := time.ParseDuration(v) if err != nil { return err } diff --git a/config/mapflags.go b/config/mapflags.go index 30b4e35bd8..48132ca058 100644 --- a/config/mapflags.go +++ b/config/mapflags.go @@ -38,14 +38,13 @@ func (m *mapFlags) Set(value string) error { vs := strings.Split(value, ",") for _, vi := range vs { - kv := strings.SplitN(vi, "=", 2) - - if len(kv) != 2 { + k, v, found := strings.Cut(vi, "=") + if !found { return fmt.Errorf(formatErrorString, vi) } - k := strings.TrimSpace(kv[0]) - v := strings.TrimSpace(kv[1]) + k = strings.TrimSpace(k) + v = strings.TrimSpace(v) if k == "" || v == "" { return fmt.Errorf(formatErrorString, vi) diff --git a/config/ratelimiterflags.go b/config/ratelimiterflags.go index 700543cffc..2b4be9f6f5 100644 --- a/config/ratelimiterflags.go +++ b/config/ratelimiterflags.go @@ -39,14 +39,14 @@ func (r *ratelimitFlags) Set(value string) error { vs := strings.Split(value, ",") for _, vi := range vs { - kv := strings.Split(vi, "=") - if len(kv) != 2 { + k, v, found := strings.Cut(vi, "=") + if !found { return errInvalidRatelimitConfig } - switch kv[0] { + switch k { case "type": - switch kv[1] { + switch v { case "local": log.Warning("LocalRatelimit is deprecated, please use ClientRatelimit instead") fallthrough @@ -64,20 +64,20 @@ func (r *ratelimitFlags) Set(value string) error { return errInvalidRatelimitConfig } case "max-hits": - i, err := strconv.Atoi(kv[1]) + i, err := strconv.Atoi(v) if err != nil { return err } s.MaxHits = i case "time-window": - d, err := time.ParseDuration(kv[1]) + d, err := time.ParseDuration(v) if err != nil { return err } s.TimeWindow = d s.CleanInterval = d * 10 case "group": - s.Group = kv[1] + s.Group = v default: return errInvalidRatelimitConfig } diff --git a/dataclients/kubernetes/defaultfilters.go b/dataclients/kubernetes/defaultfilters.go index 771f011472..2ea5ac9be1 100644 --- a/dataclients/kubernetes/defaultfilters.go +++ b/dataclients/kubernetes/defaultfilters.go @@ -30,8 +30,13 @@ func readDefaultFilters(dir string) (defaultFilters, error) { filters := make(defaultFilters) for _, f := range files { r := strings.Split(f.Name(), ".") // format: {service}.{namespace} + if len(r) != 2 { + log.WithField("file", f.Name()).Debug("malformed file name") + continue + } + info, err := f.Info() - if err != nil || len(r) != 2 || !(f.Type().IsRegular() || f.Type()&os.ModeSymlink != 0) || info.Size() > maxFileSize { + if err != nil || !(f.Type().IsRegular() || f.Type()&os.ModeSymlink != 0) || info.Size() > maxFileSize { log.WithError(err).WithField("file", f.Name()).Debug("incompatible file") continue } diff --git a/etcd/etcd_test.go b/etcd/etcd_test.go index 5402ec7e58..b922c0b38d 100644 --- a/etcd/etcd_test.go +++ b/etcd/etcd_test.go @@ -592,20 +592,20 @@ func TestRequestWithBasicAuth(t *testing.T) { t.Fatal(err) } - parts := strings.Split(authHeader, " ") - if len(parts) != 2 { + k, v, found := strings.Cut(authHeader, " ") + if !found || k == "" || v == "" { t.Error("invalid auth header sent") t.Log(authHeader) return } - if parts[0] != "Basic" { + if k != "Basic" { t.Error("invalid auth header sent") t.Log(authHeader) return } - decodedArr, err := base64.StdEncoding.DecodeString(parts[1]) + decodedArr, err := base64.StdEncoding.DecodeString(v) if err != nil { t.Error(err) t.Log("header sent:", authHeader) diff --git a/filters/apiusagemonitoring/pathinfo.go b/filters/apiusagemonitoring/pathinfo.go index 4c0c997285..dda74cebc2 100644 --- a/filters/apiusagemonitoring/pathinfo.go +++ b/filters/apiusagemonitoring/pathinfo.go @@ -23,11 +23,10 @@ type pathInfo struct { } func newPathInfo(applicationId, tag, apiId, pathTemplate string, clientTracking *clientTrackingInfo) *pathInfo { - if strings.Contains(applicationId, ":") { - //can be removed after feature toggle is enabled on monitoring controller - split := strings.Split(applicationId, ":") - applicationId = split[0] - tag = split[1] + id, t, found := strings.Cut(applicationId, ":") + if found { + applicationId = id + tag = t } if tag == "" { diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index 50b60e30d8..d11828be7a 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -290,11 +290,11 @@ func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) f.upstreamHeaders = make(map[string]string) for _, header := range strings.Split(sargs[paramUpstrHeaders], " ") { - sl := strings.SplitN(header, ":", 2) - if len(sl) != 2 || sl[0] == "" || sl[1] == "" { - return nil, fmt.Errorf("%w: malformatted filter for upstream headers %s", filters.ErrInvalidFilterParameters, sl) + k, v, found := strings.Cut(header, ":") + if !found || k == "" || v == "" { + return nil, fmt.Errorf("%w: malformatted filter for upstream headers %s", filters.ErrInvalidFilterParameters, header) } - f.upstreamHeaders[sl[0]] = sl[1] + f.upstreamHeaders[k] = v } log.Debugf("Upstream Headers: %v", f.upstreamHeaders) } diff --git a/filters/auth/oidc_introspection.go b/filters/auth/oidc_introspection.go index cb919a8754..5988e2ba8c 100644 --- a/filters/auth/oidc_introspection.go +++ b/filters/auth/oidc_introspection.go @@ -64,12 +64,12 @@ func (spec *oidcIntrospectionSpec) CreateFilter(args []interface{}) (filters.Fil switch filter.typ { case checkOIDCQueryClaims: for _, arg := range sargs { - slice := strings.SplitN(arg, ":", 2) - if len(slice) != 2 { + path, queries, found := strings.Cut(arg, ":") + if !found || path == "" { return nil, fmt.Errorf("%v: malformatted filter arg %s", filters.ErrInvalidFilterParameters, arg) } - pq := pathQuery{path: slice[0]} - for _, query := range splitQueries(slice[1]) { + pq := pathQuery{path: path} + for _, query := range splitQueries(queries) { if query == "" { return nil, fmt.Errorf("%v: %s", errUnsupportedClaimSpecified, arg) } diff --git a/filters/auth/oidc_test.go b/filters/auth/oidc_test.go index 69d29c8d00..aba2f155d5 100644 --- a/filters/auth/oidc_test.go +++ b/filters/auth/oidc_test.go @@ -868,8 +868,8 @@ func TestOIDCSetup(t *testing.T) { if tc.queries != nil { q := reqURL.Query() for _, rq := range tc.queries { - splitRQ := strings.Split(rq, "=") - q.Add(splitRQ[0], splitRQ[1]) + k, v, _ := strings.Cut(rq, "=") + q.Add(k, v) } reqURL.RawQuery = q.Encode() } diff --git a/filters/builtin/compress.go b/filters/builtin/compress.go index 2c01ec5edb..6ab4aeae90 100644 --- a/filters/builtin/compress.go +++ b/filters/builtin/compress.go @@ -4,7 +4,6 @@ import ( "compress/flate" "compress/gzip" "errors" - "github.com/andybalholm/brotli" "io" "math" "net/http" @@ -13,6 +12,8 @@ import ( "strings" "sync" + "github.com/andybalholm/brotli" + log "github.com/sirupsen/logrus" "github.com/zalando/skipper/filters" ) diff --git a/go.mod b/go.mod index 4ed0ed42f2..5a0b0abe8a 100644 --- a/go.mod +++ b/go.mod @@ -129,4 +129,4 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect ) -go 1.17 +go 1.18 diff --git a/go.sum b/go.sum index 8a0503dae4..dff55d1b66 100644 --- a/go.sum +++ b/go.sum @@ -133,7 +133,6 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3k github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -862,7 +861,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= @@ -998,9 +996,7 @@ golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1103,11 +1099,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA= @@ -1376,7 +1370,6 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8 k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= diff --git a/net/net.go b/net/net.go index 009e1a36d6..7af882a810 100644 --- a/net/net.go +++ b/net/net.go @@ -33,7 +33,7 @@ func parse(addr string) net.IP { // X-Forwarded-For: client, proxy1, proxy2 func RemoteHost(r *http.Request) net.IP { ffs := r.Header.Get("X-Forwarded-For") - ff := strings.Split(ffs, ",")[0] + ff, _, _ := strings.Cut(ffs, ",") if ffh := parse(ff); ffh != nil { return ffh } diff --git a/predicates/forwarded/forwarded.go b/predicates/forwarded/forwarded.go index 5abecf0ff0..796e3c0f8e 100644 --- a/predicates/forwarded/forwarded.go +++ b/predicates/forwarded/forwarded.go @@ -19,11 +19,12 @@ Examples: package forwarded import ( - "github.com/zalando/skipper/predicates" - "github.com/zalando/skipper/routing" "net/http" "regexp" "strings" + + "github.com/zalando/skipper/predicates" + "github.com/zalando/skipper/routing" ) const ( @@ -134,9 +135,9 @@ func parseForwarded(fh string) *forwarded { for _, forwardedFull := range strings.Split(fh, ",") { for _, forwardedPair := range strings.Split(strings.TrimSpace(forwardedFull), ";") { - if tv := strings.SplitN(forwardedPair, "=", 2); len(tv) == 2 { - token, value := tv[0], tv[1] - value = strings.Trim(value, `"`) + token, value, found := strings.Cut(forwardedPair, "=") + value = strings.Trim(value, `"`) + if found && value != "" { switch token { case "proto": f.proto = value diff --git a/proxy/breaker_test.go b/proxy/breaker_test.go index 7e2669289d..f403b15bef 100644 --- a/proxy/breaker_test.go +++ b/proxy/breaker_test.go @@ -44,7 +44,8 @@ const ( ) func urlHostNerr(u string) string { - return strings.Split(u, "//")[1] + _, v, _ := strings.Cut(u, "//") + return v } func newBreakerProxy( diff --git a/script/script.go b/script/script.go index bfa48008a8..b0e14f96e3 100644 --- a/script/script.go +++ b/script/script.go @@ -216,11 +216,8 @@ func (s *script) runFunc(name string, f filters.FilterContext) { pt := L.CreateTable(len(s.routeParams), len(s.routeParams)) for i, p := range s.routeParams { - parts := strings.SplitN(p, "=", 2) - if len(parts) == 1 { - parts = append(parts, "") - } - pt.RawSetString(parts[0], lua.LString(parts[1])) + k, v, _ := strings.Cut(p, "=") + pt.RawSetString(k, lua.LString(v)) pt.RawSetInt(i+1, lua.LString(p)) } diff --git a/tracing/tracers/basic/basic.go b/tracing/tracers/basic/basic.go index 036e4789ce..e5a9f15f45 100644 --- a/tracing/tracers/basic/basic.go +++ b/tracing/tracers/basic/basic.go @@ -21,34 +21,34 @@ func InitTracer(opts []string) (opentracing.Tracer, error) { ) for _, o := range opts { - parts := strings.SplitN(o, "=", 2) - switch parts[0] { + k, v, _ := strings.Cut(o, "=") + switch k { case "drop-all-logs": dropAllLogs = true case "sample-modulo": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - sampleModulo, err = strconv.ParseUint(parts[1], 10, 64) + sampleModulo, err = strconv.ParseUint(v, 10, 64) if err != nil { - return nil, invalidArg(parts[0], err) + return nil, invalidArg(k, err) } case "max-logs-per-span": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - maxLogsPerSpan, err = strconv.Atoi(parts[1]) + maxLogsPerSpan, err = strconv.Atoi(v) if err != nil { - return nil, invalidArg(parts[0], err) + return nil, invalidArg(k, err) } case "recorder": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - switch parts[1] { + switch v { case "in-memory": recorder = basic.NewInMemoryRecorder() default: diff --git a/tracing/tracers/instana/instana.go b/tracing/tracers/instana/instana.go index 9952e95b20..9d4b892a8f 100644 --- a/tracing/tracers/instana/instana.go +++ b/tracing/tracers/instana/instana.go @@ -15,11 +15,11 @@ func InitTracer(opts []string) (opentracing.Tracer, error) { serviceName := defServiceName for _, o := range opts { - parts := strings.SplitN(o, "=", 2) - switch parts[0] { + k, v, _ := strings.Cut(o, "=") + switch k { case "service-name": - if len(parts) > 1 { - serviceName = parts[1] + if v != "" { + serviceName = v } } } diff --git a/tracing/tracers/jaeger/jaeger.go b/tracing/tracers/jaeger/jaeger.go index f0defeda23..199fd6ee90 100644 --- a/tracing/tracers/jaeger/jaeger.go +++ b/tracing/tracers/jaeger/jaeger.go @@ -29,69 +29,67 @@ func parseOptions(opts []string) (*config.Configuration, error) { var globalTags []opentracing.Tag for _, o := range opts { - parts := strings.SplitN(o, "=", 2) - switch parts[0] { + k, v, _ := strings.Cut(o, "=") + switch k { case "service-name": - if len(parts) > 1 { - serviceName = parts[1] + if v != "" { + serviceName = v } case "use-rpc-metrics": useRPCMetrics = true case "sampler-type": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - samplerValue := parts[1] - parts = strings.SplitN(samplerValue, ":", 2) - samplerType = parts[0] + samplerType, v, _ = strings.Cut(v, ":") switch samplerType { case "const": samplerParam = 1.0 case "probabilistic", "rateLimiting", "remote": - if len(parts) == 1 { - return nil, missingArg(parts[1]) + if v == "" { + return nil, missingArg(k) } - samplerParam, err = strconv.ParseFloat(parts[1], 64) + samplerParam, err = strconv.ParseFloat(v, 64) if err != nil { - return nil, invalidArg(parts[1], err) + return nil, invalidArg(v, err) } default: - return nil, invalidArg(parts[0], errors.New("invalid sampler type")) + return nil, invalidArg(k, errors.New("invalid sampler type")) } case "sampler-url": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - samplerURL = parts[1] + samplerURL = v case "reporter-queue": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - reporterQueue, _ = strconv.Atoi(parts[1]) + reporterQueue, _ = strconv.Atoi(v) case "reporter-interval": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - reporterInterval, err = time.ParseDuration(parts[1]) + reporterInterval, err = time.ParseDuration(v) if err != nil { - return nil, invalidArg(parts[1], err) + return nil, invalidArg(v, err) } case "local-agent": - if len(parts) == 1 { - return nil, missingArg(parts[0]) + if v == "" { + return nil, missingArg(k) } - localAgent = parts[1] + localAgent = v case "tag": - if len(parts) > 1 { - kv := strings.SplitN(parts[1], "=", 2) - if len(kv) != 2 { - return nil, fmt.Errorf("missing value for tag %s", kv[0]) + if v != "" { + k, v, _ := strings.Cut(v, "=") + if v == "" { + return nil, fmt.Errorf("missing value for tag %s", k) } - globalTags = append(globalTags, opentracing.Tag{Key: kv[0], Value: kv[1]}) + globalTags = append(globalTags, opentracing.Tag{Key: k, Value: v}) } } } diff --git a/tracing/tracers/lightstep/lightstep.go b/tracing/tracers/lightstep/lightstep.go index c6fcd3eb7d..f7e2e1b958 100644 --- a/tracing/tracers/lightstep/lightstep.go +++ b/tracing/tracers/lightstep/lightstep.go @@ -47,45 +47,45 @@ func parseOptions(opts []string) (lightstep.Options, error) { propagators[opentracing.HTTPHeaders] = defPropagator for _, o := range opts { - parts := strings.SplitN(o, "=", 2) - switch parts[0] { + key, val, _ := strings.Cut(o, "=") + switch key { case "component-name": - if len(parts) > 1 { - componentName = parts[1] + if val != "" { + componentName = val } case "token": - token = parts[1] + token = val case "grpc-max-msg-size": - v, err := strconv.Atoi(parts[1]) + v, err := strconv.Atoi(val) if err != nil { - return lightstep.Options{}, fmt.Errorf("failed to parse %s as int grpc-max-msg-size: %v", parts[1], err) + return lightstep.Options{}, fmt.Errorf("failed to parse %s as int grpc-max-msg-size: %v", val, err) } grpcMaxMsgSize = v case "min-period": - v, err := time.ParseDuration(parts[1]) + v, err := time.ParseDuration(val) if err != nil { - return lightstep.Options{}, fmt.Errorf("failed to parse %s as time.Duration min-period : %v", parts[1], err) + return lightstep.Options{}, fmt.Errorf("failed to parse %s as time.Duration min-period : %v", val, err) } minReportingPeriod = v case "max-period": - v, err := time.ParseDuration(parts[1]) + v, err := time.ParseDuration(val) if err != nil { - return lightstep.Options{}, fmt.Errorf("failed to parse %s as time.Duration max-period: %v", parts[1], err) + return lightstep.Options{}, fmt.Errorf("failed to parse %s as time.Duration max-period: %v", val, err) } maxReportingPeriod = v case "tag": - if len(parts) > 1 { - tags := strings.SplitN(parts[1], "=", 2) - if len(tags) != 2 { - return lightstep.Options{}, fmt.Errorf("missing value for tag %s", tags[0]) + if val != "" { + tag, tagVal, found := strings.Cut(val, "=") + if !found { + return lightstep.Options{}, fmt.Errorf("missing value for tag %s", val) } - globalTags[tags[0]] = tags[1] + globalTags[tag] = tagVal } case "collector": var err error var sport string - host, sport, err = net.SplitHostPort(parts[1]) + host, sport, err = net.SplitHostPort(val) if err != nil { return lightstep.Options{}, err } @@ -96,48 +96,48 @@ func parseOptions(opts []string) (lightstep.Options, error) { } case "plaintext": var err error - plaintext, err = strconv.ParseBool(parts[1]) + plaintext, err = strconv.ParseBool(val) if err != nil { - return lightstep.Options{}, fmt.Errorf("failed to parse %s as bool: %v", parts[1], err) + return lightstep.Options{}, fmt.Errorf("failed to parse %s as bool: %v", val, err) } case "cmd-line": - cmdLine = parts[1] + cmdLine = val logCmdLine = true case "protocol": - switch parts[1] { + switch val { case "http": useGRPC = false case "grpc": useGRPC = true default: - return lightstep.Options{}, fmt.Errorf("failed to parse protocol allowed 'http' or 'grpc', got: %s", parts[1]) + return lightstep.Options{}, fmt.Errorf("failed to parse protocol allowed 'http' or 'grpc', got: %s", val) } case "log-events": logEvents = true case "max-buffered-spans": var err error - if maxBufferedSpans, err = strconv.Atoi(parts[1]); err != nil { + if maxBufferedSpans, err = strconv.Atoi(val); err != nil { return lightstep.Options{}, fmt.Errorf("failed to parse max buffered spans: %v", err) } case "max-log-key-len": var err error - if maxLogKeyLen, err = strconv.Atoi(parts[1]); err != nil { + if maxLogKeyLen, err = strconv.Atoi(val); err != nil { return lightstep.Options{}, fmt.Errorf("failed to parse max log key length: %v", err) } case "max-log-value-len": var err error - if maxLogValueLen, err = strconv.Atoi(parts[1]); err != nil { + if maxLogValueLen, err = strconv.Atoi(val); err != nil { return lightstep.Options{}, fmt.Errorf("failed to parse max log value length: %v", err) } case "max-logs-per-span": var err error - if maxLogsPerSpan, err = strconv.Atoi(parts[1]); err != nil { + if maxLogsPerSpan, err = strconv.Atoi(val); err != nil { return lightstep.Options{}, fmt.Errorf("failed to parse max logs per span: %v", err) } case "propagators": - if len(parts) > 1 { + if val != "" { prStack := lightstep.PropagatorStack{} - prs := strings.SplitN(parts[1], ",", 2) + prs := strings.SplitN(val, ",", 2) for _, pr := range prs { switch pr { case "lightstep", "ls": From ebe4ee2b0218d4ea38b65842052e5f53255932c4 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 15 Jun 2022 12:29:51 +0200 Subject: [PATCH 0037/1047] gosec: configure http.Server timeouts to fix G112 (#2018) Signed-off-by: Alexander Yastrebov --- cmd/webhook/main.go | 7 +++++-- routesrv/routesrv.go | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index c1faf6348f..c7f03b98e2 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -5,6 +5,7 @@ import ( "os" "os/signal" "syscall" + "time" "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" @@ -73,8 +74,10 @@ func healthCheck(writer http.ResponseWriter, _ *http.Request) { func serve(cfg *config, handler http.Handler) { server := &http.Server{ - Addr: cfg.address, - Handler: handler, + Addr: cfg.address, + Handler: handler, + ReadTimeout: 1 * time.Minute, + ReadHeaderTimeout: 1 * time.Minute, } log.Infof("Starting server on %s", cfg.address) diff --git a/routesrv/routesrv.go b/routesrv/routesrv.go index 43d18be900..bf0b0f9b61 100644 --- a/routesrv/routesrv.go +++ b/routesrv/routesrv.go @@ -45,7 +45,13 @@ func New(opts Options) (*RouteServer, error) { handler.Handle("/health", bs) handler.Handle("/routes", b) handler.Handle("/metrics", promhttp.Handler()) - rs.server = &http.Server{Addr: opts.Address, Handler: handler} + + rs.server = &http.Server{ + Addr: opts.Address, + Handler: handler, + ReadTimeout: 1 * time.Minute, + ReadHeaderTimeout: 1 * time.Minute, + } dataclient, err := kubernetes.New(kubernetes.Options{ AllowedExternalNames: opts.KubernetesAllowedExternalNames, From ca1f491e706dc4c740ff5217c921459c3b23c2c4 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 15 Jun 2022 22:34:37 +0200 Subject: [PATCH 0038/1047] pathmux: use byte-oriented common prefix length calculation (#2017) Fixes #2016 Signed-off-by: Alexander Yastrebov --- pathmux/tree.go | 24 ++++++++++++------------ pathmux/tree_test.go | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/pathmux/tree.go b/pathmux/tree.go index a3633feef5..46ef5a3fd4 100644 --- a/pathmux/tree.go +++ b/pathmux/tree.go @@ -120,7 +120,7 @@ func (n *node) addPath(path string) (*node, error) { return nil, fmt.Errorf("* or : in middle of path component %s", path) } - // Do we have an existing node that starts with the same letter? + // Do we have an existing node that starts with the same byte? for i, index := range n.staticIndices { if c == index { // Yes. Split it based on the common prefix of the existing @@ -132,7 +132,7 @@ func (n *node) addPath(path string) (*node, error) { } } - // No existing node starting with this letter, so create it. + // No existing node starting with this byte, so create it. child := &node{path: thisToken} if n.staticIndices == nil { @@ -158,16 +158,8 @@ func (n *node) splitCommonPrefix(existingNodeIndex int, path string) (*node, int return childNode, len(childNode.path) } - var i int // Find the length of the common prefix of the child node and the new path. - for i = range childNode.path { - if i == len(path) { - break - } - if path[i] != childNode.path[i] { - break - } - } + i := commonPrefixLen(childNode.path, path) commonPrefix := path[0:i] childNode.path = childNode.path[i:] @@ -177,7 +169,7 @@ func (n *node) splitCommonPrefix(existingNodeIndex int, path string) (*node, int newNode := &node{ path: commonPrefix, priority: childNode.priority, - // Index is the first letter of the non-common part of the path. + // Index is the first byte of the non-common part of the path. staticIndices: []byte{childNode.path[0]}, staticChild: []*node{childNode}, } @@ -186,6 +178,14 @@ func (n *node) splitCommonPrefix(existingNodeIndex int, path string) (*node, int return newNode, i } +func commonPrefixLen(x, y string) int { + n := 0 + for n < len(x) && n < len(y) && x[n] == y[n] { + n++ + } + return n +} + func (n *node) search(path string, m Matcher) (found *node, params []string, value interface{}) { pathLen := len(path) if pathLen == 0 { diff --git a/pathmux/tree_test.go b/pathmux/tree_test.go index 046b52a247..3594b63937 100644 --- a/pathmux/tree_test.go +++ b/pathmux/tree_test.go @@ -207,6 +207,20 @@ func TestTree(t *testing.T) { } +func TestTreeKatakana(t *testing.T) { + const ( + ma = "/マ" + ka = "/カ" + ) + tree := &node{path: "/"} + + addPath(t, tree, ma) + addPath(t, tree, ka) + + testPath(t, tree, ma, ma, nil) + testPath(t, tree, ka, ka, nil) +} + func TestPanics(t *testing.T) { sawPanic := false From 8670e7d7d90aa17cd69ed141e2807b40d8110b8a Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Fri, 17 Jun 2022 10:57:20 +0200 Subject: [PATCH 0039/1047] Add instructions to build multi-arch container image (#2019) * add instructions to build multi-arch container image for skipper and tools Signed-off-by: Martin Linkhorst * add custom multi-arch builder for CDP pipeline Signed-off-by: Martin Linkhorst * inline multi-arch build with current build commands Signed-off-by: Martin Linkhorst --- Makefile | 7 +- delivery.yaml | 10 ++- docs/tutorials/basics.md | 2 +- packaging/Dockerfile | 10 ++- packaging/Dockerfile.arm64 | 10 +-- .../{Dockerfile.arm => Dockerfile.armv7} | 10 +-- packaging/Makefile | 76 +++++++++++++------ 7 files changed, 86 insertions(+), 39 deletions(-) rename packaging/{Dockerfile.arm => Dockerfile.armv7} (63%) diff --git a/Makefile b/Makefile index 46832e3206..8a49633281 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ endif build: $(SOURCES) lib skipper eskip webhook routesrv -build.linux.armv8: +build.linux.arm64: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper build.linux.armv7: @@ -47,7 +47,10 @@ build.linux.armv7: build.linux: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper -build.osx: +build.darwin.arm64: + GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + +build.darwin: GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper build.windows: diff --git a/delivery.yaml b/delivery.yaml index 9e7ccca14c..15d134b3ac 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -9,6 +9,7 @@ pipeline: - desc: build-push cmd: | IMAGE_REGISTRY="registry-write.opensource.zalan.do" + MULTIARCH_REGISTRY="container-registry-test.zalando.net" if [[ $CDP_TARGET_BRANCH == master && ! $CDP_PULL_REQUEST_NUMBER ]]; then LATEST_VERSION=$(git describe --tags --always | awk -F \- '{print $1}') CUR_PART=$(echo $LATEST_VERSION | awk -F . '{print $1"."$2}') @@ -21,19 +22,23 @@ pipeline: IMAGE="${IMAGE_REGISTRY}/teapot/skipper:${RELEASE_VERSION}" ARM_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-armv7:${RELEASE_VERSION}" ARM64_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-arm64:${RELEASE_VERSION}" + MULTIARCH_IMAGE="${MULTIARCH_REGISTRY}/teapot/skipper:${RELEASE_VERSION}" else IMAGE="${IMAGE_REGISTRY}/teapot/skipper-test:${CDP_BUILD_VERSION}" ARM_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-armv7-test:${CDP_BUILD_VERSION}" ARM64_IMAGE="${IMAGE_REGISTRY}/teapot/skipper-arm64-test:${CDP_BUILD_VERSION}" + MULTIARCH_IMAGE="${MULTIARCH_REGISTRY}/teapot/skipper-test:${CDP_BUILD_VERSION}" fi - export IMAGE ARM_IMAGE ARM64_IMAGE + export IMAGE ARM_IMAGE ARM64_IMAGE MULTIARCH_IMAGE make deps cicheck staticcheck gosec git status git diff - cd packaging && make docker.build.amd64 && git status && git diff && make docker.push.amd64 + cd packaging && make docker.build.amd64 && git status && git diff && make docker.push.amd64 && make docker.push.multiarch if [[ $CDP_TARGET_BRANCH == master && ! $CDP_PULL_REQUEST_NUMBER ]]; then echo "Created docker image registry.opensource.zalan.do/teapot/skipper:${RELEASE_VERSION}" + cdp-promote-image "${MULTIARCH_IMAGE}" + echo "Created multi-arch docker image container-registry.zalando.net/teapot/skipper:${RELEASE_VERSION}" echo "Creating docker image registry.opensource.zalan.do/teapot/skipper-arm64:${RELEASE_VERSION}" make docker.build.arm64 && git status && git diff && make docker.push.arm64 echo "Creating docker image registry.opensource.zalan.do/teapot/skipper-armv7:${RELEASE_VERSION}" @@ -62,6 +67,7 @@ pipeline: git gh-release --message-from-file "${tf}" "${files[@]}" "$RELEASE_VERSION" else echo "Created docker image registry.opensource.zalan.do/teapot/skipper-test:${CDP_BUILD_VERSION}" + echo "Created multi-arch docker image container-registry-test.zalando.net/teapot/skipper-test:${CDP_BUILD_VERSION}" echo "Not creating a release. No release version defined." fi - id: docs diff --git a/docs/tutorials/basics.md b/docs/tutorials/basics.md index f79e110faf..e2741fba0e 100644 --- a/docs/tutorials/basics.md +++ b/docs/tutorials/basics.md @@ -244,7 +244,7 @@ We use Go modules to build skipper, therefore you need [Go](https://golang.org/d To get a local build of skipper for your CPU architecture, you can run `make skipper`. To cross compile to non Linux platforms you can use: -- `make build.osx` for Mac OS X (amd64) +- `make build.darwin` for Mac OS X (amd64) - `make build.windows` for Windows (amd64) The local build will write into `./bin/` directory. diff --git a/packaging/Dockerfile b/packaging/Dockerfile index 7ec15f8f81..97a0e91dd4 100644 --- a/packaging/Dockerfile +++ b/packaging/Dockerfile @@ -1,8 +1,14 @@ -FROM registry.opensource.zalan.do/library/alpine-3.13:latest +ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3.13:latest +FROM ${BASE_IMAGE} LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates RUN mkdir -p /usr/bin -ADD skipper eskip webhook routesrv /usr/bin/ +ARG BUILD_FOLDER=build +ARG TARGETPLATFORM +ADD ${BUILD_FOLDER}/${TARGETPLATFORM}/skipper \ + ${BUILD_FOLDER}/${TARGETPLATFORM}/eskip \ + ${BUILD_FOLDER}/${TARGETPLATFORM}/webhook \ + ${BUILD_FOLDER}/${TARGETPLATFORM}/routesrv /usr/bin/ ENV PATH $PATH:/usr/bin EXPOSE 9090 9911 diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 55ea266aa0..8c07f6a265 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,10 +1,10 @@ -FROM --platform=linux/amd64 alpine:3.13 AS build +FROM --platform=linux/arm64 alpine:3.13 LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates - -FROM --platform=linux/arm64 alpine:3.13 -COPY --from=build /etc/ssl/certs/ /etc/ssl/certs/ -ADD build/linux_arm8/skipper /usr/bin/ +ADD build/linux/arm64/skipper \ + build/linux/arm64/eskip \ + build/linux/arm64/webhook \ + build/linux/arm64/routesrv /usr/bin/ ENV PATH $PATH:/usr/bin EXPOSE 9090 9911 diff --git a/packaging/Dockerfile.arm b/packaging/Dockerfile.armv7 similarity index 63% rename from packaging/Dockerfile.arm rename to packaging/Dockerfile.armv7 index 54a8fad4ad..a08511b8d1 100644 --- a/packaging/Dockerfile.arm +++ b/packaging/Dockerfile.armv7 @@ -1,10 +1,10 @@ -FROM --platform=linux/amd64 alpine:3.13 AS build +FROM --platform=linux/arm/v7 alpine:3.13 LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates - -FROM --platform=linux/arm/v7 alpine:3.13 -COPY --from=build /etc/ssl/certs/ /etc/ssl/certs/ -ADD build/linux_arm7/skipper /usr/bin/ +ADD build/linux/arm/v7/skipper \ + build/linux/arm/v7/eskip \ + build/linux/arm/v7/webhook \ + build/linux/arm/v7/routesrv /usr/bin/ ENV PATH $PATH:/usr/bin EXPOSE 9090 9911 diff --git a/packaging/Makefile b/packaging/Makefile index 944de84565..3c3d809881 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -2,6 +2,7 @@ VERSION ?= $(shell git rev-parse HEAD) REGISTRY ?= registry-write.opensource.zalan.do/teapot +BINARIES ?= skipper webhook eskip routesrv IMAGE ?= $(REGISTRY)/skipper:$(VERSION) ARM64_IMAGE ?= $(REGISTRY)/skipper-arm64:$(VERSION) ARM_IMAGE ?= $(REGISTRY)/skipper-armv7:$(VERSION) @@ -34,15 +35,18 @@ routesrv: GO111MODULE=$(GO111) GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build -o routesrv ../cmd/routesrv/*.go clean: - rm -rf skipper eskip webhook routesrv build/ + rm -rf $(BINARIES) build/ + +docker.build: clean $(BINARIES) + docker build -t $(IMAGE) --build-arg BUILD_FOLDER=. --build-arg TARGETPLATFORM= . docker-build: docker.build.amd64 docker.build.arm64 docker.build.armv7 -docker.build.amd64: clean skipper eskip webhook routesrv - docker build -t $(IMAGE) . +docker.build.amd64: clean build.linux.amd64 docker.build.enable + docker buildx build -t $(IMAGE) --platform linux/amd64 -f Dockerfile . docker.build.arm64: clean build.linux.arm64 docker.build.enable docker buildx build -t $(ARM64_IMAGE) --platform linux/arm64 -f Dockerfile.arm64 . docker.build.armv7: clean build.linux.armv7 docker.build.enable - docker buildx build -t $(ARM_IMAGE) --platform linux/arm/v7 -f Dockerfile.arm . + docker buildx build -t $(ARM_IMAGE) --platform linux/arm/v7 -f Dockerfile.armv7 . docker-push: docker.push.amd64 docker.push.arm64 docker.push.armv7 docker.push.amd64: @@ -52,6 +56,17 @@ docker.push.arm64: docker.build.arm64 docker.push.armv7: docker.build.armv7 docker push $(ARM_IMAGE) +docker.push.multiarch: clean build.linux docker.build.enable + # Install qemu interpreter for arm64 (https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images) + docker run --rm --privileged container-registry.zalando.net/teapot/tonistiigi-binfmt:qemu-v6.2.0-main-2 --install arm64 + + # create a Buildkit builder with CDP specific configuration (https://cloud.docs.zalando.net/howtos/cdp-multiarch/) + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use + + # build multi-arch container image using a trusted multi-arch base image + docker buildx build --rm -t $(MULTIARCH_IMAGE) --platform linux/amd64,linux/arm64 --push \ + --build-arg BASE_IMAGE=container-registry.zalando.net/library/alpine-3.13:latest . + # https://docs.docker.com/build/working-with-build/ # ~/.docker/config.json add: "experimental": "enabled", docker.build.enable: @@ -60,48 +75,65 @@ docker.build.enable: [ -f $$HOME/.docker/config.json ] || touch $$HOME/.docker/config.json if [ -s $$HOME/.docker/config.json ]; then jq -r '. += {experimental: "enabled"}' $$HOME/.docker/config.json > $$HOME/.docker/config.json.new; mv $$HOME/.docker/config.json.new $$HOME/.docker/config.json; else echo '{"experimental": "enabled"}' >$$HOME/.docker/config.json; fi -build.linux: $(SOURCES) build.linux.amd64 build.linux.arm64 build.linux.armv7 +build.linux: build.linux.amd64 build.linux.arm64 build.linux.armv7 +build.linux.amd64: $(addprefix build/linux/amd64/,$(BINARIES)) +build.linux.arm64: $(addprefix build/linux/arm64/,$(BINARIES)) +build.linux.armv7: $(addprefix build/linux/arm/v7/,$(BINARIES)) + +build.darwin: build.darwin.amd64 build.darwin.arm64 +build.darwin.amd64: $(addprefix build/darwin/amd64/,$(BINARIES)) +build.darwin.arm64: $(addprefix build/darwin/arm64/,$(BINARIES)) + +build.windows: $(addprefix build/windows/amd64/,$(BINARIES)) -build.linux.amd64: +build/linux/amd64/%: GO111MODULE=$(GO111) \ GOOS=linux \ GOARCH=amd64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/skipper ../cmd/skipper/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go -build.linux.arm64: +build/linux/arm64/%: GO111MODULE=$(GO111) \ GOOS=linux \ GOARCH=arm64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux_arm8/skipper ../cmd/skipper/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/arm64/$(notdir $@) ../cmd/$(notdir $@)/*.go -build.linux.armv7: +build/linux/arm/v7/%: GO111MODULE=$(GO111) \ GOOS=linux \ GOARCH=arm \ GOARM=7 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux_arm7/skipper ../cmd/skipper/*.go + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/arm/v7/$(notdir $@) ../cmd/$(notdir $@)/*.go -build.osx: +build/darwin/amd64/%: GO111MODULE=$(GO111) \ GOOS=darwin \ GOARCH=amd64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -o build/osx/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ../cmd/skipper + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/darwin/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go + +build/darwin/arm64/%: + GO111MODULE=$(GO111) \ + GOOS=darwin \ + GOARCH=arm64 \ + CGO_ENABLED=$(CGO_ENABLED) \ + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/darwin/arm64/$(notdir $@) ../cmd/$(notdir $@)/*.go -build.windows: +build/windows/amd64/%: GO111MODULE=$(GO111) \ GOOS=windows \ GOARCH=amd64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -o build/windows/skipper.exe -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ../cmd/skipper - -build.package: build.linux build.osx build.windows - tar --transform 's,^\.,skipper-$(VERSION)-linux-amd64,' -C build/linux -czvf skipper-$(VERSION)-linux-amd64.tar.gz . - tar --transform 's,^\.,skipper-$(VERSION)-linux-arm8,' -C build/linux_arm8 -czvf skipper-$(VERSION)-linux-arm8.tar.gz . - tar --transform 's,^\.,skipper-$(VERSION)-linux-arm7,' -C build/linux_arm7 -czvf skipper-$(VERSION)-linux-arm7.tar.gz . - tar --transform 's,^\.,skipper-$(VERSION)-osx-amd64,' -C build/osx -czvf skipper-$(VERSION)-osx-amd64.tar.gz . - tar --transform 's,^\.,skipper-$(VERSION)-windows-amd64,' -C build/windows -czvf skipper-$(VERSION)-windows-amd64.tar.gz . + go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/windows/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go + +build.package: build.linux build.darwin build.windows + tar --transform 's,^\.,skipper-$(VERSION)-linux-amd64,' -C build/linux/amd64 -czvf skipper-$(VERSION)-linux-amd64.tar.gz . + tar --transform 's,^\.,skipper-$(VERSION)-linux-arm64,' -C build/linux/arm64 -czvf skipper-$(VERSION)-linux-arm64.tar.gz . + tar --transform 's,^\.,skipper-$(VERSION)-linux-armv7,' -C build/linux/arm/v7 -czvf skipper-$(VERSION)-linux-armv7.tar.gz . + tar --transform 's,^\.,skipper-$(VERSION)-darwin-amd64,' -C build/darwin/amd64 -czvf skipper-$(VERSION)-darwin-amd64.tar.gz . + tar --transform 's,^\.,skipper-$(VERSION)-darwin-arm64,' -C build/darwin/arm64 -czvf skipper-$(VERSION)-darwin-arm64.tar.gz . + tar --transform 's,^\.,skipper-$(VERSION)-windows-amd64,' -C build/windows/amd64 -czvf skipper-$(VERSION)-windows-amd64.tar.gz . for f in *.tar.gz; do sha256sum $$f >> sha256sum.txt; done From 97e927b1eb3ce7c8a881da57d7b5823e647aad45 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Fri, 17 Jun 2022 14:15:35 +0200 Subject: [PATCH 0040/1047] make sure to import the built image to the local daemon (#2020) Signed-off-by: Martin Linkhorst --- packaging/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/Makefile b/packaging/Makefile index 3c3d809881..934259b060 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -42,11 +42,11 @@ docker.build: clean $(BINARIES) docker-build: docker.build.amd64 docker.build.arm64 docker.build.armv7 docker.build.amd64: clean build.linux.amd64 docker.build.enable - docker buildx build -t $(IMAGE) --platform linux/amd64 -f Dockerfile . + docker buildx build -t $(IMAGE) --platform linux/amd64 -f Dockerfile --load . docker.build.arm64: clean build.linux.arm64 docker.build.enable - docker buildx build -t $(ARM64_IMAGE) --platform linux/arm64 -f Dockerfile.arm64 . + docker buildx build -t $(ARM64_IMAGE) --platform linux/arm64 -f Dockerfile.arm64 --load . docker.build.armv7: clean build.linux.armv7 docker.build.enable - docker buildx build -t $(ARM_IMAGE) --platform linux/arm/v7 -f Dockerfile.armv7 . + docker buildx build -t $(ARM_IMAGE) --platform linux/arm/v7 -f Dockerfile.armv7 --load . docker-push: docker.push.amd64 docker.push.arm64 docker.push.armv7 docker.push.amd64: From 1a019b970448bd2e2b9eeab66a4b8e17d383b7de Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Fri, 17 Jun 2022 16:17:10 +0200 Subject: [PATCH 0041/1047] install arm (32-bit) emulator to build armv7 image (#2022) Signed-off-by: Martin Linkhorst --- packaging/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/Makefile b/packaging/Makefile index 934259b060..6bb2383638 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -58,7 +58,7 @@ docker.push.armv7: docker.build.armv7 docker.push.multiarch: clean build.linux docker.build.enable # Install qemu interpreter for arm64 (https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images) - docker run --rm --privileged container-registry.zalando.net/teapot/tonistiigi-binfmt:qemu-v6.2.0-main-2 --install arm64 + docker run --rm --privileged container-registry.zalando.net/teapot/tonistiigi-binfmt:qemu-v6.2.0-main-3 --install arm,arm64 # create a Buildkit builder with CDP specific configuration (https://cloud.docs.zalando.net/howtos/cdp-multiarch/) docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use From 691d12a620fd30eda75a575e91b2c76dfa089b64 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Fri, 17 Jun 2022 18:01:37 +0200 Subject: [PATCH 0042/1047] install all qemu interpreters before the build runs (#2023) Signed-off-by: Martin Linkhorst --- delivery.yaml | 7 +++++++ packaging/Makefile | 8 +------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 15d134b3ac..8ca1fccbbe 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -6,6 +6,13 @@ pipeline: image: "cdp-runtime/go" type: script commands: + - desc: Setup QEMU + cmd: | + # Install qemu interpreter for arm64 (https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images) + docker run --rm --privileged container-registry.zalando.net/teapot/tonistiigi-binfmt:qemu-v6.2.0-main-3 --install arm,arm64 + + # create a Buildkit builder with CDP specific configuration (https://cloud.docs.zalando.net/howtos/cdp-multiarch/) + docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use - desc: build-push cmd: | IMAGE_REGISTRY="registry-write.opensource.zalan.do" diff --git a/packaging/Makefile b/packaging/Makefile index 6bb2383638..bcb203bffb 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -56,14 +56,8 @@ docker.push.arm64: docker.build.arm64 docker.push.armv7: docker.build.armv7 docker push $(ARM_IMAGE) +# build multi-arch container image using a trusted multi-arch base image docker.push.multiarch: clean build.linux docker.build.enable - # Install qemu interpreter for arm64 (https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images) - docker run --rm --privileged container-registry.zalando.net/teapot/tonistiigi-binfmt:qemu-v6.2.0-main-3 --install arm,arm64 - - # create a Buildkit builder with CDP specific configuration (https://cloud.docs.zalando.net/howtos/cdp-multiarch/) - docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use - - # build multi-arch container image using a trusted multi-arch base image docker buildx build --rm -t $(MULTIARCH_IMAGE) --platform linux/amd64,linux/arm64 --push \ --build-arg BASE_IMAGE=container-registry.zalando.net/library/alpine-3.13:latest . From 998a658ce5a68a336a98f4e63e4371e200860dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Mon, 20 Jun 2022 11:22:42 +0200 Subject: [PATCH 0043/1047] fix: Query predicate could be bypassed by prepared request, config is enabled by default and you can disable with -validate-query=false or (#2028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- config/config.go | 8 +++++++ config/config_test.go | 1 + net/query.go | 18 ++++++++++++++ net/query_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 net/query.go create mode 100644 net/query_test.go diff --git a/config/config.go b/config/config.go index d3a32745b9..28e1a9b922 100644 --- a/config/config.go +++ b/config/config.go @@ -142,6 +142,7 @@ type Config struct { NormalizeHost bool `yaml:"normalize-host"` HostPatch net.HostPatch `yaml:"-"` + ValidateQuery bool `yaml:"validate-query"` RefusePayload multiFlag `yaml:"refuse-payload"` // Kubernetes: @@ -404,6 +405,7 @@ func NewConfig() *Config { flag.Var(cfg.ForwardedHeadersExcludeCIDRList, "forwarded-headers-exclude-cidrs", "disables addition of forwarded headers for the remote host IPs from the comma separated list of CIDRs") flag.BoolVar(&cfg.NormalizeHost, "normalize-host", false, "converts request host to lowercase and removes port and trailing dot if any") + flag.BoolVar(&cfg.ValidateQuery, "validate-query", true, "Validates the HTTP Query of a request and if invalid responds with status code 400") flag.Var(&cfg.RefusePayload, "refuse-payload", "refuse requests that match configured value. Can be set multiple times") @@ -920,6 +922,12 @@ func (c *Config) ToOptions() skipper.Options { }) } + if c.ValidateQuery { + wrappers = append(wrappers, func(handler http.Handler) http.Handler { + return &net.ValidateQueryHandler{Handler: handler} + }) + } + return options } diff --git a/config/config_test.go b/config/config_test.go index c3dd217f5c..ec7e65406f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -168,6 +168,7 @@ func Test_NewConfig(t *testing.T) { ForwardedHeadersExcludeCIDRList: commaListFlag(), ClusterRatelimitMaxGroupShards: 1, RefusePayload: multiFlag{"foo", "bar", "baz"}, + ValidateQuery: true, }, wantErr: false, }, diff --git a/net/query.go b/net/query.go new file mode 100644 index 0000000000..3953f30a03 --- /dev/null +++ b/net/query.go @@ -0,0 +1,18 @@ +package net + +import ( + "net/http" + "net/url" +) + +type ValidateQueryHandler struct { + Handler http.Handler +} + +func (q *ValidateQueryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if _, err := url.ParseQuery(r.URL.RawQuery); err != nil { + http.Error(w, "Invalid query", http.StatusBadRequest) + return + } + q.Handler.ServeHTTP(w, r) +} diff --git a/net/query_test.go b/net/query_test.go new file mode 100644 index 0000000000..02bb90c131 --- /dev/null +++ b/net/query_test.go @@ -0,0 +1,55 @@ +package net + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestQueryHandler(t *testing.T) { + for _, ti := range []struct { + name string + routes string + query string + expected int + }{ + { + name: "request without query", + routes: `r: * -> inlineContent("OK") -> ;`, + query: "", + expected: http.StatusOK, + }, + { + name: "request with query", + routes: `r1: Query("foo") -> status(400) -> inlineContent("FAIL") -> ;r2: * -> inlineContent("OK") -> ;`, + query: "foo=bar", + expected: http.StatusOK, + }, + { + name: "request with bad query", + routes: `r1: Query("foo") -> status(400) -> inlineContent("FAIL") -> ;r2: * -> inlineContent("OK") -> ;`, + query: "foo=bar;", + expected: http.StatusBadRequest, + }, + } { + t.Run(ti.name, func(t *testing.T) { + + req, err := http.NewRequest("GET", "/path", nil) + if err != nil { + t.Fatal(err) + } + req.URL.RawQuery = ti.query + + noop := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + recorder := httptest.NewRecorder() + h := &ValidateQueryHandler{ + Handler: noop, + } + h.ServeHTTP(recorder, req) + + if recorder.Code != ti.expected { + t.Fatalf("Failed to get expected code %d, got %d", ti.expected, recorder.Code) + } + }) + } +} From b43f4a1afc3d78577c62acf0ed73fc099fc50828 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 20 Jun 2022 18:21:45 +0200 Subject: [PATCH 0044/1047] dataclients/kubernetes: ignore Ingress user filters when no endpoints (#2024) Signed-off-by: Alexander Yastrebov --- dataclients/kubernetes/ingressv1.go | 12 +++++++----- dataclients/kubernetes/ingressv1beta1.go | 12 +++++++----- .../bar.foo | 1 + .../ingress-data/ing-with-prule-wrong-ing-port.eskip | 7 ++++++- .../ingress-data/ing-with-prule-wrong-ing-port.yaml | 3 +++ .../bar.foo | 1 + .../ingress-data/ing-with-prule-wrong-ing-port.eskip | 7 ++++++- .../ingress-data/ing-with-prule-wrong-ing-port.yaml | 3 +++ .../myapp.default | 1 + .../convert/target-endpoints-not-found.eskip | 4 ++-- .../convert/target-endpoints-not-found.yaml | 3 +++ 11 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo create mode 100644 dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.default-filters/myapp.default diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index bafc01f76e..d286e1e309 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -155,11 +155,13 @@ func (ing *ingress) addEndpointsRuleV1(ic ingressContext, host string, prule *de return fmt.Errorf("error while getting service: %v", err) } - // safe prepend, see: https://play.golang.org/p/zg5aGKJpRyK - filters := make([]*eskip.Filter, len(endpointsRoute.Filters)+len(ic.annotationFilters)) - copy(filters, ic.annotationFilters) - copy(filters[len(ic.annotationFilters):], endpointsRoute.Filters) - endpointsRoute.Filters = filters + if endpointsRoute.BackendType != eskip.ShuntBackend { + // safe prepend, see: https://play.golang.org/p/zg5aGKJpRyK + filters := make([]*eskip.Filter, len(endpointsRoute.Filters)+len(ic.annotationFilters)) + copy(filters, ic.annotationFilters) + copy(filters[len(ic.annotationFilters):], endpointsRoute.Filters) + endpointsRoute.Filters = filters + } // add pre-configured default filters df, err := ic.defaultFilters.getNamed(meta.Namespace, prule.Backend.Service.Name) diff --git a/dataclients/kubernetes/ingressv1beta1.go b/dataclients/kubernetes/ingressv1beta1.go index 1e1c1ae745..649e6997e4 100644 --- a/dataclients/kubernetes/ingressv1beta1.go +++ b/dataclients/kubernetes/ingressv1beta1.go @@ -154,11 +154,13 @@ func (ing *ingress) addEndpointsRule(ic ingressContext, host string, prule *defi return fmt.Errorf("error while getting service: %v", err) } - // safe prepend, see: https://play.golang.org/p/zg5aGKJpRyK - filters := make([]*eskip.Filter, len(endpointsRoute.Filters)+len(ic.annotationFilters)) - copy(filters, ic.annotationFilters) - copy(filters[len(ic.annotationFilters):], endpointsRoute.Filters) - endpointsRoute.Filters = filters + if endpointsRoute.BackendType != eskip.ShuntBackend { + // safe prepend, see: https://play.golang.org/p/zg5aGKJpRyK + filters := make([]*eskip.Filter, len(endpointsRoute.Filters)+len(ic.annotationFilters)) + copy(filters, ic.annotationFilters) + copy(filters[len(ic.annotationFilters):], endpointsRoute.Filters) + endpointsRoute.Filters = filters + } // add pre-configured default filters df, err := ic.defaultFilters.getNamed(meta.Namespace, prule.Backend.ServiceName) diff --git a/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo b/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo new file mode 100644 index 0000000000..546e104c10 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo @@ -0,0 +1 @@ +setResponseHeader("default", "filter") diff --git a/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.eskip b/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.eskip index 08341eb20e..d358682bed 100644 --- a/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.eskip +++ b/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.eskip @@ -1 +1,6 @@ -kube_foo__qux__www_example_org_____bar: Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") -> status(502) -> inlineContent("no endpoints") -> ; +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setResponseHeader("default", "filter") + -> status(502) + -> inlineContent("no endpoints") + -> ; diff --git a/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.yaml b/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.yaml index 0f37ca5b3b..cae0ec2fc9 100644 --- a/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.yaml +++ b/dataclients/kubernetes/testdata/ingress/ingress-data/ing-with-prule-wrong-ing-port.yaml @@ -3,6 +3,9 @@ kind: Ingress metadata: namespace: foo name: qux + annotations: + # user filters ignored when no endpoints + zalando.org/skipper-filter: setRequestHeader("user", "filter") spec: rules: - host: www.example.org diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo new file mode 100644 index 0000000000..546e104c10 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.default-filters/bar.foo @@ -0,0 +1 @@ +setResponseHeader("default", "filter") diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.eskip index 08341eb20e..d358682bed 100644 --- a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.eskip +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.eskip @@ -1 +1,6 @@ -kube_foo__qux__www_example_org_____bar: Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") -> status(502) -> inlineContent("no endpoints") -> ; +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setResponseHeader("default", "filter") + -> status(502) + -> inlineContent("no endpoints") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.yaml index a22e84ffa0..7191d69c78 100644 --- a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.yaml +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-prule-wrong-ing-port.yaml @@ -3,6 +3,9 @@ kind: Ingress metadata: namespace: foo name: qux + annotations: + # user filters ignored when no endpoints + zalando.org/skipper-filter: setRequestHeader("user", "filter") spec: rules: - host: www.example.org diff --git a/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.default-filters/myapp.default b/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.default-filters/myapp.default new file mode 100644 index 0000000000..546e104c10 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.default-filters/myapp.default @@ -0,0 +1 @@ +setResponseHeader("default", "filter") diff --git a/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.eskip b/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.eskip index 7a09861380..78562c79c5 100644 --- a/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.eskip +++ b/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.eskip @@ -1,6 +1,6 @@ kube_rg__default__myapp__all__0_0: - Host("^(example[.]org[.]?(:[0-9]+)?)$") - && Path("/app") + Host("^(example[.]org[.]?(:[0-9]+)?)$") && Path("/app") + -> setResponseHeader("default", "filter") -> status(502) -> inlineContent("no endpoints") -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.yaml b/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.yaml index 8193b3d035..fa8cb75eb9 100644 --- a/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.yaml +++ b/dataclients/kubernetes/testdata/routegroups/convert/target-endpoints-not-found.yaml @@ -14,6 +14,9 @@ spec: - backendName: myapp routes: - path: /app + filters: + # user filters ignored when no endpoints + - setRequestHeader("user", "filter") --- apiVersion: v1 kind: Service From 3c2f1081d6285e829352037cd62eb25ddc01fddc Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Tue, 21 Jun 2022 04:24:49 +0800 Subject: [PATCH 0045/1047] refactor: replace strings.Replace with strings.ReplaceAll (#2029) strings.ReplaceAll(s, old, new) is a wrapper function for strings.Replace(s, old, new, -1). But strings.ReplaceAll is more readable and removes the hardcoded -1. Signed-off-by: Eng Zer Jun --- dataclients/kubernetes/hosts.go | 2 +- dataclients/kubernetes/ingress.go | 2 +- eskip/string.go | 18 +++++++++--------- eskip/template.go | 2 +- .../filter_endpointmetrics_test.go | 4 ++-- filters/auth/oidc_test.go | 8 ++++---- metrics/utils.go | 4 ++-- proxy/loopback_test.go | 2 +- proxy/proxy_test.go | 6 +++--- rfc/patchhost.go | 2 +- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dataclients/kubernetes/hosts.go b/dataclients/kubernetes/hosts.go index ec66ac2d08..60dfd4a37a 100644 --- a/dataclients/kubernetes/hosts.go +++ b/dataclients/kubernetes/hosts.go @@ -18,7 +18,7 @@ func createHostRx(hosts ...string) string { // trailing dots and port are not allowed in kube // ingress spec, so we can append optional setting // without check - hrx[i] = strings.Replace(host, ".", "[.]", -1) + "[.]?(:[0-9]+)?" + hrx[i] = strings.ReplaceAll(host, ".", "[.]") + "[.]?(:[0-9]+)?" } return "^(" + strings.Join(hrx, "|") + ")$" diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index 8687843a08..b44c7642c2 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -213,7 +213,7 @@ func addExtraRoutes(ic ingressContext, ruleHost, path, pathType, eastWestDomain ns, name, route.Id, - ruleHost+strings.Replace(path, "/", "_", -1), + ruleHost+strings.ReplaceAll(path, "/", "_"), extraIndex) setPathV1(ic.pathMode, &route, pathType, path) if n := countPathRoutes(&route); n <= 1 { diff --git a/eskip/string.go b/eskip/string.go index decbf32530..5a1eaca7e7 100644 --- a/eskip/string.go +++ b/eskip/string.go @@ -14,17 +14,17 @@ type PrettyPrintInfo struct { } func escape(s string, chars string) string { - s = strings.Replace(s, `\`, `\\`, -1) // escape backslash before others - s = strings.Replace(s, "\a", `\a`, -1) - s = strings.Replace(s, "\b", `\b`, -1) - s = strings.Replace(s, "\f", `\f`, -1) - s = strings.Replace(s, "\n", `\n`, -1) - s = strings.Replace(s, "\r", `\r`, -1) - s = strings.Replace(s, "\t", `\t`, -1) - s = strings.Replace(s, "\v", `\v`, -1) + s = strings.ReplaceAll(s, `\`, `\\`) // escape backslash before others + s = strings.ReplaceAll(s, "\a", `\a`) + s = strings.ReplaceAll(s, "\b", `\b`) + s = strings.ReplaceAll(s, "\f", `\f`) + s = strings.ReplaceAll(s, "\n", `\n`) + s = strings.ReplaceAll(s, "\r", `\r`) + s = strings.ReplaceAll(s, "\t", `\t`) + s = strings.ReplaceAll(s, "\v", `\v`) for i := 0; i < len(chars); i++ { c := chars[i : i+1] - s = strings.Replace(s, c, `\`+c, -1) + s = strings.ReplaceAll(s, c, `\`+c) } return s diff --git a/eskip/template.go b/eskip/template.go index 209d61f056..8551baa873 100644 --- a/eskip/template.go +++ b/eskip/template.go @@ -109,7 +109,7 @@ func (t *Template) apply(get TemplateGetter) (string, bool) { if value == "" { missing = true } - result = strings.Replace(result, "${"+placeholder+"}", value, -1) + result = strings.ReplaceAll(result, "${"+placeholder+"}", value) } return result, !missing } diff --git a/filters/apiusagemonitoring/filter_endpointmetrics_test.go b/filters/apiusagemonitoring/filter_endpointmetrics_test.go index 5f4181ee33..7f23577114 100644 --- a/filters/apiusagemonitoring/filter_endpointmetrics_test.go +++ b/filters/apiusagemonitoring/filter_endpointmetrics_test.go @@ -331,7 +331,7 @@ func Test_Filter_PathTemplateMatchesInternalSlashesTooFollowingVarPart(t *testin {"foo/1/2/3/4/5", "foo/{a}/{b}/{c}"}, {"bar/1/2-3/4/5", "bar/{a}-{b}/{c}"}, } { - subTestName := strings.Replace(c.requestPath, "/", "_", -1) + subTestName := strings.ReplaceAll(c.requestPath, "/", "_") t.Run(subTestName, func(t *testing.T) { testWithFilter( t, @@ -379,7 +379,7 @@ func Test_Filter_PathTemplateMatchesPathFromRequestChain(t *testing.T) { }{ {"foo/x", "bar/x", "foo/{a}"}, } { - subTestName := strings.Replace(c.requestPath, "/", "_", -1) + subTestName := strings.ReplaceAll(c.requestPath, "/", "_") t.Run(subTestName, func(t *testing.T) { testWithFilterModifyContext( t, diff --git a/filters/auth/oidc_test.go b/filters/auth/oidc_test.go index aba2f155d5..b1a0a42c2d 100644 --- a/filters/auth/oidc_test.go +++ b/filters/auth/oidc_test.go @@ -135,10 +135,10 @@ func createOIDCServer(cb, client, clientsecret string, extraClaims jwt.MapClaims case "/.well-known/openid-configuration": // dynamic config handling // set oidcServer local dynamic listener to us - st := strings.Replace(testOpenIDConfig, "https://accounts.google.com", oidcServer.URL, -1) - st = strings.Replace(st, "https://oauth2.googleapis.com", oidcServer.URL, -1) - st = strings.Replace(st, "https://www.googleapis.com", oidcServer.URL, -1) - st = strings.Replace(st, "https://openidconnect.googleapis.com", oidcServer.URL, -1) + st := strings.ReplaceAll(testOpenIDConfig, "https://accounts.google.com", oidcServer.URL) + st = strings.ReplaceAll(st, "https://oauth2.googleapis.com", oidcServer.URL) + st = strings.ReplaceAll(st, "https://www.googleapis.com", oidcServer.URL) + st = strings.ReplaceAll(st, "https://openidconnect.googleapis.com", oidcServer.URL) _, _ = w.Write([]byte(st)) case "/o/oauth2/v2/auth": // https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowSteps diff --git a/metrics/utils.go b/metrics/utils.go index 4c14e2d575..82dcb18a8a 100644 --- a/metrics/utils.go +++ b/metrics/utils.go @@ -19,8 +19,8 @@ func createTimer(sample metrics.Sample) metrics.Timer { } func hostForKey(h string) string { - h = strings.Replace(h, ".", "_", -1) - h = strings.Replace(h, ":", "__", -1) + h = strings.ReplaceAll(h, ".", "_") + h = strings.ReplaceAll(h, ":", "__") return h } diff --git a/proxy/loopback_test.go b/proxy/loopback_test.go index 322c7f2d6e..6712554380 100644 --- a/proxy/loopback_test.go +++ b/proxy/loopback_test.go @@ -88,7 +88,7 @@ func testLoopback( w.Header().Set("X-Backend-Done", "true") })) - routes = strings.Replace(routes, "$backend", backend.URL, -1) + routes = strings.ReplaceAll(routes, "$backend", backend.URL) fr := builtin.MakeRegistry() fr.Register(&preserveOriginalSpec{}) diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 35ece602a4..7b3c866dde 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -1545,9 +1545,9 @@ func TestBranding(t *testing.T) { backendDown.Close() routes := routesTpl - routes = strings.Replace(routes, "${backend-down}", backendDown.URL, -1) - routes = strings.Replace(routes, "${backend-default}", backendDefault.URL, -1) - routes = strings.Replace(routes, "${backend-set}", backendSet.URL, -1) + routes = strings.ReplaceAll(routes, "${backend-down}", backendDown.URL) + routes = strings.ReplaceAll(routes, "${backend-default}", backendDefault.URL) + routes = strings.ReplaceAll(routes, "${backend-set}", backendSet.URL) p, err := newTestProxy(routes, FlagsNone) if err != nil { diff --git a/rfc/patchhost.go b/rfc/patchhost.go index 8b9a365496..ff03bb123c 100644 --- a/rfc/patchhost.go +++ b/rfc/patchhost.go @@ -6,6 +6,6 @@ import "strings" // see also the discussion in // https://lists.w3.org/Archives/Public/ietf-http-wg/2016JanMar/0430.html. func PatchHost(host string) string { - host = strings.Replace(host, ".:", ":", -1) + host = strings.ReplaceAll(host, ".:", ":") return strings.TrimSuffix(host, ".") } From 1ef90fae858e8cb4846890c6f2d995c3db2c22e2 Mon Sep 17 00:00:00 2001 From: Wim Spaargaren Date: Mon, 27 Jun 2022 12:39:33 +0200 Subject: [PATCH 0046/1047] add a custom routing pre processor option (#2031) Signed-off-by: Wim --- skipper.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/skipper.go b/skipper.go index e64b43991c..e1e0d14cef 100644 --- a/skipper.go +++ b/skipper.go @@ -277,6 +277,10 @@ type Options struct { // will apply changes to all matching routes. EditRoute *eskip.Editor + // A list of custom routing pre-processor implementations that will + // be applied to all routes. + CustomRoutingPreProcessors []routing.PreProcessor + // Deprecated. See ProxyFlags. When used together with ProxyFlags, // the values will be combined with |. ProxyOptions proxy.Options @@ -1610,6 +1614,10 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ro.PreProcessors = append(ro.PreProcessors, oauthConfig.NewGrantPreprocessor()) } + if o.CustomRoutingPreProcessors != nil { + ro.PreProcessors = append(ro.PreProcessors, o.CustomRoutingPreProcessors...) + } + routing := routing.New(ro) defer routing.Close() From a6572d6bf69c7342c8c49f6ac64e0e68f4b3eea1 Mon Sep 17 00:00:00 2001 From: Mustafa Saber Date: Wed, 29 Jun 2022 11:25:51 +0200 Subject: [PATCH 0047/1047] Log invalid query (#2032) * log invalid query Signed-off-by: Mustafa Abdelrahman --- config/config.go | 11 ++++++++--- config/config_test.go | 1 + net/query.go | 10 +++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 28e1a9b922..ddd72f8b20 100644 --- a/config/config.go +++ b/config/config.go @@ -142,8 +142,9 @@ type Config struct { NormalizeHost bool `yaml:"normalize-host"` HostPatch net.HostPatch `yaml:"-"` - ValidateQuery bool `yaml:"validate-query"` - RefusePayload multiFlag `yaml:"refuse-payload"` + ValidateQuery bool `yaml:"validate-query"` + ValidateQueryLog bool `yaml:"validate-query-log"` + RefusePayload multiFlag `yaml:"refuse-payload"` // Kubernetes: KubernetesIngress bool `yaml:"kubernetes"` @@ -406,6 +407,7 @@ func NewConfig() *Config { flag.BoolVar(&cfg.NormalizeHost, "normalize-host", false, "converts request host to lowercase and removes port and trailing dot if any") flag.BoolVar(&cfg.ValidateQuery, "validate-query", true, "Validates the HTTP Query of a request and if invalid responds with status code 400") + flag.BoolVar(&cfg.ValidateQueryLog, "validate-query-log", true, "Enable looging for validate query logs") flag.Var(&cfg.RefusePayload, "refuse-payload", "refuse requests that match configured value. Can be set multiple times") @@ -924,7 +926,10 @@ func (c *Config) ToOptions() skipper.Options { if c.ValidateQuery { wrappers = append(wrappers, func(handler http.Handler) http.Handler { - return &net.ValidateQueryHandler{Handler: handler} + return &net.ValidateQueryHandler{ + LogsEnabled: c.ValidateQueryLog, + Handler: handler, + } }) } diff --git a/config/config_test.go b/config/config_test.go index ec7e65406f..885d14b56a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -169,6 +169,7 @@ func Test_NewConfig(t *testing.T) { ClusterRatelimitMaxGroupShards: 1, RefusePayload: multiFlag{"foo", "bar", "baz"}, ValidateQuery: true, + ValidateQueryLog: true, }, wantErr: false, }, diff --git a/net/query.go b/net/query.go index 3953f30a03..ccbc2cf678 100644 --- a/net/query.go +++ b/net/query.go @@ -3,14 +3,22 @@ package net import ( "net/http" "net/url" + + log "github.com/sirupsen/logrus" ) type ValidateQueryHandler struct { - Handler http.Handler + LogsEnabled bool + Handler http.Handler } func (q *ValidateQueryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if _, err := url.ParseQuery(r.URL.RawQuery); err != nil { + + if q.LogsEnabled { + log.Info("Invalid query") + } + http.Error(w, "Invalid query", http.StatusBadRequest) return } From ec1f324b1f2414fb2483614390b962d3a06860a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 29 Jun 2022 13:33:45 +0200 Subject: [PATCH 0048/1047] pull out logging query into separate handler such that we can log without blocking (#2033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- config/config.go | 10 ++++++++-- net/query.go | 26 +++++++++++++++++-------- net/query_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index ddd72f8b20..9104a7c2bc 100644 --- a/config/config.go +++ b/config/config.go @@ -927,8 +927,14 @@ func (c *Config) ToOptions() skipper.Options { if c.ValidateQuery { wrappers = append(wrappers, func(handler http.Handler) http.Handler { return &net.ValidateQueryHandler{ - LogsEnabled: c.ValidateQueryLog, - Handler: handler, + Handler: handler, + } + }) + } + if c.ValidateQueryLog { + wrappers = append(wrappers, func(handler http.Handler) http.Handler { + return &net.ValidateQueryLogHandler{ + Handler: handler, } }) } diff --git a/net/query.go b/net/query.go index ccbc2cf678..50a64dedf4 100644 --- a/net/query.go +++ b/net/query.go @@ -8,19 +8,29 @@ import ( ) type ValidateQueryHandler struct { - LogsEnabled bool - Handler http.Handler + Handler http.Handler } func (q *ValidateQueryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if _, err := url.ParseQuery(r.URL.RawQuery); err != nil { - - if q.LogsEnabled { - log.Info("Invalid query") - } - + if err := validateQuery(r.URL.RawQuery); err != nil { http.Error(w, "Invalid query", http.StatusBadRequest) return } q.Handler.ServeHTTP(w, r) } + +type ValidateQueryLogHandler struct { + Handler http.Handler +} + +func (q *ValidateQueryLogHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if err := validateQuery(r.URL.RawQuery); err != nil { + log.Infof("Invalid query: %s -> %s %s %s", r.RemoteAddr, r.Host, r.URL.Path, r.Method) + } + q.Handler.ServeHTTP(w, r) +} + +func validateQuery(s string) error { + _, err := url.ParseQuery(s) + return err +} diff --git a/net/query_test.go b/net/query_test.go index 02bb90c131..fe78d34b47 100644 --- a/net/query_test.go +++ b/net/query_test.go @@ -53,3 +53,51 @@ func TestQueryHandler(t *testing.T) { }) } } + +func TestQueryLogHandler(t *testing.T) { + for _, ti := range []struct { + name string + routes string + query string + expected int + }{ + { + name: "request without query", + routes: `r: * -> inlineContent("OK") -> ;`, + query: "", + expected: http.StatusOK, + }, + { + name: "request with query", + routes: `r1: Query("foo") -> status(400) -> inlineContent("FAIL") -> ;r2: * -> inlineContent("OK") -> ;`, + query: "foo=bar", + expected: http.StatusOK, + }, + { + name: "request with bad query does not block", + routes: `r1: Query("foo") -> status(400) -> inlineContent("FAIL") -> ;r2: * -> inlineContent("OK") -> ;`, + query: "foo=bar;", + expected: http.StatusOK, + }, + } { + t.Run(ti.name, func(t *testing.T) { + + req, err := http.NewRequest("GET", "/path", nil) + if err != nil { + t.Fatal(err) + } + req.URL.RawQuery = ti.query + + noop := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + recorder := httptest.NewRecorder() + h := &ValidateQueryLogHandler{ + Handler: noop, + } + h.ServeHTTP(recorder, req) + + if recorder.Code != ti.expected { + t.Fatalf("Failed to get expected code %d, got %d", ti.expected, recorder.Code) + } + }) + } +} From d9fc6b7dc4fc07c000409c45bc5d71c3d4cf9c49 Mon Sep 17 00:00:00 2001 From: Martin Linkhorst Date: Thu, 30 Jun 2022 18:57:20 +0200 Subject: [PATCH 0049/1047] use arm64 emulator that CDP provides (#2034) Signed-off-by: Martin Linkhorst --- delivery.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/delivery.yaml b/delivery.yaml index 8ca1fccbbe..6b10b5e0c4 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -6,11 +6,8 @@ pipeline: image: "cdp-runtime/go" type: script commands: - - desc: Setup QEMU + - desc: Setup BuildKit cmd: | - # Install qemu interpreter for arm64 (https://docs.docker.com/buildx/working-with-buildx/#build-multi-platform-images) - docker run --rm --privileged container-registry.zalando.net/teapot/tonistiigi-binfmt:qemu-v6.2.0-main-3 --install arm,arm64 - # create a Buildkit builder with CDP specific configuration (https://cloud.docs.zalando.net/howtos/cdp-multiarch/) docker buildx create --config /etc/cdp-buildkitd.toml --driver-opt network=host --bootstrap --use - desc: build-push From c67b6443e6c20452cd6a816abce7b936cbf8bf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 1 Jul 2022 10:53:46 +0200 Subject: [PATCH 0050/1047] update vulnerability report link (#2036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 9c9356f73d..84822b339a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,7 +3,7 @@ We are trying to deal with it responsibly and provide patches as quickly as poss We host our bug bounty program on HackerOne, it is currently private, therefore if you would like to report a vulnerability and get rewarded for it, please ask to join our program by filling this form: -https://corporate.zalando.com/en/services-and-contact#security-form +https://zeng.page.link/open-source-report-vulnerability You can also send you report via this form if your do not want to join our bug bounty program and just want to report a vulnerability or security issue. From ce89ee363f3a131d38bb3439f0f52e93b1f1a417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 6 Jul 2022 13:21:10 +0200 Subject: [PATCH 0051/1047] feature: admissionControl filter (#2030) --- docs/reference/filters.md | 80 +++++- filters/filters.go | 4 +- filters/shedder/admission.go | 447 ++++++++++++++++++++++++++++ filters/shedder/admission_test.go | 463 ++++++++++++++++++++++++++++++ proxy/proxytest/proxytest.go | 2 +- skipper.go | 13 + 6 files changed, 1003 insertions(+), 6 deletions(-) create mode 100644 filters/shedder/admission.go create mode 100644 filters/shedder/admission_test.go diff --git a/docs/reference/filters.md b/docs/reference/filters.md index a1cb4a34ea..0619cc76e3 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -372,8 +372,8 @@ no-compression, 1 means best-speed and 11 means best-compression. Example: ``` The filter also checks the incoming request, if it accepts the supported encodings, -explicitly stated in the Accept-Encoding header. -The filter currently supports by default `gzip`, `deflate` and `br` (can be overridden with flag `compress-encodings`). +explicitly stated in the Accept-Encoding header. +The filter currently supports by default `gzip`, `deflate` and `br` (can be overridden with flag `compress-encodings`). It does not assume that the client accepts any encoding if the Accept-Encoding header is not set. It ignores * in the Accept-Encoding header. @@ -1848,6 +1848,78 @@ Path("/cheap") -> clusterLeakyBucketRatelimit("user-${request.cookie.Authori Path("/expensive") -> clusterLeakyBucketRatelimit("user-${request.cookie.Authorization}", 1, "1s", 5, 2) -> ... ``` +## shedder + +The basic idea of load shedding is to reduce errors by early stopping +some of the ingress requests that create too much load and serving +the maximum throughput the system can process at a point in time. + +There is a great talk by [Acacio Cruz from +Google](https://www.youtube.com/watch?v=XNEIkivvaV4) +that explains the basic principles. + +### admissionControl + +Implements an admission control filter, that rejects traffic by +observed error rate and probability. + +The probability of rejection is calculated by the following equation: + +$$ P_{reject} = ( { n_{total} - { n_{success} \over threshold } \over n_{total} + 1} )^{ exponent } $$ + +Examples: + + admissionControl(metricSuffix, mode, d, windowSize, minRPS, successThreshold, maxRejectProbability, exponent) + admissionControl("myapp", "active", "1s", 5, 10, 0.95, 0.9, 0.5) + +Parameters: + +* metric suffix (string) +* mode (enum) +* d (time.Duration) +* window size (int) +* minRps (int) +* success threshold (float64) +* max reject probability (float64) +* exponent (float64) + +Metric suffix is the chosen suffix key to expose reject counter, +should be unique by filter instance + +Mode has 3 different possible values: + +* "active" will reject traffic +* "inactive" will never reject traffic +* "logInactive" will not reject traffic, but log to debug filter settings + +D the time duration of a single slot for required counters in our +circular buffer of window size. + +Window size is the size of the circular buffer. It is used to snapshot +counters to calculate total requests and number of success. It is +within $[1, 100]$. + +MinRps is the minimum requests per second that have to pass this filter +otherwise it will not reject traffic. + +Success threshold sets the lowest request success rate at which the +filter will not reject requests. It is within $(0,1]$. A value of +0.95 means an error rate of lower than 5% will not trigger +rejects. + +Max reject probability sets the upper bound of reject probability. It +is within (0,1]. A value of 0.95 means if backend errors with 100% it +will only reject up to 95%. + +exponent is used to dictate the rejection probability. The +calculation is done by $p = p^{exponent}$ +The exponent value is within $(0,\infty]$, to increase rejection +probability you have to use values lower than 1: + +* 1: linear +* 1/2: quadratic +* 1/3: cubic + ## lua See [the scripts page](scripts.md) @@ -2557,7 +2629,7 @@ fadeIn("3m", 1.5) #### Warning on fadeIn and Rolling Restarts -Traffic fade-in has the potential to skew the traffic to your backend pods in case of a rolling restart +Traffic fade-in has the potential to skew the traffic to your backend pods in case of a rolling restart (`kubectl rollout restart`), because it is very likely that the rolling restart is going faster than the fade-in duration. The image below shows an example of a rolling restart for a four-pod deployment (A, B, C, D) into (E, F, G, H), and the traffic share of each pod over time. While the ramp-up of the new pods is ongoing, @@ -2565,7 +2637,7 @@ the remaining old pods will receive a largely increased traffic share (especiall example), as well as an over-propotional traffic share for the first pod in the rollout (E). To make rolling restarts safe, you need to slow them down by setting `spec.minReadySeconds` on the pod spec -of your deployment or stackset, according to your fadeIn duration. +of your deployment or stackset, according to your fadeIn duration. ![Rolling Restart and Fade-In](../img/fadein_traffic_skew.png) diff --git a/filters/filters.go b/filters/filters.go index ef805b59f2..52f92163b7 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -2,10 +2,11 @@ package filters import ( "errors" - log "github.com/sirupsen/logrus" "net/http" "time" + log "github.com/sirupsen/logrus" + "github.com/opentracing/opentracing-go" ) @@ -264,6 +265,7 @@ const ( ConsecutiveBreakerName = "consecutiveBreaker" RateBreakerName = "rateBreaker" DisableBreakerName = "disableBreaker" + AdmissionControlName = "admissionControl" ClientRatelimitName = "clientRatelimit" RatelimitName = "ratelimit" ClusterClientRatelimitName = "clusterClientRatelimit" diff --git a/filters/shedder/admission.go b/filters/shedder/admission.go new file mode 100644 index 0000000000..69cd946a7b --- /dev/null +++ b/filters/shedder/admission.go @@ -0,0 +1,447 @@ +package shedder + +import ( + "context" + "math" + "math/rand" + "net/http" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/metrics" + "github.com/zalando/skipper/routing" + "go.uber.org/atomic" +) + +func getIntArg(a interface{}) (int, error) { + if i, ok := a.(int); ok { + return i, nil + } + + if f, ok := a.(float64); ok { + return int(f), nil + } + + return 0, filters.ErrInvalidFilterParameters +} + +func getDurationArg(a interface{}) (time.Duration, error) { + if s, ok := a.(string); ok { + return time.ParseDuration(s) + } + return 0, filters.ErrInvalidFilterParameters +} + +func getFloat64Arg(a interface{}) (float64, error) { + if f, ok := a.(float64); ok { + return f, nil + } + + return 0, filters.ErrInvalidFilterParameters +} + +func getModeArg(a interface{}) (mode, error) { + s, ok := a.(string) + if !ok { + return 0, filters.ErrInvalidFilterParameters + } + switch s { + case "active": + return active, nil + case "inactive": + return inactive, nil + case "logInactive": + return logInactive, nil + } + + return 0, filters.ErrInvalidFilterParameters +} + +type mode int + +const ( + inactive mode = iota + 1 + logInactive + active +) + +func (m mode) String() string { + switch m { + case active: + return "active" + case inactive: + return "inactive" + case logInactive: + return "logInactive" + } + return "unknown" +} + +const ( + counterPrefix = "shedder.admission_control." + admissionControlSpanName = "admission_control" + admissionControlKey = "shedder:admission_control" + admissionControlValue = "reject" + minWindowSize = 1 + maxWindowSize = 100 +) + +type Options struct { + Tracer opentracing.Tracer +} + +type admissionControlPre struct{} + +// Do removes duplicate filters, because we can only handle one in a +// chain. The last one will override the others. +func (spec *admissionControlPre) Do(routes []*eskip.Route) []*eskip.Route { + for _, r := range routes { + foundAt := -1 + toDelete := make(map[int]struct{}) + + for i, f := range r.Filters { + if f.Name == filters.AdmissionControlName { + if foundAt != -1 { + toDelete[foundAt] = struct{}{} + } + foundAt = i + } + } + + if len(toDelete) == 0 { + continue + } + + rf := make([]*eskip.Filter, 0, len(r.Filters)-len(toDelete)) + for i, f := range r.Filters { + if _, ok := toDelete[i]; !ok { + rf = append(rf, f) + } + } + r.Filters = rf + } + + return routes +} + +type admissionControlPost struct { + filters map[string]*admissionControl +} + +// Do implements routing.PostProcessor and makes it possible to close goroutines. +func (spec *admissionControlPost) Do(routes []*routing.Route) []*routing.Route { + inUse := make(map[string]struct{}) + + for _, r := range routes { + + for _, f := range r.Filters { + if ac, ok := f.Filter.(*admissionControl); ok { + oldAc, okOld := spec.filters[r.Id] + if okOld { + // replace: close the old one + oldAc.Close() + } + spec.filters[r.Id] = ac + inUse[r.Id] = struct{}{} + } + } + } + + for id, f := range spec.filters { + if _, ok := inUse[id]; !ok { + // delete: close the old one + f.Close() + } + } + return routes +} + +type AdmissionControlSpec struct { + tracer opentracing.Tracer +} + +type admissionControl struct { + once sync.Once + mu sync.Mutex + quit chan struct{} + closed bool + + metrics metrics.Metrics + metricSuffix string + tracer opentracing.Tracer + + mode mode + windowSize int + minRps int + d time.Duration + successThreshold float64 // (0,1] + maxRejectProbability float64 // (0,1] + exponent float64 // >0 + + averageRpsFactor float64 + totals []int64 + success []int64 + counter *atomic.Int64 + successCounter *atomic.Int64 +} + +func NewAdmissionControl(o Options) filters.Spec { + tracer := o.Tracer + if tracer == nil { + tracer = &opentracing.NoopTracer{} + } + return &AdmissionControlSpec{ + tracer: tracer, + } +} + +func (*AdmissionControlSpec) PreProcessor() *admissionControlPre { + return &admissionControlPre{} +} + +func (*AdmissionControlSpec) PostProcessor() *admissionControlPost { + return &admissionControlPost{ + filters: make(map[string]*admissionControl), + } +} + +func (*AdmissionControlSpec) Name() string { return filters.AdmissionControlName } + +// CreateFilter creates a new admissionControl filter with passed configuration: +// +// admissionControl(metricSuffix, mode, d, windowSize, minRps, successThreshold, maxRejectProbability, exponent) +// admissionControl("$app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) +// +// metricSuffix is the suffix key to expose reject counter, should be unique by filter instance +// mode is one of "active", "inactive", "logInactive" +// active will reject traffic +// inactive will never reject traffic +// logInactive will not reject traffic, but log to debug filter settings +// windowSize is within [minWindowSize, maxWindowSize] +// minRps threshold that needs to be reached such that the filter will apply +// successThreshold is within (0,1] and sets the lowest request success rate at which the filter will not reject requests. +// maxRejectProbability is within (0,1] and sets the upper bound of reject probability. +// exponent >0, 1: linear, 1/2: qudratic, 1/3: cubic, .. +// +// see also https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/admission_control_filter#admission-control +func (spec *AdmissionControlSpec) CreateFilter(args []interface{}) (filters.Filter, error) { + var err error + + if len(args) != 8 { + return nil, filters.ErrInvalidFilterParameters + } + + metricSuffix, ok := args[0].(string) + if !ok { + log.Warn("metricsuffix required as string") + return nil, filters.ErrInvalidFilterParameters + } + + mode, err := getModeArg(args[1]) + if err != nil { + log.Warnf("mode failed: %v", err) + return nil, filters.ErrInvalidFilterParameters + } + + d, err := getDurationArg(args[2]) + if err != nil { + log.Warnf("d failed: %v", err) + return nil, filters.ErrInvalidFilterParameters + } + + windowSize, err := getIntArg(args[3]) + if err != nil { + log.Warnf("windowsize failed: %v", err) + return nil, filters.ErrInvalidFilterParameters + } + if minWindowSize > windowSize || windowSize > maxWindowSize { + log.Warnf("windowsize too small, should be within: [%d,%d], got: %d", minWindowSize, maxWindowSize, windowSize) + return nil, filters.ErrInvalidFilterParameters + } + + minRps, err := getIntArg(args[4]) + if err != nil { + log.Warnf("minRequests failed: %v", err) + return nil, filters.ErrInvalidFilterParameters + } + + threshold, err := getFloat64Arg(args[5]) + if err != nil { + log.Warnf("threshold failed %v", err) + return nil, filters.ErrInvalidFilterParameters + } + + maxRejectProbability, err := getFloat64Arg(args[6]) + if err != nil { + log.Warnf("maxRejectProbability failed: %v", err) + return nil, filters.ErrInvalidFilterParameters + } + + exponent, err := getFloat64Arg(args[7]) + if err != nil { + log.Warnf("exponent failed: %v", err) + return nil, filters.ErrInvalidFilterParameters + } + if exponent <= 0.0 { + log.Warn("exponent should be >0") + return nil, filters.ErrInvalidFilterParameters + } + + averageRpsFactor := float64(time.Second) / (float64(d) * float64(windowSize)) + + ac := &admissionControl{ + once: sync.Once{}, + + quit: make(chan struct{}), + metrics: metrics.Default, + metricSuffix: metricSuffix, + tracer: spec.tracer, + + mode: mode, + d: d, + windowSize: windowSize, + minRps: minRps, + successThreshold: threshold, + maxRejectProbability: maxRejectProbability, + exponent: exponent, + + averageRpsFactor: averageRpsFactor, + totals: make([]int64, windowSize), + success: make([]int64, windowSize), + counter: atomic.NewInt64(0), + successCounter: atomic.NewInt64(0), + } + go ac.tickWindows(d) + return ac, nil +} + +// Close stops the background goroutine. The filter keeps working on stale data. +func (ac *admissionControl) Close() { + ac.once.Do(func() { + ac.closed = true + close(ac.quit) + }) +} + +func (ac *admissionControl) tickWindows(d time.Duration) { + t := time.NewTicker(d) + i := 0 + for range t.C { + select { + case <-ac.quit: + return + default: + } + val := ac.counter.Swap(0) + ok := ac.successCounter.Swap(0) + + ac.mu.Lock() + ac.totals[i] = val + ac.success[i] = ok + ac.mu.Unlock() + + i = (i + 1) % ac.windowSize + } +} + +func (ac *admissionControl) count() (float64, float64) { + ac.mu.Lock() + defer ac.mu.Unlock() + return float64(sum(ac.totals)), float64(sum(ac.success)) +} + +func sum(a []int64) int64 { + var result int64 + for _, v := range a { + result += v + } + return result +} + +// calculates P_{reject} see https://opensource.zalando.com/skipper/reference/filters/#admissioncontrol +func (ac *admissionControl) pReject() float64 { + var rejectP float64 + + total, success := ac.count() + avgRps := total * ac.averageRpsFactor + if ac.mode == logInactive { + log.Infof("avgRps %0.2f does not reach minRps %d", avgRps, ac.minRps) + } + if avgRps < float64(ac.minRps) { + return -1 + } + + s := success / ac.successThreshold + if ac.mode == logInactive { + log.Infof("%s: total < s = %v, rejectP = (%0.2f - %0.2f) / (%0.2f + 1) --- success: %0.2f and threshold: %0.2f", filters.AdmissionControlName, total < s, total, s, total, success, ac.successThreshold) + } + if total < s { + return -1 + } + rejectP = (total - s) / (total + 1) + rejectP = math.Pow(rejectP, ac.exponent) + + rejectP = math.Min(rejectP, ac.maxRejectProbability) + return math.Max(rejectP, 0.0) +} + +func (ac *admissionControl) shouldReject() bool { + p := ac.pReject() // [0, ac.maxRejectProbability] and -1 to disable + /* #nosec */ + r := rand.Float64() // [0,1) + + if ac.mode == logInactive { + log.Infof("%s: p: %0.2f, r: %0.2f", filters.AdmissionControlName, p, r) + } + + return p > r +} + +func (ac *admissionControl) Request(ctx filters.FilterContext) { + span := ac.startSpan(ctx.Request().Context()) + defer span.Finish() + + if ac.shouldReject() { + ac.metrics.IncCounter(counterPrefix + ac.metricSuffix) + ext.Error.Set(span, true) + + ctx.StateBag()[admissionControlKey] = admissionControlValue + + // shadow mode to measure data + if ac.mode != active { + return + } + ctx.Serve(&http.Response{ + StatusCode: http.StatusServiceUnavailable, + }) + } +} + +func (ac *admissionControl) Response(ctx filters.FilterContext) { + // we don't want to count ourselves + if ctx.StateBag()[admissionControlKey] == admissionControlValue { + return + } + + code := ctx.Response().StatusCode + if code < 499 { + ac.successCounter.Inc() + } + ac.counter.Inc() +} + +func (ac *admissionControl) startSpan(ctx context.Context) (span opentracing.Span) { + parent := opentracing.SpanFromContext(ctx) + if parent != nil { + span = ac.tracer.StartSpan(admissionControlSpanName, opentracing.ChildOf(parent.Context())) + ext.Component.Set(span, "skipper") + ext.SpanKind.Set(span, "shedder") + span.SetTag("mode", ac.mode.String()) + } + return +} diff --git a/filters/shedder/admission_test.go b/filters/shedder/admission_test.go new file mode 100644 index 0000000000..4675ed55f7 --- /dev/null +++ b/filters/shedder/admission_test.go @@ -0,0 +1,463 @@ +package shedder + +import ( + "fmt" + "math/rand" + "net/http" + "net/http/httptest" + "net/url" + "sort" + "testing" + "time" + + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/builtin" + "github.com/zalando/skipper/net" + "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/routing/testdataclient" +) + +func TestAdmissionControl(t *testing.T) { + for _, ti := range []struct { + msg string + mode string + d time.Duration + windowsize int + minRequests int + successThreshold float64 + maxrejectprobability float64 + exponent float64 + N int // iterations + pBackendErr float64 // [0,1] + pExpectedAdmissionShedding float64 // [0,1] + }{{ + msg: "no error", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.9, + maxrejectprobability: 0.95, + exponent: 1.0, + N: 20, + pBackendErr: 0.0, + pExpectedAdmissionShedding: 0.0, + }, { + msg: "only errors", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.9, + maxrejectprobability: 0.95, + exponent: 1.0, // 1000.0 + N: 20, + pBackendErr: 1.0, + pExpectedAdmissionShedding: 0.95, + }, { + msg: "smaller error rate, than threshold won't block", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.9, + maxrejectprobability: 0.95, + exponent: 1.0, + N: 20, + pBackendErr: 0.01, + pExpectedAdmissionShedding: 0.0, + }, { + msg: "tiny error rate and bigger than threshold will block some traffic", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.99, + maxrejectprobability: 0.95, + exponent: 1.0, + N: 20, + pBackendErr: 0.1, + pExpectedAdmissionShedding: 0.1, + }, { + msg: "small error rate and bigger than threshold will block some traffic", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.9, + maxrejectprobability: 0.95, + exponent: 1.0, + N: 20, + pBackendErr: 0.2, + pExpectedAdmissionShedding: 0.1, + }, { + msg: "medium error rate and bigger than threshold will block some traffic", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.9, + maxrejectprobability: 0.95, + exponent: 1.0, + N: 20, + pBackendErr: 0.5, + pExpectedAdmissionShedding: 0.615, + }, { + msg: "large error rate and bigger than threshold will block some traffic", + mode: "active", + d: 10 * time.Millisecond, + windowsize: 5, + minRequests: 10, + successThreshold: 0.9, + maxrejectprobability: 0.95, + exponent: 1.0, + N: 20, + pBackendErr: 0.8, + pExpectedAdmissionShedding: 0.91, + }} { + t.Run(ti.msg, func(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + p := rand.Float64() + if p < ti.pBackendErr { + w.WriteHeader(http.StatusInternalServerError) + } else { + w.WriteHeader(http.StatusOK) + } + })) + + spec := NewAdmissionControl(Options{}) + args := make([]interface{}, 0, 6) + args = append(args, "testmetric", ti.mode, ti.d.String(), ti.windowsize, ti.minRequests, ti.successThreshold, ti.maxrejectprobability, ti.exponent) + _, err := spec.CreateFilter(args) + if err != nil { + t.Logf("args: %+v", args...) + t.Fatalf("error creating filter: %v", err) + return + } + + fr := make(filters.Registry) + fr.Register(spec) + r := &eskip.Route{Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, Backend: backend.URL} + + proxy := proxytest.New(fr, r) + reqURL, err := url.Parse(proxy.URL) + if err != nil { + t.Fatalf("Failed to parse url %s: %v", proxy.URL, err) + } + + client := net.NewClient(net.Options{}) + req, err := http.NewRequest("GET", reqURL.String(), nil) + if err != nil { + t.Error(err) + return + } + + var failBackend, fail, ok, N float64 + // iterations to make sure we have enough traffic + until := time.After(time.Duration(ti.N) * time.Duration(ti.windowsize) * ti.d) + work: + for { + select { + case <-until: + break work + default: + } + N++ + rsp, err := client.Do(req) + if err != nil { + t.Error(err) + } + switch rsp.StatusCode { + case http.StatusInternalServerError: + failBackend += 1 + case http.StatusServiceUnavailable: + fail += 1 + case http.StatusOK: + ok += 1 + default: + t.Logf("Unexpected status code %d %s", rsp.StatusCode, rsp.Status) + } + rsp.Body.Close() + } + t.Logf("ok: %0.2f, fail: %0.2f, failBackend: %0.2f", ok, fail, failBackend) + + epsilon := 0.05 * N // maybe 5% instead of 0.1% + expectedFails := (N - failBackend) * ti.pExpectedAdmissionShedding + + if expectedFails-epsilon > fail || fail > expectedFails+epsilon { + t.Errorf("Failed to get expected fails should be in: %0.2f < %0.2f < %0.2f", expectedFails-epsilon, fail, expectedFails+epsilon) + } + + // TODO(sszuecs) how to calculate expected oks? + // expectedOKs := N - (N-failBackend)*ti.pExpectedAdmissionShedding + // if ok < expectedOKs-epsilon || expectedOKs+epsilon < ok { + // t.Errorf("Failed to get expected ok should be in: %0.2f < %0.2f < %0.2f", expectedOKs-epsilon, ok, expectedOKs+epsilon) + // } + }) + } +} + +func TestAdmissionControlCleanupModes(t *testing.T) { + for _, ti := range []struct { + msg string + mode string + }{{ + msg: "cleanup works for active mode", + mode: active.String(), + }, { + msg: "cleanup works for inactive mode", + mode: inactive.String(), + }, { + msg: "cleanup works for inactiveLog mode", + mode: logInactive.String(), + }} { + t.Run(ti.msg, func(t *testing.T) { + ch := make(chan tuple) + validationPostProcessor := &validationPostProcessorClosedFilter{ + ch: ch, + } + backend1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + backend2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + fspec := NewAdmissionControl(Options{}) + spec, ok := fspec.(*AdmissionControlSpec) + if !ok { + t.Fatal("FilterSpec is not a AdmissionControlSpec") + } + preProcessor := spec.PreProcessor() + postProcessor := spec.PostProcessor() + + args := make([]interface{}, 0, 6) + args = append(args, "testmetric", ti.mode, "10ms", 5, 1, 0.1, 0.95, 0.5) + _, err := spec.CreateFilter(args) + if err != nil { + t.Fatalf("error creating filter: %v", err) + return + } + + fr := make(filters.Registry) + fr.Register(spec) + + r1 := &eskip.Route{ + Id: "r1", + Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, + Backend: backend1.URL, + } + r2 := &eskip.Route{ + Id: "r2", + Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, + Backend: backend2.URL, + } + + dc := testdataclient.New([]*eskip.Route{r1}) + proxy := proxytest.WithRoutingOptions(fr, routing.Options{ + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{postProcessor, validationPostProcessor}, + PreProcessors: []routing.PreProcessor{preProcessor}, + }, nil) + defer proxy.Close() + + var tuple tuple + var deletedIDs []string + + // first does not need update, it's the dataclient triggered load that runs the processors + tuple = <-ch + if tuple.id != r1.Id { + t.Fatalf("Failed to get route got: %s", tuple.id) + } + + // delete route triggers closing filter, add r2 works + deletedIDs = []string{r1.Id} + dc.Update([]*eskip.Route{r2}, deletedIDs) + tuple = <-ch + if tuple.id != r2.Id { + t.Fatalf("Failed to get route %s, got: %s", r2.Id, tuple.id) + } + if !tuple.closed { + t.Errorf("Deleted filter should be closed routeID: %s", deletedIDs[0]) + } + + // preProcessor will only apply one filter in r2 (last wins) + r2.Filters = append(r2.Filters, r2.Filters...) + dc.Update([]*eskip.Route{r1, r2}, nil) + tuple = <-ch + tuple2 := <-ch + if tuple2.id == r2.Id { + tuple = tuple2 + } + if tuple.id != r2.Id { + t.Fatalf("Failed to cleanup preprocessor %s should be there", r2.Id) + } + // reset r2 + r2.Filters = []*eskip.Filter{r2.Filters[0]} + + // delete r2 triggers closing and r1 exists + deletedIDs = []string{r2.Id} + dc.Update([]*eskip.Route{}, deletedIDs) + tuple = <-ch + if tuple.id != r1.Id { + t.Fatalf("Failed to delete route %s, got: '%q'", r2.Id, tuple.id) + } + if !tuple.closed { + t.Error("old filter should be closed") + } + + // delete r1 triggers closing + deletedIDs = []string{r1.Id} + dc.Update([]*eskip.Route{}, deletedIDs) + tuple = <-ch + if !tuple.closed { + t.Error("old filter should be closed") + } + }) + } +} + +type tuple struct { + id string + closed bool +} +type validationPostProcessorClosedFilter struct { + ch chan tuple + oldFilter *admissionControl +} + +// Do validates if admissioncontrol filter exists in at least one +// route. It sends a tuple of route ID and closed state of found +// admissionControl filter through the channel. Empty string if not +// found. True if closed. +func (vpp *validationPostProcessorClosedFilter) Do(routes []*routing.Route) []*routing.Route { + found := false + + sort.Slice(routes, func(i, j int) bool { + return routes[i].Id < routes[j].Id + }) + for _, r := range routes { + for _, f := range r.Filters { + if ac, ok := f.Filter.(*admissionControl); ok { + found = true + + closed := false + if vpp.oldFilter != nil { + closed = vpp.oldFilter.closed + } + go func(id string, b bool) { + vpp.ch <- tuple{id: id, closed: closed} + }(r.Id, closed) + + vpp.oldFilter = ac + } + } + } + + if !found { + go func() { vpp.ch <- tuple{id: "", closed: vpp.oldFilter.closed} }() + } + return routes +} + +func TestAdmissionControlCleanupMultipleFilters(t *testing.T) { + for _, ti := range []struct { + msg string + doc string + }{{ + msg: "no filter", + doc: `* -> "%s"`, + }, { + msg: "one not matching filter", + doc: `* -> setRequestHeader("Foo", "bar") -> "%s"`, + }, { + msg: "one matching filter", + doc: `* -> admissionControl("app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) -> "%s"`, + }, { + msg: "two matching filters", + doc: `* -> admissionControl("app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) -> admissionControl("app2", "active", "1s", 5, 10, 0.1, 0.95, 0.5) -> "%s"`, + }, { + msg: "multi filter with multiple matching filters", + doc: `* -> setRequestHeader("Foo", "bar") -> admissionControl("app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) -> status(200) -> admissionControl("app2", "active", "1s", 5, 10, 0.1, 0.95, 0.5) -> setRequestHeader("Foo2", "bar2") -> admissionControl("app3", "active", "1s", 5, 10, 0.1, 0.95, 0.5) -> setRequestHeader("Foo3", "bar3") -> "%s"`, + }} { + t.Run(ti.msg, func(t *testing.T) { + ch := make(chan []*routing.Route) + validationProcessor := &validationPostProcessorNumberOfFilters{ + ch: ch, + } + + backend1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + fspec := NewAdmissionControl(Options{}) + spec, ok := fspec.(*AdmissionControlSpec) + if !ok { + t.Fatal("FilterSpec is not a AdmissionControlSpec") + } + preProcessor := spec.PreProcessor() + postProcessor := spec.PostProcessor() + + r, err := eskip.Parse(fmt.Sprintf(ti.doc, backend1.URL)) + if err != nil { + t.Fatalf("Failed to parse doc: %v", err) + } + + fr := make(filters.Registry) + fr.Register(fspec) + fr.Register(builtin.NewSetRequestHeader()) + fr.Register(builtin.NewStatus()) + + dc := testdataclient.New(r) + proxy := proxytest.WithRoutingOptions(fr, routing.Options{ + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{postProcessor, validationProcessor}, + PreProcessors: []routing.PreProcessor{preProcessor}, + }, r...) + defer proxy.Close() + + dc.Update(r, nil) + + result := <-ch + if result == nil { + t.Error("Failed to cleanup filters correctly, found more than one admissionControl filter in one route") + } + }) + } +} + +type validationPostProcessorNumberOfFilters struct { + ch chan []*routing.Route +} + +// Do validates if number of admissionControl filters are less than +// one for each route passed, if so it returns routes as it is +// if not it returns nil. +func (vpp *validationPostProcessorNumberOfFilters) Do(routes []*routing.Route) []*routing.Route { + i := 0 + for _, r := range routes { + j := 0 + for _, f := range r.Filters { + if _, ok := f.Filter.(*admissionControl); ok { + j++ + } + } + i = max(i, j) + } + + if i > 1 { + go func() { vpp.ch <- nil }() + return nil + } + go func() { vpp.ch <- routes }() + return routes +} + +func max(i, j int) int { + if i > j { + return i + } + return j +} diff --git a/proxy/proxytest/proxytest.go b/proxy/proxytest/proxytest.go index 23d69de350..407281aac1 100644 --- a/proxy/proxytest/proxytest.go +++ b/proxy/proxytest/proxytest.go @@ -39,7 +39,7 @@ func newTestProxy(fr filters.Registry, routingOptions routing.Options, proxyPara routingOptions.FilterRegistry = fr routingOptions.Log = tl - routingOptions.PostProcessors = []routing.PostProcessor{loadbalancer.NewAlgorithmProvider()} + routingOptions.PostProcessors = append(routingOptions.PostProcessors, loadbalancer.NewAlgorithmProvider()) rt := routing.New(routingOptions) proxyParams.Routing = rt diff --git a/skipper.go b/skipper.go index e1e0d14cef..5ee129caf4 100644 --- a/skipper.go +++ b/skipper.go @@ -35,6 +35,7 @@ import ( "github.com/zalando/skipper/filters/fadein" logfilter "github.com/zalando/skipper/filters/log" ratelimitfilters "github.com/zalando/skipper/filters/ratelimit" + "github.com/zalando/skipper/filters/shedder" "github.com/zalando/skipper/innkeeper" "github.com/zalando/skipper/loadbalancer" "github.com/zalando/skipper/logging" @@ -1342,6 +1343,14 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { Tracer: tracer, } + admissionControlFilter := shedder.NewAdmissionControl(shedder.Options{ + Tracer: tracer, + }) + admissionControlSpec, ok := admissionControlFilter.(*shedder.AdmissionControlSpec) + if !ok { + log.Fatal("Failed to cast admission control filter to spec") + } + o.CustomFilters = append(o.CustomFilters, logfilter.NewAuditLog(o.MaxAuditBody), auth.NewBearerInjector(sp), @@ -1365,6 +1374,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { o.ApiUsageMonitoringClientKeys, o.ApiUsageMonitoringRealmsTrackingPattern, ), + admissionControlFilter, ) var swarmer ratelimit.Swarmer @@ -1592,6 +1602,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { schedulerRegistry, builtin.NewRouteCreationMetrics(mtr), fadein.NewPostProcessor(), + admissionControlSpec.PostProcessor(), }, SignalFirstLoad: o.WaitFirstRouteLoad, } @@ -1618,6 +1629,8 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ro.PreProcessors = append(ro.PreProcessors, o.CustomRoutingPreProcessors...) } + ro.PreProcessors = append(ro.PreProcessors, admissionControlSpec.PreProcessor()) + routing := routing.New(ro) defer routing.Close() From bf70549179e716e796b81dcad3a7cadf46e96f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 6 Jul 2022 13:21:25 +0200 Subject: [PATCH 0052/1047] doc: route creation (#2037) --- docs/tutorials/basics.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/basics.md b/docs/tutorials/basics.md index e2741fba0e..bce8f47dbd 100644 --- a/docs/tutorials/basics.md +++ b/docs/tutorials/basics.md @@ -221,7 +221,7 @@ The route matching logic can be summed up as follows: 2. _If_ step #1 matches multiple routes, which means there are multiple routes in the same position of the path tree, and all other predicates - match the request, too, then the route with the highest + match the request, too, then the route with the highest [weight](../reference/predicates.md#weight) is matched. * this is an O(n) lookup, but only on the same leaf @@ -235,6 +235,31 @@ The route matching logic can be summed up as follows: See more details about the predicates here: [Predicates](../reference/predicates.md). +### Route creation + +Skipper has two kind of routes: + +1. [eskip.Route](https://pkg.go.dev/github.com/zalando/skipper/eskip#Route) +2. [routing.Route](https://pkg.go.dev/github.com/zalando/skipper/routing#Route) + +An `eskip.Route` is the parsed representation of user input. This will +be converted to a `routing.Route`, when the routing table is built. A +tree of `routing.Route` will be used to match an incoming Request to a route. + +Route creation steps: + +1. Skipper's route creation starts with the [Dataclient](https://pkg.go.dev/github.com/zalando/skipper/routing#DataClient) + to fetch routes (`[]*eskip.Route`). +2. These will be first processed by + `[]routing.PreProcessor`. [PreProcessors](https://pkg.go.dev/github.com/zalando/skipper/routing#PreProcessor) are able to add, remove, + modify all `[]*eskip.Route`. +3. After that `[]*eskip.Route` are converted to `[]*routing.Route`. +4. `[]routing.PostProcessor` are executed. [PostProcessors](https://pkg.go.dev/github.com/zalando/skipper/routing#PostProcessor) are a ble to + add, remove, modify all `[]*routing.Route`. +5. Last the active routing table is swapped. Now all incoming requests + are handled by the new routing table + + ## Building skipper We use Go modules to build skipper, therefore you need [Go](https://golang.org/dl) version `>= 1.11`. From 57c4e6e3b6dad259b809bfe6025154c3dd5da206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 8 Jul 2022 10:13:40 +0200 Subject: [PATCH 0053/1047] feature: add cgroup v2 memory limit detection (#2039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- skipper.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/skipper.go b/skipper.go index 5ee129caf4..72a8147ab7 100644 --- a/skipper.go +++ b/skipper.go @@ -101,9 +101,13 @@ type Options struct { // It defines the expected average memory required to process an incoming // request. It is used only when MaxTCPListenerConcurrency is not defined. // It is used together with the memory limit defined in: - // /sys/fs/cgroup/memory/memory.limit_in_bytes. + // cgroup v1 /sys/fs/cgroup/memory/memory.limit_in_bytes + // or + // cgroup v2 /sys/fs/cgroup/memory.max // - // See also: https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt + // See also: + // cgroup v1: https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt + // cgroup v2: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files ExpectedBytesPerRequest int // MaxTCPListenerConcurrency is used by the TCP LIFO listener. @@ -1057,14 +1061,21 @@ func listen(o *Options, mtr metrics.Metrics) (net.Listener, error) { var memoryLimit int if o.MaxTCPListenerConcurrency <= 0 { // cgroup v1: https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt - // cgroup v2: TODO(sszuecs) has to wait for docker/k8s check path /sys/fs/cgroup//memory.max + // cgroup v2: https://www.kernel.org/doc/Documentation/cgroup-v2.txt // Note that in containers this will be the container limit. - // Runtimes without the file will use defaults defined in `queuelistener` package. - const memoryLimitFile = "/sys/fs/cgroup/memory/memory.limit_in_bytes" - memoryLimitBytes, err := os.ReadFile(memoryLimitFile) + // Runtimes without these files will use defaults defined in `queuelistener` package. + const ( + memoryLimitFileV1 = "/sys/fs/cgroup/memory/memory.limit_in_bytes" + memoryLimitFileV2 = "/sys/fs/cgroup/memory.max" + ) + memoryLimitBytes, err := os.ReadFile(memoryLimitFileV2) if err != nil { - log.Errorf("Failed to read memory limits, fallback to defaults: %v", err) - } else { + memoryLimitBytes, err = os.ReadFile(memoryLimitFileV1) + if err != nil { + log.Errorf("Failed to read memory limits, fallback to defaults: %v", err) + } + } + if err == nil { memoryLimitString := strings.TrimSpace(string(memoryLimitBytes)) memoryLimit, err = strconv.Atoi(memoryLimitString) if err != nil { From 5b6a9e8b84acd89626d28d5e4293a7a9e0c00f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 8 Jul 2022 10:13:59 +0200 Subject: [PATCH 0054/1047] build static binary as make target (#2040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 8a49633281..7de5d11997 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,9 @@ endif build: $(SOURCES) lib skipper eskip webhook routesrv +build.linux.static: + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-extldflags=-static -X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper + build.linux.arm64: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/skipper -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" ./cmd/skipper From cdd35f1847125a755e415ef3baa21bbe01f1a939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 8 Jul 2022 10:24:09 +0200 Subject: [PATCH 0055/1047] update dependencies - containerd (#2035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update dependencies https://github.com/zalando/skipper/security/dependabot/6 Signed-off-by: Sandor Szücs * update and tidy Signed-off-by: Sandor Szücs --- go.mod | 48 +++++++-------- go.sum | 186 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 182 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 5a0b0abe8a..aa8a130f76 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_golang v1.11.1 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/sanity-io/litter v1.5.2 github.com/sarslanhan/cronmask v0.0.0-20190709075623-766eca24d011 @@ -46,12 +46,12 @@ require ( golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d + golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.21.4 - k8s.io/apimachinery v0.21.4 + k8s.io/api v0.22.5 + k8s.io/apimachinery v0.22.5 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf ) @@ -59,35 +59,35 @@ require ( cloud.google.com/go v0.65.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect - github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 // indirect - github.com/Microsoft/hcsshim v0.8.16 // indirect + github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/Microsoft/hcsshim v0.9.3 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 // indirect - github.com/containerd/containerd v1.5.0-beta.4 // indirect + github.com/containerd/cgroups v1.0.3 // indirect + github.com/containerd/containerd v1.6.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.0+incompatible // indirect github.com/docker/docker v20.10.11+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/go-logr/logr v0.4.0 // indirect + github.com/go-logr/logr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.0.0 // indirect github.com/hashicorp/go-msgpack v0.5.3 // indirect - github.com/hashicorp/go-multierror v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-sockaddr v1.0.0 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect github.com/looplab/fsm v0.3.0 // indirect github.com/magiconair/properties v1.8.5 // indirect @@ -96,36 +96,36 @@ require ( github.com/moby/sys/mountinfo v0.5.0 // indirect github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/opencontainers/runc v1.1.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect + github.com/prometheus/common v0.30.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/shirou/gopsutil/v3 v3.21.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/tklauser/numcpus v0.3.0 // indirect - go.opencensus.io v0.22.4 // indirect + go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect google.golang.org/grpc v1.43.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.8.0 // indirect + k8s.io/klog/v2 v2.30.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect ) diff --git a/go.sum b/go.sum index dff55d1b66..1496855046 100644 --- a/go.sum +++ b/go.sum @@ -67,16 +67,21 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 h1:mw6pDQqv38/WGF1cO/jF5t/jyAJ2yi7CmtFLLO5tGFI= github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.9.3 h1:k371PzBuRrz2b+ebGuI2nVgVhgsVX60jMfSw80NECxo= +github.com/Microsoft/hcsshim v0.9.3/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -132,6 +137,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -146,6 +152,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 h1:rdWOzitWlNYeUsXmz+IQfa9NkGEq3gA/qQ3mOEqBU6o= @@ -156,6 +163,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -163,15 +171,19 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 h1:hkGVFjz+plgr5UfxZUTPFbUFIF/Km6/s+RVRIRHLrrY= github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= +github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= @@ -188,50 +200,69 @@ github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4 h1:zjz4MOAOFgdBlwid2nNUlJ3YLpVi/97L36lfMYJex60= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.6.6 h1:xJNPhbrmz8xAMDNoVjHy9YHtWwEQNS+CDkcIRh7t8Y0= +github.com/containerd/containerd v1.6.6/go.mod h1:ZoP1geJldzCVY3Tonoz7b1IXk8rIX0Nltt5QE4OMNk0= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= 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= @@ -278,14 +309,17 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY= github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -307,10 +341,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -337,8 +373,10 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.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 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= @@ -419,8 +457,9 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -428,6 +467,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -457,13 +497,16 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/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.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/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.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -477,6 +520,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -484,12 +528,15 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -501,15 +548,17 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= @@ -526,6 +575,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/instana/go-sensor v1.38.3 h1:/PdHEDveLmUCvK+O6REvSv8kKljX8vaj9JMjMeCHAWk= github.com/instana/go-sensor v1.38.3/go.mod h1:E42MelHWFz11qqaLwvgt0j98v2s2O/bq22UDkGaG0Gg= @@ -534,14 +584,16 @@ github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65/go.mod h1:nYhERE github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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 v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -553,6 +605,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -569,6 +622,7 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7/go.mod h1:Spd59icnvRxSKuyijbbwe5AemzvcyXAUBgApa7VybMw= github.com/lightstep/lightstep-tracer-go v0.25.0 h1:sGVnz8h3jTQuHKMbUe2949nXm3Sg09N1UcR3VoQNN5E= github.com/lightstep/lightstep-tracer-go v0.25.0/go.mod h1:G1ZAEaqTHFPWpWunnbUn1ADEY/Jvzz7jIOaXwAfD6A8= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI= github.com/looplab/fsm v0.3.0 h1:kIgNS3Yyud1tyxhG8kDqh853B7QqwnlWdgL3TD2s3Sw= github.com/looplab/fsm v0.3.0/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= @@ -588,9 +642,11 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.45 h1:g5fRIhm9nx7g8osrAvgb16QJfmyMsyOCb+J7LSv+Qzk= github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= @@ -599,6 +655,7 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= @@ -614,10 +671,12 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -637,19 +696,25 @@ github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 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.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -660,8 +725,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= +github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -691,6 +756,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -708,8 +774,9 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -722,8 +789,9 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -734,8 +802,9 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -750,6 +819,7 @@ github.com/sanity-io/litter v1.5.2/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFW github.com/sarslanhan/cronmask v0.0.0-20190709075623-766eca24d011 h1:S5j3KTsiGwmQSEJJBp0iIG87CDBCGCwbYLmVv8L/nuE= github.com/sarslanhan/cronmask v0.0.0-20190709075623-766eca24d011/go.mod h1:NmI1tg7wwsf1hF6G5EtyGCrtNKsH2RIdYYoJa7GsnP8= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= @@ -793,6 +863,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -845,8 +916,10 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -869,6 +942,7 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -879,13 +953,15 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -907,6 +983,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -989,14 +1066,19 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1004,6 +1086,7 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1042,6 +1125,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1066,6 +1150,7 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1073,6 +1158,7 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1084,8 +1170,10 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1099,17 +1187,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1117,6 +1209,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -1124,9 +1217,10 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1149,6 +1243,7 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1176,6 +1271,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1183,9 +1279,12 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= @@ -1220,8 +1319,9 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1252,12 +1352,15 @@ google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1277,6 +1380,7 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1330,6 +1434,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -1346,27 +1452,44 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.21.4 h1:WtDkzTAuI31WZKDPeIYpEUA+WeUfXAmA7gwj6nzFfbc= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= +k8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8= +k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.21.4 h1:KDq0lWZVslHkuE5I7iGAQHwpK0aDTlar1E7IWEc4CNw= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.21.4/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= +k8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s= +k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.21.4/go.mod h1:t0/eMKyUAq/DoQ7vW8NVVA00/nomlwC+eInsS8PxSew= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/code-generator v0.21.4/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -1374,11 +1497,15 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1390,10 +1517,13 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-tools v0.4.0/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= From a0fa5d8e99117d5a43b84c4948cf819c16715efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 12 Jul 2022 13:01:11 +0200 Subject: [PATCH 0056/1047] enable math rendering (#2041) --- mkdocs.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 151bbb5130..31cb3fbc45 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,13 @@ markdown_extensions: - pymdownx.superfences - pymdownx.details - pymdownx.mark +- pymdownx.arithmatex: + generic: true + +extra_javascript: + - javascripts/mathjax.js + - https://polyfill.io/v3/polyfill.min.js?features=es6 + - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js plugins: - search From f1ae3f03b2c568ea4131885e01a762578a53fe79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 26 Jul 2022 17:03:33 +0200 Subject: [PATCH 0057/1047] feature: dynamic detect and resize redis instances (#2009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: dynamic detect and resize redis instances In case of kubernetes dataclient we can detect the number of redis shards available and trigger an update to the redis ringclient. In order to use this feature we have to use a forked version of go-redis until it is merged and released https://github.com/go-redis/redis/pull/2093 Signed-off-by: Sandor Szücs --- config/config.go | 9 + config/config_test.go | 1 + dataclients/kubernetes/clusterclient.go | 3 +- dataclients/kubernetes/clusterstate.go | 12 +- dataclients/kubernetes/ingressdefinitions.go | 4 +- dataclients/kubernetes/ingressv1.go | 4 +- dataclients/kubernetes/ingressv1beta1.go | 4 +- dataclients/kubernetes/kube.go | 15 ++ dataclients/kubernetes/routegroup.go | 2 +- go.mod | 10 +- go.sum | 23 +- net/redisclient.go | 108 +++++++++- net/redisclient_test.go | 211 ++++++++++++++++++- net/redistest/redistest.go | 2 +- ratelimit/registry.go | 8 +- skipper.go | 53 ++++- 16 files changed, 417 insertions(+), 52 deletions(-) diff --git a/config/config.go b/config/config.go index 9104a7c2bc..8cc571486c 100644 --- a/config/config.go +++ b/config/config.go @@ -167,6 +167,9 @@ type Config struct { KubernetesEastWestRangePredicates []*eskip.Predicate `yaml:"-"` KubernetesOnlyAllowedExternalNames bool `yaml:"kubernetes-only-allowed-external-names"` KubernetesAllowedExternalNames regexpListFlag `yaml:"kubernetes-allowed-external-names"` + KubernetesRedisServiceNamespace string `yaml:"kubernetes-redis-service-namespace"` + KubernetesRedisServiceName string `yaml:"kubernetes-redis-service-name"` + KubernetesRedisServicePort int `yaml:"kubernetes-redis-service-port"` // Default filters DefaultFiltersDir string `yaml:"default-filters-dir"` @@ -430,6 +433,9 @@ func NewConfig() *Config { flag.StringVar(&cfg.KubernetesEastWestRangePredicatesString, "kubernetes-east-west-range-predicates", "", "set the predicates that will be appended to routes identified as to -kubernetes-east-west-range-domains") flag.BoolVar(&cfg.KubernetesOnlyAllowedExternalNames, "kubernetes-only-allowed-external-names", false, "only accept external name services, route group network backends and route group explicit LB endpoints from an allow list defined by zero or more -kubernetes-allowed-external-name flags") flag.Var(&cfg.KubernetesAllowedExternalNames, "kubernetes-allowed-external-name", "set zero or more regular expressions from which at least one should be matched by the external name services, route group network addresses and explicit endpoints domain names") + flag.StringVar(&cfg.KubernetesRedisServiceNamespace, "kubernetes-redis-service-namespace", "", "Sets namespace for redis to be used to lookup endpoints") + flag.StringVar(&cfg.KubernetesRedisServiceName, "kubernetes-redis-service-name", "", "Sets name for redis to be used to lookup endpoints") + flag.IntVar(&cfg.KubernetesRedisServicePort, "kubernetes-redis-service-port", 6379, "Sets the port for redis to be used to lookup endpoints") // Auth: flag.BoolVar(&cfg.EnableOAuth2GrantFlow, "enable-oauth2-grant-flow", false, "enables OAuth2 Grant Flow filter") @@ -775,6 +781,9 @@ func (c *Config) ToOptions() skipper.Options { KubernetesEastWestRangePredicates: c.KubernetesEastWestRangePredicates, KubernetesOnlyAllowedExternalNames: c.KubernetesOnlyAllowedExternalNames, KubernetesAllowedExternalNames: c.KubernetesAllowedExternalNames, + KubernetesRedisServiceNamespace: c.KubernetesRedisServiceNamespace, + KubernetesRedisServiceName: c.KubernetesRedisServiceName, + KubernetesRedisServicePort: c.KubernetesRedisServicePort, // API Monitoring: ApiUsageMonitoringEnable: c.ApiUsageMonitoringEnable, diff --git a/config/config_test.go b/config/config_test.go index 885d14b56a..1b39623838 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -121,6 +121,7 @@ func Test_NewConfig(t *testing.T) { KubernetesHTTPSRedirect: true, KubernetesHTTPSRedirectCode: 308, KubernetesPathModeString: "kubernetes-ingress", + KubernetesRedisServicePort: 6379, Oauth2TokeninfoTimeout: 2 * time.Second, Oauth2TokenintrospectionTimeout: 2 * time.Second, Oauth2TokeninfoSubjectKey: "uid", diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index cc43ecb0d4..f27326720e 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -438,7 +438,8 @@ func (c *clusterClient) loadEndpoints() (map[definitions.ResourceID]*endpoint, e log.Debugf("all endpoints received: %d", len(endpoints.Items)) result := make(map[definitions.ResourceID]*endpoint) for _, endpoint := range endpoints.Items { - result[endpoint.Meta.ToResourceID()] = endpoint + resID := endpoint.Meta.ToResourceID() + result[resID] = endpoint } return result, nil diff --git a/dataclients/kubernetes/clusterstate.go b/dataclients/kubernetes/clusterstate.go index da35c439e1..cfe5f74948 100644 --- a/dataclients/kubernetes/clusterstate.go +++ b/dataclients/kubernetes/clusterstate.go @@ -42,11 +42,11 @@ func (state *clusterState) getServiceRG(namespace, name string) (*service, error return s, nil } -func (state *clusterState) getEndpointsByService(namespace, name, protocol string, servicePort *servicePort) []string { +func (state *clusterState) GetEndpointsByService(namespace, name, protocol string, servicePort *servicePort) []string { epID := endpointID{ ResourceID: newResourceID(namespace, name), - protocol: protocol, - targetPort: servicePort.TargetPort.String(), + Protocol: protocol, + TargetPort: servicePort.TargetPort.String(), } if cached, ok := state.cachedEndpoints[epID]; ok { @@ -64,11 +64,11 @@ func (state *clusterState) getEndpointsByService(namespace, name, protocol strin return targets } -func (state *clusterState) getEndpointsByTarget(namespace, name, protocol string, target *definitions.BackendPort) []string { +func (state *clusterState) GetEndpointsByTarget(namespace, name, protocol string, target *definitions.BackendPort) []string { epID := endpointID{ ResourceID: newResourceID(namespace, name), - protocol: protocol, - targetPort: target.String(), + Protocol: protocol, + TargetPort: target.String(), } if cached, ok := state.cachedEndpoints[epID]; ok { diff --git a/dataclients/kubernetes/ingressdefinitions.go b/dataclients/kubernetes/ingressdefinitions.go index 431c57ba94..76fb0631ba 100644 --- a/dataclients/kubernetes/ingressdefinitions.go +++ b/dataclients/kubernetes/ingressdefinitions.go @@ -160,8 +160,8 @@ func newResourceID(namespace, name string) definitions.ResourceID { type endpointID struct { definitions.ResourceID - targetPort string - protocol string + TargetPort string + Protocol string } type ClusterResource struct { diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index d286e1e309..8befeb42f6 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -84,7 +84,7 @@ func convertPathRuleV1( protocol = p } - eps = state.getEndpointsByService(ns, svcName, protocol, servicePort) + eps = state.GetEndpointsByService(ns, svcName, protocol, servicePort) log.Debugf("convertPathRuleV1: Found %d endpoints %s for %s", len(eps), servicePort, svcName) } if len(eps) == 0 { @@ -373,7 +373,7 @@ func (ing *ingress) convertDefaultBackendV1( protocol = p } - eps = state.getEndpointsByService( + eps = state.GetEndpointsByService( ns, svcName, protocol, diff --git a/dataclients/kubernetes/ingressv1beta1.go b/dataclients/kubernetes/ingressv1beta1.go index 649e6997e4..cb81148c75 100644 --- a/dataclients/kubernetes/ingressv1beta1.go +++ b/dataclients/kubernetes/ingressv1beta1.go @@ -84,7 +84,7 @@ func convertPathRule( protocol = p } - eps = state.getEndpointsByService(ns, svcName, protocol, servicePort) + eps = state.GetEndpointsByService(ns, svcName, protocol, servicePort) log.Debugf("convertPathRule: Found %d endpoints %s for %s", len(eps), servicePort, svcName) } if len(eps) == 0 { @@ -358,7 +358,7 @@ func (ing *ingress) convertDefaultBackend( protocol = p } - eps = state.getEndpointsByService( + eps = state.GetEndpointsByService( ns, svcName, protocol, diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 492197c195..20ac0ef3ce 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -11,6 +11,7 @@ import ( "time" log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/dataclients/kubernetes/definitions" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/secrets/certregistry" @@ -197,6 +198,7 @@ type Client struct { current map[string]*eskip.Route quit chan struct{} defaultFiltersDir string + state *clusterState } // New creates and initializes a Kubernetes DataClient. @@ -334,6 +336,7 @@ func (c *Client) loadAndConvert() ([]*eskip.Route, error) { if err != nil { return nil, err } + c.state = state defaultFilters := c.fetchDefaultFilterConfigs() @@ -491,6 +494,18 @@ func (c *Client) fetchDefaultFilterConfigs() defaultFilters { return filters } +func (c *Client) GetEndpointAddresses(ns, name string, port int) []string { + if c.state == nil { + return nil + } + + addrs := c.state.GetEndpointsByTarget(ns, name, "TCP", &definitions.BackendPort{ + Value: port, + }) + + return addrs +} + func compareStringList(a, b []string) []string { c := make([]string, 0) for i := len(a) - 1; i >= 0; i-- { diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index fa480e7914..aae64c09c6 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -254,7 +254,7 @@ func applyServiceBackend(ctx *routeGroupContext, backend *definitions.SkipperBac return targetPortNotFound(backend.ServiceName, backend.ServicePort) } - eps := ctx.clusterState.getEndpointsByTarget( + eps := ctx.clusterState.GetEndpointsByTarget( namespaceString(ctx.routeGroup.Metadata.Namespace), s.Meta.Name, protocol, diff --git a/go.mod b/go.mod index aa8a130f76..05e411b6ce 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 github.com/ghodss/yaml v1.0.0 - github.com/go-redis/redis/v8 v8.11.4 + github.com/go-redis/redis/v9 v9.0.0-beta.1 github.com/golang-jwt/jwt/v4 v4.2.0 github.com/google/go-cmp v0.5.6 github.com/hashicorp/memberlist v0.3.0 @@ -42,11 +42,12 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible github.com/yookoala/gofast v0.6.0 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 + go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 + golang.org/x/net v0.0.0-20220225172249-27dd8689420f golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 @@ -113,7 +114,6 @@ require ( github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/tklauser/numcpus v0.3.0 // indirect go.opencensus.io v0.23.0 // indirect - go.uber.org/atomic v1.9.0 // indirect golang.org/x/mod v0.5.1 // indirect golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect golang.org/x/text v0.3.7 // indirect @@ -130,3 +130,5 @@ require ( ) go 1.18 + +replace github.com/go-redis/redis/v9 => github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220720205457-d1c17518c5ee diff --git a/go.sum b/go.sum index 1496855046..806a66a60a 100644 --- a/go.sum +++ b/go.sum @@ -425,13 +425,10 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= -github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-restit/lzjson v0.0.0-20161206095556-efe3c53acc68/go.mod h1:7vXSKQt83WmbPeyVjCfNT9YDJ5BUFmcwFsEjI9SCvYM= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -688,7 +685,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -703,8 +699,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -715,8 +710,7 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -883,6 +877,8 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/szuecs/rate-limit-buffer v0.7.1 h1:kpVLwDvpCTFQi8uhiXQrhAKWzNUaEKhArFdjb4GQ8F4= github.com/szuecs/rate-limit-buffer v0.7.1/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= +github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220720205457-d1c17518c5ee h1:2/YbVT6UC36rX9iiX4PXiT0bTCjzU197GqzA/nGxSUw= +github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220720205457-d1c17518c5ee/go.mod h1:6gNX1bXdwkpEG0M/hEBNK/Fp8zdyCkjwwKc6vBbfCDI= github.com/szuecs/routegroup-client v0.21.0 h1:XWLxK2OCs4lZeIIHOJVVaFpe7nFmJjM3r+fOsZ2eyWw= github.com/szuecs/routegroup-client v0.21.0/go.mod h1:GbR5pqdcJdiFx9aL2hoq1ghAMZ9E1qFKBf88w7vqyK0= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= @@ -1073,14 +1069,13 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= -golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1177,7 +1172,6 @@ golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1200,8 +1194,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1280,7 +1274,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= diff --git a/net/redisclient.go b/net/redisclient.go index 42c221d4c0..cdb7a384ce 100644 --- a/net/redisclient.go +++ b/net/redisclient.go @@ -4,10 +4,11 @@ import ( "context" "fmt" "log" + "sync" "time" "github.com/cenkalti/backoff" - "github.com/go-redis/redis/v8" + "github.com/go-redis/redis/v9" "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/logging" "github.com/zalando/skipper/metrics" @@ -25,6 +26,16 @@ import ( type RedisOptions struct { // Addrs are the list of redis shards Addrs []string + + // AddrUpdater is a func that is regularly called to update + // redis address list. This func should return a list of redis + // shards. + AddrUpdater func() []string + + // UpdateInterval is the time.Duration that AddrUpdater is + // triggered and SetAddrs be used to update the redis shards + UpdateInterval time.Duration + // Password is the password needed to connect to Redis server Password string @@ -80,6 +91,8 @@ type RedisRingClient struct { options *RedisOptions tracer opentracing.Tracer quit chan struct{} + once sync.Once + closed bool } type RedisScript struct { @@ -101,6 +114,7 @@ const ( DefaultMaxConns = 100 defaultConnMetricsInterval = 60 * time.Second + defaultUpdateInterval = 10 * time.Second ) // https://arxiv.org/pdf/1406.2294.pdf @@ -184,10 +198,12 @@ func NewRendezvousVnodes(shards []string) redis.ConsistentHash { } func NewRedisRingClient(ro *RedisOptions) *RedisRingClient { - r := new(RedisRingClient) - r.quit = make(chan struct{}) - r.metrics = metrics.Default - r.tracer = &opentracing.NoopTracer{} + r := &RedisRingClient{ + once: sync.Once{}, + quit: make(chan struct{}), + metrics: metrics.Default, + tracer: &opentracing.NoopTracer{}, + } ringOptions := &redis.RingOptions{ Addrs: map[string]string{}, @@ -204,9 +220,15 @@ func NewRedisRingClient(ro *RedisOptions) *RedisRingClient { ringOptions.NewConsistentHash = NewMultiprobe } - for idx, addr := range ro.Addrs { - ringOptions.Addrs[fmt.Sprintf("redis%d", idx)] = addr + if ro.AddrUpdater != nil { + ringOptions.Addrs = createAddressMap(ro.AddrUpdater()) + } else { + ringOptions.Addrs = createAddressMap(ro.Addrs) } + if ro.Log == nil { + ro.Log = &logging.DefaultLog{} + } + ro.Log.Infof("create ring with addresses: %v", ro.Addrs) ringOptions.ReadTimeout = ro.ReadTimeout ringOptions.WriteTimeout = ro.WriteTimeout ringOptions.PoolTimeout = ro.PoolTimeout @@ -226,11 +248,67 @@ func NewRedisRingClient(ro *RedisOptions) *RedisRingClient { r.ring = redis.NewRing(ringOptions) r.log = ro.Log r.metricsPrefix = ro.MetricsPrefix + + if ro.AddrUpdater != nil { + if ro.UpdateInterval == 0 { + ro.UpdateInterval = defaultUpdateInterval + } + go r.startUpdater(context.Background()) + } } return r } +func createAddressMap(addrs []string) map[string]string { + res := make(map[string]string) + for _, addr := range addrs { + res[addr] = addr + } + return res +} + +func hasAll(a []string, set map[string]struct{}) bool { + if len(a) != len(set) { + return false + } + for _, w := range a { + if _, ok := set[w]; !ok { + return false + } + } + return true +} + +func (r *RedisRingClient) startUpdater(ctx context.Context) { + old := make(map[string]struct{}) + for _, addr := range r.options.Addrs { + old[addr] = struct{}{} + } + + r.log.Infof("Start goroutine to update redis instances every %s", r.options.UpdateInterval) + + for { + select { + case <-r.quit: + return + case <-time.After(r.options.UpdateInterval): + } + + addrs := r.options.AddrUpdater() + if !hasAll(addrs, old) { + r.log.Infof("Redis updater updating old(%d) != new(%d)", len(old), len(addrs)) + r.SetAddrs(ctx, addrs) + + old = make(map[string]struct{}) + for _, addr := range addrs { + old[addr] = struct{}{} + } + + } + } +} + func (r *RedisRingClient) RingAvailable() bool { var err error err = backoff.Retry(func() error { @@ -272,9 +350,17 @@ func (r *RedisRingClient) StartSpan(operationName string, opts ...opentracing.St } func (r *RedisRingClient) Close() { - if r != nil { + r.once.Do(func() { + r.closed = true close(r.quit) + }) +} + +func (r *RedisRingClient) SetAddrs(ctx context.Context, addrs []string) { + if len(addrs) == 0 { + return } + r.ring.SetAddrs(ctx, createAddressMap(addrs)) } func (r *RedisRingClient) Get(ctx context.Context, key string) (string, error) { @@ -288,7 +374,7 @@ func (r *RedisRingClient) Set(ctx context.Context, key string, value interface{} } func (r *RedisRingClient) ZAdd(ctx context.Context, key string, val int64, score float64) (int64, error) { - res := r.ring.ZAdd(ctx, key, &redis.Z{Member: val, Score: score}) + res := r.ring.ZAdd(ctx, key, redis.Z{Member: val, Score: score}) return res.Val(), res.Err() } @@ -313,13 +399,13 @@ func (r *RedisRingClient) ZCard(ctx context.Context, key string) (int64, error) } func (r *RedisRingClient) ZRangeByScoreWithScoresFirst(ctx context.Context, key string, min, max float64, offset, count int64) (interface{}, error) { - opt := &redis.ZRangeBy{ + opt := redis.ZRangeBy{ Min: fmt.Sprint(min), Max: fmt.Sprint(max), Offset: offset, Count: count, } - res := r.ring.ZRangeByScoreWithScores(ctx, key, opt) + res := r.ring.ZRangeByScoreWithScores(ctx, key, &opt) zs, err := res.Result() if err != nil { return nil, err diff --git a/net/redisclient_test.go b/net/redisclient_test.go index 5102070fc3..e648a40f58 100644 --- a/net/redisclient_test.go +++ b/net/redisclient_test.go @@ -10,6 +10,80 @@ import ( "github.com/zalando/skipper/tracing/tracers/basic" ) +func TestRedisContainer(t *testing.T) { + redisAddr, done := redistest.NewTestRedis(t) + defer done() + if redisAddr == "" { + t.Fatal("Failed to create redis 1") + } + redisAddr2, done2 := redistest.NewTestRedis(t) + defer done2() + if redisAddr2 == "" { + t.Fatal("Failed to create redis 2") + } +} + +func Test_hasAll(t *testing.T) { + for _, tt := range []struct { + name string + a []string + h map[string]struct{} + want bool + }{ + { + name: "both empty", + a: nil, + h: nil, + want: true, + }, + { + name: "a empty", + a: nil, + h: map[string]struct{}{ + "foo": {}, + }, + want: false, + }, + { + name: "h empty", + a: []string{"foo"}, + h: nil, + want: false, + }, + { + name: "both set equal", + a: []string{"foo"}, + h: map[string]struct{}{ + "foo": {}, + }, + want: true, + }, + { + name: "both set notequal", + a: []string{"fo"}, + h: map[string]struct{}{ + "foo": {}, + }, + want: false, + }, + { + name: "both set multiple equal", + a: []string{"bar", "foo"}, + h: map[string]struct{}{ + "foo": {}, + "bar": {}, + }, + want: true, + }} { + t.Run(tt.name, func(t *testing.T) { + got := hasAll(tt.a, tt.h) + if tt.want != got { + t.Fatalf("Failed to get %v for hasall(%v, %v)", tt.want, tt.a, tt.h) + } + }) + } +} + func TestRedisClient(t *testing.T) { tracer, err := basic.InitTracer([]string{"recorder=in-memory"}) if err != nil { @@ -18,6 +92,8 @@ func TestRedisClient(t *testing.T) { redisAddr, done := redistest.NewTestRedis(t) defer done() + redisAddr2, done2 := redistest.NewTestRedis(t) + defer done2() for _, tt := range []struct { name string @@ -31,6 +107,24 @@ func TestRedisClient(t *testing.T) { }, wantErr: false, }, + { + name: "With AddrUpdater", + options: &RedisOptions{ + AddrUpdater: func() []string { return []string{redisAddr} }, + // i := 0 + // return func() []string { + // i++ + // if i < 2 { + // return []string{redisAddr} + // } + // return []string{redisAddr, redisAddr2} + // }() + + // }, + UpdateInterval: 10 * time.Millisecond, + }, + wantErr: false, + }, { name: "With tracer", options: &RedisOptions{ @@ -50,17 +144,69 @@ func TestRedisClient(t *testing.T) { } { tt := tt t.Run(tt.name, func(t *testing.T) { + var orig []string + var ch chan struct{} + if tt.options.AddrUpdater != nil { + orig = tt.options.AddrUpdater() + // test background update + ch = make(chan struct{}) + tt.options.AddrUpdater = func() []string { + ch <- struct{}{} + return []string{redisAddr, redisAddr2} + } + } + + go func() { <-ch }() // create client will block cli := NewRedisRingClient(tt.options) + defer func() { + if !cli.closed { + t.Error("Failed to close redis ring client") + } + }() defer cli.Close() if !cli.RingAvailable() { - t.Errorf("Failed to have a connected redis client, ring not available") + t.Error("Failed to have a connected redis client, ring not available") } - // can't compare these - // if tt.options.Tracer != opentracing.Tracer{} { // cli.tracer == opentracing.Tracer{}{ - // t.Errorf("Found an unexpected tracer, want: %v, got: %v", tt.options.Tracer, cli.tracer) - // } + if tt.options.AddrUpdater != nil { + if !cmp.Equal(orig, []string{redisAddr}) { + t.Errorf("Failed to get addr: %v", cmp.Diff(orig, []string{redisAddr})) + } + + time.Sleep(2 + cli.options.UpdateInterval) + select { + case <-ch: + //ok + default: + t.Error("Failed to get updater called by goroutine") + } + + // test shards available + stats := cli.ring.PoolStats() + if stats.IdleConns < uint32(2*cli.options.MinIdleConns) { + t.Errorf("Failed to have enough idleconns for the new redis instance %d < %d", stats.IdleConns, 2*cli.options.MinIdleConns) + } + + // test close will stop background update + cli.Close() + time.Sleep(2 + cli.options.UpdateInterval) + select { + case <-ch: + t.Error("Failed to close background updater goroutine") + default: + //ok + } + if !cli.closed { + t.Error("Failed to close") + } + + } + + if tt.options.Tracer != nil { + span := cli.StartSpan("test") + span.Finish() + } if tt.options.ConnMetricsInterval != defaultConnMetricsInterval { cli.StartMetricsCollection() @@ -876,3 +1022,58 @@ func TestRedisClientZRangeByScoreWithScoresFirst(t *testing.T) { }) } } + +func TestRedisClientSetAddr(t *testing.T) { + redisAddr1, done1 := redistest.NewTestRedis(t) + defer done1() + redisAddr2, done2 := redistest.NewTestRedis(t) + defer done2() + + for _, tt := range []struct { + name string + options *RedisOptions + redisUpdate []string + keys []string + vals []string + }{ + { + name: "no redis change", + options: &RedisOptions{ + Addrs: []string{redisAddr1, redisAddr2}, + }, + keys: []string{"foo1", "foo2", "foo3", "foo4", "foo5"}, + vals: []string{"bar1", "bar2", "bar3", "bar4", "bar5"}, + }, + { + name: "with redis change", + options: &RedisOptions{ + Addrs: []string{redisAddr1}, + }, + redisUpdate: []string{ + redisAddr1, + redisAddr2, + }, + keys: []string{"foo1", "foo2", "foo3", "foo4", "foo5"}, + vals: []string{"bar1", "bar2", "bar3", "bar4", "bar5"}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + r := NewRedisRingClient(tt.options) + for i := 0; i < len(tt.keys); i++ { + r.Set(context.Background(), tt.keys[i], tt.vals[i], time.Second) + } + if len(tt.redisUpdate) != len(tt.options.Addrs) { + r.SetAddrs(context.Background(), tt.redisUpdate) + } + for i := 0; i < len(tt.keys); i++ { + got, err := r.Get(context.Background(), tt.keys[i]) + if err != nil { + t.Fatal(err) + } + if got != tt.vals[i] { + t.Errorf("Failed to get key '%s' wanted '%s', got '%s'", tt.keys[i], tt.vals[i], got) + } + } + }) + } +} diff --git a/net/redistest/redistest.go b/net/redistest/redistest.go index 3f1456eae7..f1a80eb12c 100644 --- a/net/redistest/redistest.go +++ b/net/redistest/redistest.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/go-redis/redis/v8" + "github.com/go-redis/redis/v9" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) diff --git a/ratelimit/registry.go b/ratelimit/registry.go index 2ad50e4e0b..e92102b7ac 100644 --- a/ratelimit/registry.go +++ b/ratelimit/registry.go @@ -20,6 +20,8 @@ const ( // ratelimiters. type Registry struct { sync.Mutex + once sync.Once + closed bool defaults Settings global Settings lookup map[Settings]*Ratelimit @@ -48,6 +50,7 @@ func NewSwarmRegistry(swarm Swarmer, ro *net.RedisOptions, settings ...Settings) } r := &Registry{ + once: sync.Once{}, defaults: defaults, global: defaults, lookup: make(map[Settings]*Ratelimit), @@ -67,7 +70,10 @@ func NewSwarmRegistry(swarm Swarmer, ro *net.RedisOptions, settings ...Settings) // Close teardown Registry and dependent resources func (r *Registry) Close() { - r.redisRing.Close() + r.once.Do(func() { + r.closed = true + r.redisRing.Close() + }) } func (r *Registry) get(s Settings) *Ratelimit { diff --git a/skipper.go b/skipper.go index 72a8147ab7..6e6f92d7e9 100644 --- a/skipper.go +++ b/skipper.go @@ -227,6 +227,15 @@ type Options struct { // used with external name services (type=ExternalName). KubernetesAllowedExternalNames []*regexp.Regexp + // KubernetesRedisServiceNamespace to be used to lookup ring shards dynamically + KubernetesRedisServiceNamespace string + + // KubernetesRedisServiceName to be used to lookup ring shards dynamically + KubernetesRedisServiceName string + + // KubernetesRedisServicePort to be used to lookup ring shards dynamically + KubernetesRedisServicePort int + // *DEPRECATED* API endpoint of the Innkeeper service, storing route definitions. InnkeeperUrl string @@ -1193,6 +1202,31 @@ func listenAndServe(proxy http.Handler, o *Options) error { return listenAndServeQuit(proxy, o, nil, nil, nil, nil) } +func findKubernetesDataclient(dataClients []routing.DataClient) *kubernetes.Client { + var kdc *kubernetes.Client + for _, dc := range dataClients { + if kc, ok := dc.(*kubernetes.Client); ok { + kdc = kc + break + } + } + return kdc +} + +func getRedisUpdaterFunc(namespace, name string, port int, kdc *kubernetes.Client) func() []string { + return func() []string { + // TODO(sszuecs): make sure kubernetes dataclient is already initialized and + // has polled the data once or kdc.GetEndpointAdresses should be blocking + // call to kubernetes API + a := kdc.GetEndpointAddresses(namespace, name, port) + log.Debugf("Redis updater called and found %d redis endpoints", len(a)) + for i := 0; i < len(a); i++ { + a[i] = strings.TrimPrefix(a[i], "TCP://") + } + return a + } +} + func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { // init log err := initLog(o) @@ -1390,9 +1424,11 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { var swarmer ratelimit.Swarmer var redisOptions *skpnet.RedisOptions + log.Infof("enable swarm: %v", o.EnableSwarm) if o.EnableSwarm { - if len(o.SwarmRedisURLs) > 0 { + if len(o.SwarmRedisURLs) > 0 || o.KubernetesRedisServiceName != "" { log.Infof("Redis based swarm with %d shards", len(o.SwarmRedisURLs)) + redisOptions = &skpnet.RedisOptions{ Addrs: o.SwarmRedisURLs, Password: o.SwarmRedisPassword, @@ -1405,6 +1441,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { MaxIdleConns: o.SwarmRedisMaxIdleConns, ConnMetricsInterval: o.redisConnMetricsInterval, Tracer: tracer, + Log: log.New(), } } else { log.Infof("Start swim based swarm") @@ -1450,6 +1487,19 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { defer theSwarm.Leave() swarmer = theSwarm } + + // in case we have kubernetes dataclient and we can detect redis instances, we patch redisOptions + if redisOptions != nil && o.KubernetesRedisServiceNamespace != "" && o.KubernetesRedisServiceName != "" && o.KubernetesRedisServicePort > 0 { + log.Infof("Use endpoints %s/%s :%d to fetch updated redis shards", o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName, o.KubernetesRedisServicePort) + + kdc := findKubernetesDataclient(dataClients) + if kdc != nil { + redisOptions.AddrUpdater = getRedisUpdaterFunc(o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName, o.KubernetesRedisServicePort, kdc) + } else { + log.Errorf("Failed to find kubernetes dataclient, but redis shards should be get by kubernetes svc %s/%s", o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName) + } + } + } var ratelimitRegistry *ratelimit.Registry @@ -1747,6 +1797,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { // wait for the first route configuration to be loaded if enabled: <-routing.FirstLoad() + log.Info("Dataclients are updated once, first load complete") return listenAndServeQuit(o.CustomHttpHandlerWrap(proxy), &o, sig, idleConnsCH, mtr, cr) } From 311cb2d6f20a6abdefcd3bc2e57c9358f53bc459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 26 Jul 2022 19:42:53 +0200 Subject: [PATCH 0058/1047] doc: fix markdown list (#2042) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- docs/reference/filters.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 0619cc76e3..cd633c506e 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1701,6 +1701,7 @@ using backend endpoint address instead of incoming request IP address or a HTTP Requires command line flags `-enable-swarm` and `-enable-ratelimits`. Both _rate limiting_ and _load shedding_ can use the exact same mechanism to protect the backend but the key difference is the semantics: + * _rate limiting_ should adopt 4XX and inform the client that they are exceeding some quota. It doesn't depend on the current capacity of the backend. * _load shedding_ should adopt 5XX and inform the client that the backend is not able to provide the service. It depends on the current capacity of the backend. From 91ffebf83cf0282a340cf7552679b7f4cc0829aa Mon Sep 17 00:00:00 2001 From: Peter Klijn Date: Tue, 2 Aug 2022 10:30:58 +0200 Subject: [PATCH 0059/1047] Fixed hardcoded predicate name (#2046) Signed-off-by: Peter Klijn --- routing/datasource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing/datasource.go b/routing/datasource.go index b9d80d420f..06ab683d35 100644 --- a/routing/datasource.go +++ b/routing/datasource.go @@ -363,7 +363,7 @@ func processPredicates(cpm map[string]PredicateSpec, defs []*eskip.Predicate) ([ cps := make([]Predicate, 0, len(defs)) var weight int for _, def := range defs { - if def.Name == "Weight" { + if def.Name == predicates.WeightName { var err error if weight, err = parseWeightPredicateArgs(def.Args); err != nil { return nil, 0, err From d07794cbd4bce07d9c0a2bd49c06541a22832603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 2 Aug 2022 10:31:15 +0200 Subject: [PATCH 0060/1047] update go-redis (#2047) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- go.mod | 8 ++++---- go.sum | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 05e411b6ce..1caef27bd9 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/go-redis/redis/v9 v9.0.0-beta.1 github.com/golang-jwt/jwt/v4 v4.2.0 - github.com/google/go-cmp v0.5.6 + github.com/google/go-cmp v0.5.8 github.com/hashicorp/memberlist v0.3.0 github.com/instana/go-sensor v1.38.3 github.com/lightstep/lightstep-tracer-go v0.25.0 @@ -44,7 +44,7 @@ require ( github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/net v0.0.0-20220225172249-27dd8689420f + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 @@ -115,7 +115,7 @@ require ( github.com/tklauser/numcpus v0.3.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect + golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect @@ -131,4 +131,4 @@ require ( go 1.18 -replace github.com/go-redis/redis/v9 => github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220720205457-d1c17518c5ee +replace github.com/go-redis/redis/v9 => github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220801200609-6f7f800107ba diff --git a/go.sum b/go.sum index 806a66a60a..08154861dc 100644 --- a/go.sum +++ b/go.sum @@ -497,8 +497,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/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.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -710,7 +711,7 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -877,8 +878,8 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/szuecs/rate-limit-buffer v0.7.1 h1:kpVLwDvpCTFQi8uhiXQrhAKWzNUaEKhArFdjb4GQ8F4= github.com/szuecs/rate-limit-buffer v0.7.1/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= -github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220720205457-d1c17518c5ee h1:2/YbVT6UC36rX9iiX4PXiT0bTCjzU197GqzA/nGxSUw= -github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220720205457-d1c17518c5ee/go.mod h1:6gNX1bXdwkpEG0M/hEBNK/Fp8zdyCkjwwKc6vBbfCDI= +github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220801200609-6f7f800107ba h1:XbMix18lOs+rRJwTaqnN6pmMc8z2kRLvFOfON9fFTA8= +github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220801200609-6f7f800107ba/go.mod h1:Bldcd/M/bm9HbnNPi/LUtYBSD8ttcZYBMupwMXhdU0o= github.com/szuecs/routegroup-client v0.21.0 h1:XWLxK2OCs4lZeIIHOJVVaFpe7nFmJjM3r+fOsZ2eyWw= github.com/szuecs/routegroup-client v0.21.0/go.mod h1:GbR5pqdcJdiFx9aL2hoq1ghAMZ9E1qFKBf88w7vqyK0= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= @@ -1074,8 +1075,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1189,8 +1190,8 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From bc39a90a7f567686da59018f2bc2eadde4b24716 Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Wed, 17 Aug 2022 11:10:18 +0200 Subject: [PATCH 0061/1047] Pin build to Go 1.18 (#2057) Signed-off-by: Mikkel Oscar Lyderik Larsen Signed-off-by: Mikkel Oscar Lyderik Larsen --- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- delivery.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index d19980c328..825258125e 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.18' + go-version: '~1.18' - run: go version - run: make deps - run: make check-fmt diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index edf06df3d4..513f5460a8 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -8,7 +8,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.18' + go-version: '~1.18' - run: go version - run: make deps - run: make check-race @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.18' + go-version: '~1.18' - run: go version - run: make deps - run: make check-fmt diff --git a/delivery.yaml b/delivery.yaml index 6b10b5e0c4..f388c1f797 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -3,7 +3,7 @@ pipeline: - id: build vm_config: type: linux - image: "cdp-runtime/go" + image: "cdp-runtime/go-1.18" type: script commands: - desc: Setup BuildKit From 842634347da8fe77e396f66edea79d329fd72130 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 18 Aug 2022 16:06:37 +0200 Subject: [PATCH 0062/1047] proxy: pass url via request context (#2058) Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- proxy/proxy.go | 33 +++++++++++++++++---------------- proxy/proxy_test.go | 40 +++++++++------------------------------- 2 files changed, 26 insertions(+), 47 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index f7cf6b8bfd..86473725f4 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -45,7 +45,6 @@ const ( unknownRouteID = "_unknownroute_" unknownRouteBackendType = "" unknownRouteBackend = "" - backendIsProxyHeader = "X-Skipper-Proxy" // Number of loops allowed by default. DefaultMaxLoopbacks = 9 @@ -514,19 +513,21 @@ func mapRequest(ctx *context, requestContext stdlibcontext.Context, removeHopHea rr.Header.Add("Authorization", fmt.Sprintf("Basic %s", upBase64)) } - if _, ok := stateBag[filters.BackendIsProxyKey]; ok { - forwardToProxy(r, rr) - } - ctxspan := ot.SpanFromContext(r.Context()) if ctxspan != nil { rr = rr.WithContext(ot.ContextWithSpan(rr.Context(), ctxspan)) } + if _, ok := stateBag[filters.BackendIsProxyKey]; ok { + rr = forwardToProxy(r, rr) + } + return rr, endpoint, nil } -func forwardToProxy(incoming, outgoing *http.Request) { +type proxyUrlContextKey struct{} + +func forwardToProxy(incoming, outgoing *http.Request) *http.Request { proxyURL := &url.URL{ Scheme: outgoing.URL.Scheme, Host: outgoing.URL.Host, @@ -535,7 +536,15 @@ func forwardToProxy(incoming, outgoing *http.Request) { outgoing.URL.Host = incoming.Host outgoing.URL.Scheme = schemeFromRequest(incoming) - outgoing.Header.Set(backendIsProxyHeader, proxyURL.String()) + return outgoing.WithContext(stdlibcontext.WithValue(outgoing.Context(), proxyUrlContextKey{}, proxyURL)) +} + +func proxyFromContext(req *http.Request) (*url.URL, error) { + proxyURL, _ := req.Context().Value(proxyUrlContextKey{}).(*url.URL) + if proxyURL != nil { + return proxyURL, nil + } + return nil, nil } type skipperDialer struct { @@ -628,7 +637,7 @@ func WithParams(p Params) *Proxy { MaxIdleConnsPerHost: p.IdleConnectionsPerHost, IdleConnTimeout: p.CloseIdleConnsPeriod, DisableKeepAlives: p.DisableHTTPKeepalives, - Proxy: proxyFromHeader, + Proxy: proxyFromContext, } quit := make(chan struct{}) @@ -728,14 +737,6 @@ func tryCatch(p func(), onErr func(err interface{}, stack string)) { p() } -func proxyFromHeader(req *http.Request) (*url.URL, error) { - if u := req.Header.Get(backendIsProxyHeader); u != "" { - req.Header.Del(backendIsProxyHeader) - return url.Parse(u) - } - return nil, nil -} - // applies filters to a request func (p *Proxy) applyFiltersToRequest(f []*routing.RouteFilter, ctx *context) []*routing.RouteFilter { if len(f) == 0 { diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 7b3c866dde..5b2b64d9c0 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -2213,44 +2213,22 @@ func TestForwardToProxy(t *testing.T) { TLS: ti.tls, } - forwardToProxy(incoming, outgoing) + outgoing = forwardToProxy(incoming, outgoing) - if outgoing.URL.String() != ti.expectedRequestURL { - t.Errorf("request URLs are not equal, expected %s got %s", - ti.expectedRequestURL, outgoing.URL.String()) - } + assert.Equal(t, ti.expectedRequestURL, outgoing.URL.String()) - proxyURL := outgoing.Header.Get(backendIsProxyHeader) + proxyURL, err := proxyFromContext(outgoing) - if proxyURL != ti.expectedProxyURL { - t.Errorf("proxy URLs are not equal, expected %s got %s", - ti.expectedProxyURL, proxyURL) - } + assert.NoError(t, err) + assert.Equal(t, ti.expectedProxyURL, proxyURL.String()) } } -func TestProxyFromHeader(t *testing.T) { - u1, err := proxyFromHeader(&http.Request{}) - if err != nil { - t.Error(err) - } - if u1 != nil { - t.Errorf("expected nil but got %v", u1) - } - - expectedProxyURL := "http://proxy.example.com" +func TestProxyFromEmptyContext(t *testing.T) { + proxyUrl, err := proxyFromContext(&http.Request{}) - u2, err := proxyFromHeader(&http.Request{ - Header: http.Header{ - backendIsProxyHeader: []string{expectedProxyURL}, - }, - }) - if err != nil { - t.Error(err) - } - if u2.String() != expectedProxyURL { - t.Errorf("expected '%s' but got '%v'", expectedProxyURL, u2) - } + assert.NoError(t, err) + assert.Nil(t, proxyUrl) } func BenchmarkAccessLogNoFilter(b *testing.B) { benchmarkAccessLog(b, "", 200) } From f0f9dd38830fb07a209d60620d0707f57d5a9ee3 Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Mon, 22 Aug 2022 17:23:51 +0300 Subject: [PATCH 0063/1047] Custom separator edit route (#2060) * Made first symbol a custom separator for EditRoute and CloneRoute options * Added info about -edit-route and -clone-route arbitrary separator in docs Signed-off-by: Roman Zavodskikh --- config/routechangerconfig.go | 10 ++++++++-- config/routechangerconfig_test.go | 5 +++++ docs/operation/operation.md | 11 +++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/config/routechangerconfig.go b/config/routechangerconfig.go index ffe41f7127..69dd09d9c0 100644 --- a/config/routechangerconfig.go +++ b/config/routechangerconfig.go @@ -9,17 +9,22 @@ import ( type routeChangerConfig struct { Reg *regexp.Regexp Repl string + Sep string } func (rcc routeChangerConfig) String() string { if rcc.Reg == nil { return "" } - return fmt.Sprintf("/%s/%s/", rcc.Reg, rcc.Repl) + return fmt.Sprintf("%s%s%s%s%s", rcc.Sep, rcc.Reg, rcc.Sep, rcc.Repl, rcc.Sep) } func (rcc *routeChangerConfig) Set(value string) error { - a := strings.Split(value, "/") + if len(value) == 0 { + return fmt.Errorf("empty string as an argument is not allowed") + } + firstSym := value[:1] + a := strings.Split(value, firstSym) if len(a) != 4 { return fmt.Errorf("unexpected size of string split: %d", len(a)) } @@ -27,6 +32,7 @@ func (rcc *routeChangerConfig) Set(value string) error { reg, repl := a[1], a[2] rcc.Reg, err = regexp.Compile(reg) rcc.Repl = repl + rcc.Sep = firstSym return err } diff --git a/config/routechangerconfig_test.go b/config/routechangerconfig_test.go index 508c27042f..1dee94fcb3 100644 --- a/config/routechangerconfig_test.go +++ b/config/routechangerconfig_test.go @@ -15,6 +15,11 @@ func Test_routeChangerConfig(t *testing.T) { input: "/Source[(](.*)[)]/ClientIP($1)/", want: "/Source[(](.*)[)]/ClientIP($1)/", }, + { + name: "test string with # separator", + input: "#Source[(](.*)[)]#ClientIP($1)#", + want: "#Source[(](.*)[)]#ClientIP($1)#", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 709539c748..16d3b832a8 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -1063,3 +1063,14 @@ r: SourceFromLast("9.0.0.0/8","2001:67c:20a0::/48") -> ...` clone_r: ClientIP("9.0.0.0/8","2001:67c:20a0::/48") -> ...` ``` for migration time. + +`/` symbol is not the only option for the separator for `-edit-route` and `-clone-route`, any +first symbol you will specify in those options could be used as separator. This could be useful +for IP mask changes, for example, you can use `-edit-route='#/26#/24#`. In this case +``` +r: SourceFromLast("9.0.0.0/26","2001:67c:20a0::/48") -> ...` +``` +will be changed to +``` +r: SourceFromLast("9.0.0.0/24","2001:67c:20a0::/48") -> ...` +``` From 8ab6863b5b7da000449ba9f0eb66ff4986e34eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Mon, 22 Aug 2022 22:50:40 +0200 Subject: [PATCH 0064/1047] ignore gosec g114 for support and debug listener which should not be exposed to untrusted entities (#2063) --- skipper.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skipper.go b/skipper.go index 6e6f92d7e9..a63a86ff6a 100644 --- a/skipper.go +++ b/skipper.go @@ -1731,7 +1731,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { do.Flags |= proxy.Debug dbg := proxy.WithParams(do) log.Infof("debug listener on %v", o.DebugListener) - go func() { http.ListenAndServe(o.DebugListener, dbg) }() + go func() { http.ListenAndServe(o.DebugListener, dbg) /* #nosec */ }() } // init support endpoints @@ -1755,6 +1755,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { log.Infof("support listener on %s", supportListener) go func() { + /* #nosec */ if err := http.ListenAndServe(supportListener, mux); err != nil { log.Errorf("Failed to start supportListener on %s: %v", supportListener, err) } From 8f18cddfd876671fbcf9601faa8b2e54731bbbfd Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Tue, 23 Aug 2022 09:31:34 +0200 Subject: [PATCH 0065/1047] Update alpine base image to latest minor version (#2061) * Use alpine:3 as base image Signed-off-by: Mikkel Oscar Lyderik Larsen * Update golang.org/x/crypto/ssh Signed-off-by: Mikkel Oscar Lyderik Larsen Signed-off-by: Mikkel Oscar Lyderik Larsen --- Dockerfile | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- packaging/Dockerfile | 2 +- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- packaging/Makefile | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 864df8ce78..5091e3b8a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.opensource.zalan.do/library/alpine-3.13:latest +FROM registry.opensource.zalan.do/library/alpine-3:latest LABEL maintainer="Team Teapot @ Zalando SE " ADD skipper /usr/bin/ diff --git a/go.mod b/go.mod index 1caef27bd9..2156d7a317 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/yookoala/gofast v0.6.0 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 go.uber.org/atomic v1.9.0 - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 + golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c @@ -115,7 +115,7 @@ require ( github.com/tklauser/numcpus v0.3.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect + golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index 08154861dc..03e99677d6 100644 --- a/go.sum +++ b/go.sum @@ -981,8 +981,8 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= +golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1190,8 +1190,8 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/packaging/Dockerfile b/packaging/Dockerfile index 97a0e91dd4..947edc65c1 100644 --- a/packaging/Dockerfile +++ b/packaging/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3.13:latest +ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3:latest FROM ${BASE_IMAGE} LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 8c07f6a265..041e691597 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine:3.13 +FROM --platform=linux/arm64 alpine:3 LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index a08511b8d1..7f85ab9d37 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine:3.13 +FROM --platform=linux/arm/v7 alpine:3 LABEL maintainer="Team Teapot @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ diff --git a/packaging/Makefile b/packaging/Makefile index bcb203bffb..6e1c4d0b36 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -59,7 +59,7 @@ docker.push.armv7: docker.build.armv7 # build multi-arch container image using a trusted multi-arch base image docker.push.multiarch: clean build.linux docker.build.enable docker buildx build --rm -t $(MULTIARCH_IMAGE) --platform linux/amd64,linux/arm64 --push \ - --build-arg BASE_IMAGE=container-registry.zalando.net/library/alpine-3.13:latest . + --build-arg BASE_IMAGE=container-registry.zalando.net/library/alpine-3:latest . # https://docs.docker.com/build/working-with-build/ # ~/.docker/config.json add: "experimental": "enabled", From d77376621c814b86b8ddc742922d305ac5052d96 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 23 Aug 2022 11:00:52 +0200 Subject: [PATCH 0066/1047] docs: fix clusterClientRatelimit typo (#2059) Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index cd633c506e..526b284901 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -116,7 +116,7 @@ foo: * -> setRequestHeader("X-Passed-Skipper", "true") -> "https://backend.examp ``` // Ratelimit per resource -Path("/resource/:id") -> setRequestHeader("X-Resource-Id", "${id}") -> clusterClientRateLimit("resource", 10, "1m", "X-Resource-Id") -> "https://backend.example.org"; +Path("/resource/:id") -> setRequestHeader("X-Resource-Id", "${id}") -> clusterClientRatelimit("resource", 10, "1m", "X-Resource-Id") -> "https://backend.example.org"; ``` ## appendRequestHeader From f73eb3200e53913a20825984fd0655c692fcec89 Mon Sep 17 00:00:00 2001 From: Xavi Ivars Date: Wed, 24 Aug 2022 16:29:16 +0200 Subject: [PATCH 0067/1047] Add custom weight for custom predicates (#2054) * Add custom weight for custom predicates Signed-off-by: Xavi Ivars --- routing/datasource.go | 10 ++++- routing/datasource_test.go | 82 ++++++++++++++++++++++++++++++++++++++ routing/export_test.go | 7 ++-- routing/routing.go | 7 ++++ 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/routing/datasource.go b/routing/datasource.go index 06ab683d35..542554a978 100644 --- a/routing/datasource.go +++ b/routing/datasource.go @@ -364,11 +364,15 @@ func processPredicates(cpm map[string]PredicateSpec, defs []*eskip.Predicate) ([ var weight int for _, def := range defs { if def.Name == predicates.WeightName { + var w int var err error - if weight, err = parseWeightPredicateArgs(def.Args); err != nil { + + if w, err = parseWeightPredicateArgs(def.Args); err != nil { return nil, 0, err } + weight += w + continue } @@ -386,6 +390,10 @@ func processPredicates(cpm map[string]PredicateSpec, defs []*eskip.Predicate) ([ return nil, 0, fmt.Errorf("failed to create predicate %q: %w", spec.Name(), err) } + if ws, ok := spec.(WeightedPredicateSpec); ok { + weight += ws.Weight() + } + cps = append(cps, cp) } diff --git a/routing/datasource_test.go b/routing/datasource_test.go index 39df08f723..38936fe8e5 100644 --- a/routing/datasource_test.go +++ b/routing/datasource_test.go @@ -1,6 +1,7 @@ package routing_test import ( + "net/http" "testing" "time" @@ -126,6 +127,68 @@ func TestProcessRouteDefErrors(t *testing.T) { } } +func TestProcessRouteDefWeight(t *testing.T) { + + wps := weightedPredicateSpec{} + + cpm := make(map[string]routing.PredicateSpec) + cpm[wps.Name()] = wps + + for _, ti := range []struct { + route string + weight int + }{ + { + `Path("/foo") -> `, + 0, + }, { + `WeightedPredicate10() -> `, + 10, + }, { + `Weight(20) -> `, + 20, + }, { + `Weight(20) && Weight(10)-> `, + 30, + }, { + `WeightedPredicate10() && Weight(20) -> `, + 30, + }, + } { + func() { + + dc, err := testdataclient.NewDoc(ti.route) + if err != nil { + t.Error(ti.route, err) + + return + } + + defs, err := dc.LoadAll() + if err != nil { + t.Error(ti.route, err) + + return + } + + r := defs[0] + + _, weight, err := routing.ExportProcessPredicates(cpm, r.Predicates) + if err != nil { + t.Error(ti.route, err) + + return + } + + if weight != ti.weight { + t.Errorf("expected weight '%d'. Got: '%d' (%s)", ti.weight, weight, ti.route) + + return + } + }() + } +} + func TestLogging(t *testing.T) { if testing.Short() { t.Skip() @@ -222,3 +285,22 @@ func TestLogging(t *testing.T) { ) }) } + +type weightedPredicateSpec struct{} +type weightedPredicate struct{} + +func (w weightedPredicate) Match(request *http.Request) bool { + return true +} + +func (w weightedPredicateSpec) Name() string { + return "WeightedPredicate10" +} + +func (w weightedPredicateSpec) Create([]interface{}) (routing.Predicate, error) { + return weightedPredicate{}, nil +} + +func (w weightedPredicateSpec) Weight() int { + return 10 +} diff --git a/routing/export_test.go b/routing/export_test.go index a836d0fa1e..174db04730 100644 --- a/routing/export_test.go +++ b/routing/export_test.go @@ -1,7 +1,8 @@ package routing var ( - ExportProcessRouteDef = processRouteDef - ExportNewMatcher = newMatcher - ExportMatch = (*matcher).match + ExportProcessRouteDef = processRouteDef + ExportNewMatcher = newMatcher + ExportMatch = (*matcher).match + ExportProcessPredicates = processPredicates ) diff --git a/routing/routing.go b/routing/routing.go index e74b4ee80a..93cb9a5d29 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -72,6 +72,13 @@ type PredicateSpec interface { Create([]interface{}) (Predicate, error) } +type WeightedPredicateSpec interface { + PredicateSpec + + // Extra Weight of the predicate + Weight() int +} + // Options for initialization for routing. type Options struct { From 003b1a678fc62aeb1d8440c815d986ca8225e09e Mon Sep 17 00:00:00 2001 From: Ricardo Herrera Date: Thu, 25 Aug 2022 07:00:32 -0400 Subject: [PATCH 0068/1047] feature: allow insecure connections when using tls (#2066) * feature: insecure connections when using tls Allow insecure connections on an alternate port when using TLS. This will remove the requirement to encrypt all traffic when enabling TLS. Signed-off-by: Ricardo Herrera --- config/config.go | 3 +++ skipper.go | 31 ++++++++++++++++++++++++------- skipper_test.go | 27 ++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index 8cc571486c..d1f2bfeb98 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,7 @@ type Config struct { // generic: Address string `yaml:"address"` + InsecureAddress string `yaml:"insecure-address"` EnableTCPQueue bool `yaml:"enable-tcp-queue"` ExpectedBytesPerRequest int `yaml:"expected-bytes-per-request"` MaxTCPListenerConcurrency int `yaml:"max-tcp-listener-concurrency"` @@ -298,6 +299,7 @@ func NewConfig() *Config { // generic: flag.StringVar(&cfg.Address, "address", ":9090", "network address that skipper should listen on") + flag.StringVar(&cfg.InsecureAddress, "insecure-address", "", "insecure network address that skipper should listen on when TLS is enabled") flag.BoolVar(&cfg.EnableTCPQueue, "enable-tcp-queue", false, "enable the TCP listener queue") flag.IntVar(&cfg.ExpectedBytesPerRequest, "expected-bytes-per-request", 50*1024, "bytes per request, that is used to calculate concurrency limits to buffer connection spikes") flag.IntVar(&cfg.MaxTCPListenerConcurrency, "max-tcp-listener-concurrency", 0, "sets hardcoded max for TCP listener concurrency, normally calculated based on available memory cgroups with max TODO") @@ -668,6 +670,7 @@ func (c *Config) ToOptions() skipper.Options { options := skipper.Options{ // generic: Address: c.Address, + InsecureAddress: c.InsecureAddress, StatusChecks: c.StatusChecks.values, EnableTCPQueue: c.EnableTCPQueue, ExpectedBytesPerRequest: c.ExpectedBytesPerRequest, diff --git a/skipper.go b/skipper.go index a63a86ff6a..bcd2ef82ec 100644 --- a/skipper.go +++ b/skipper.go @@ -93,6 +93,9 @@ type Options struct { // Network address that skipper should listen on. Address string + // Insecure network address skipper should listen on when TLS is enabled + InsecureAddress string + // EnableTCPQueue enables controlling the // concurrently processed requests at the TCP listener. EnableTCPQueue bool @@ -1058,13 +1061,13 @@ func (o *Options) tlsConfig(cr *certregistry.CertRegistry) (*tls.Config, error) return config, nil } -func listen(o *Options, mtr metrics.Metrics) (net.Listener, error) { - if o.Address == "" { - o.Address = ":http" +func listen(o *Options, address string, mtr metrics.Metrics) (net.Listener, error) { + if address == "" { + address = ":http" } if !o.EnableTCPQueue { - return net.Listen("tcp", o.Address) + return net.Listen("tcp", address) } var memoryLimit int @@ -1105,7 +1108,7 @@ func listen(o *Options, mtr metrics.Metrics) (net.Listener, error) { return queuelistener.Listen(queuelistener.Options{ Network: "tcp", - Address: o.Address, + Address: address, MaxConcurrency: o.MaxTCPListenerConcurrency, MaxQueueSize: o.MaxTCPListenerQueue, MemoryLimitBytes: memoryLimit, @@ -1175,14 +1178,28 @@ func listenAndServeQuit( log.Infof("proxy listener on %v", o.Address) if srv.TLSConfig != nil { + if o.InsecureAddress != "" { + log.Infof("insecure listener on %v", o.InsecureAddress) + + go func() { + l, err := listen(o, o.InsecureAddress, mtr) + if err != nil { + log.Errorf("Failed to start insecure listener on %s: %v", o.Address, err) + } + + if err := srv.Serve(l); err != http.ErrServerClosed { + log.Errorf("Insecure listener serve failed: %v", err) + } + }() + } + if err := srv.ListenAndServeTLS("", ""); err != http.ErrServerClosed { log.Errorf("ListenAndServeTLS failed: %v", err) return err } } else { log.Infof("TLS settings not found, defaulting to HTTP") - - l, err := listen(o, mtr) + l, err := listen(o, o.Address, mtr) if err != nil { return err } diff --git a/skipper_test.go b/skipper_test.go index 4f4c85072f..0f7ad653cd 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -157,10 +157,16 @@ func TestHTTPSServer(t *testing.T) { t.Fatal(err) } + i, err := findAddress() + if err != nil { + t.Fatal(err) + } + o := Options{ - Address: a, - CertPathTLS: "fixtures/test.crt", - KeyPathTLS: "fixtures/test.key", + Address: a, + InsecureAddress: i, + CertPathTLS: "fixtures/test.crt", + KeyPathTLS: "fixtures/test.key", } rt := routing.New(routing.Options{ @@ -186,6 +192,21 @@ func TestHTTPSServer(t *testing.T) { if err != nil { t.Fatalf("Failed to stream response body: %v", err) } + + r, err = waitConnGet("http://" + o.InsecureAddress) + if r != nil { + defer r.Body.Close() + } + if err != nil { + t.Fatalf("Cannot connect to the local server for testing: %s ", err.Error()) + } + if r.StatusCode != 404 { + t.Fatalf("Status code should be 404, instead got: %d\n", r.StatusCode) + } + _, err = io.ReadAll(r.Body) + if err != nil { + t.Fatalf("Failed to stream response body: %v", err) + } } // to run this test, set `-args listener` for the test command From 717857730d1decabe4615821fb0f77cc4c6bd483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 26 Aug 2022 10:41:56 +0200 Subject: [PATCH 0069/1047] Fix: memory leak in admissionControl filter. (#2067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: memory leak in admissionControl filter. In case routing changes triggers a lot of routing updates (f.e. every 3s) this would cause a leak of tickers and updates would be sent to the channel by Go runtime. * fix: same leak in other places Signed-off-by: Sandor Szücs --- filters/shedder/admission.go | 2 ++ loadbalancer/healthchecker.go | 1 + secrets/encrypter.go | 1 + 3 files changed, 4 insertions(+) diff --git a/filters/shedder/admission.go b/filters/shedder/admission.go index 69cd946a7b..af14d48ea5 100644 --- a/filters/shedder/admission.go +++ b/filters/shedder/admission.go @@ -330,7 +330,9 @@ func (ac *admissionControl) Close() { func (ac *admissionControl) tickWindows(d time.Duration) { t := time.NewTicker(d) + defer t.Stop() i := 0 + for range t.C { select { case <-ac.quit: diff --git a/loadbalancer/healthchecker.go b/loadbalancer/healthchecker.go index 9c4840330c..60e25ba610 100644 --- a/loadbalancer/healthchecker.go +++ b/loadbalancer/healthchecker.go @@ -144,6 +144,7 @@ func (lb *LB) FilterHealthyMemberRoutes(routes []*routing.Route) []*routing.Rout // healthchecks to all backends, which were reported. func (lb *LB) startDoHealthChecks() { healthTicker := time.NewTicker(lb.healthcheckInterval) + defer healthTicker.Stop() rt := &http.Transport{ DialContext: (&net.Dialer{ Timeout: 3000 * time.Millisecond, diff --git a/secrets/encrypter.go b/secrets/encrypter.go index cd8d4a1c97..654bf73129 100644 --- a/secrets/encrypter.go +++ b/secrets/encrypter.go @@ -160,6 +160,7 @@ func (e *Encrypter) runCipherRefresher(refreshInterval time.Duration) error { } go func() { ticker := time.NewTicker(refreshInterval) + defer ticker.Stop() for { select { case <-e.closer: From 58c61d1bec5039b6c357e5389c2594456c218a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 26 Aug 2022 20:18:14 +0200 Subject: [PATCH 0070/1047] fix: on close of ratelimit registry it should close all filters (#2068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- ratelimit/registry.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ratelimit/registry.go b/ratelimit/registry.go index e92102b7ac..08786d9d65 100644 --- a/ratelimit/registry.go +++ b/ratelimit/registry.go @@ -73,6 +73,9 @@ func (r *Registry) Close() { r.once.Do(func() { r.closed = true r.redisRing.Close() + for _, rl := range r.lookup { + rl.Close() + } }) } From 92ba2ed1bf8b84c3bd89de1ffd9ed191346bc6ac Mon Sep 17 00:00:00 2001 From: Mustafa Saber Date: Mon, 29 Aug 2022 20:06:35 +0200 Subject: [PATCH 0071/1047] content based block filter (#2002) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce `block` filter to block requests with malicious body. With `r: * -> block("word 1", "word 2") -> "http://127.0.0.1:9090";` filter will go through content and try matching against list of strings to block. Signed-off-by: Mustafa Abdelrahman Co-authored-by: Sandor Szücs --- config/config.go | 3 + config/config_test.go | 1 + docs/reference/filters.md | 17 +++ filters/block/block.go | 72 +++++++++++++ filters/block/block_test.go | 137 +++++++++++++++++++++++ filters/block/matcher.go | 210 ++++++++++++++++++++++++++++++++++++ filters/filters.go | 1 + proxy/proxy.go | 10 +- proxy/tracing.go | 1 + skipper.go | 5 + 10 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 filters/block/block.go create mode 100644 filters/block/block_test.go create mode 100644 filters/block/matcher.go diff --git a/config/config.go b/config/config.go index d1f2bfeb98..81e4b6c914 100644 --- a/config/config.go +++ b/config/config.go @@ -53,6 +53,7 @@ type Config struct { RemoveHopHeaders bool `yaml:"remove-hop-headers"` RfcPatchPath bool `yaml:"rfc-patch-path"` MaxAuditBody int `yaml:"max-audit-body"` + MaxMatcherBufferSize uint64 `yaml:"max-matcher-buffer-size"` EnableBreakers bool `yaml:"enable-breakers"` Breakers breakerFlags `yaml:"breaker"` EnableRatelimiters bool `yaml:"enable-ratelimits"` @@ -322,6 +323,7 @@ func NewConfig() *Config { flag.BoolVar(&cfg.RemoveHopHeaders, "remove-hop-headers", false, "enables removal of Hop-Headers according to RFC-2616") flag.BoolVar(&cfg.RfcPatchPath, "rfc-patch-path", false, "patches the incoming request path to preserve uncoded reserved characters according to RFC 2616 and RFC 3986") flag.IntVar(&cfg.MaxAuditBody, "max-audit-body", 1024, "sets the max body to read to log in the audit log body") + flag.Uint64Var(&cfg.MaxMatcherBufferSize, "max-matcher-buffer-size", 2097152, "sets the maximum read size of the body read by the block filter, default is 2MiB") flag.BoolVar(&cfg.EnableBreakers, "enable-breakers", false, enableBreakersUsage) flag.Var(&cfg.Breakers, "breaker", breakerUsage) flag.BoolVar(&cfg.EnableRatelimiters, "enable-ratelimits", false, enableRatelimitsUsage) @@ -687,6 +689,7 @@ func (c *Config) ToOptions() skipper.Options { LoadBalancerHealthCheckInterval: c.LoadBalancerHealthCheckInterval, ReverseSourcePredicate: c.ReverseSourcePredicate, MaxAuditBody: c.MaxAuditBody, + MaxMatcherBufferSize: c.MaxMatcherBufferSize, EnableBreakers: c.EnableBreakers, BreakerSettings: c.Breakers, EnableRatelimiters: c.EnableRatelimiters, diff --git a/config/config_test.go b/config/config_test.go index 1b39623838..f7b41e2b8e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -92,6 +92,7 @@ func Test_NewConfig(t *testing.T) { MaxLoopbacks: 12, DefaultHTTPStatus: 404, MaxAuditBody: 1024, + MaxMatcherBufferSize: 2097152, MetricsFlavour: commaListFlag("codahale", "prometheus"), FilterPlugins: newPluginFlag(), PredicatePlugins: newPluginFlag(), diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 526b284901..5aa0cb506b 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -80,6 +80,23 @@ foo3: -> ; ``` + +## blockContent + +Block a request based on it's body content. + +The filter max buffer size is 2MiB by default and can be overidden with `-max-matcher-buffer-size=`. + +Parameters: + +* toblockList (List of strings) + +Example: + +``` +* -> blockContent("Malicious Content") -> "http://example.com"; +``` + ## modRequestHeader Replace all matched regex expressions in the given header. diff --git a/filters/block/block.go b/filters/block/block.go new file mode 100644 index 0000000000..532e7d3c7c --- /dev/null +++ b/filters/block/block.go @@ -0,0 +1,72 @@ +package block + +import ( + "errors" + + "github.com/zalando/skipper/filters" +) + +var ( + ErrClosed = errors.New("reader closed") +) + +type blockSpec struct { + MaxMatcherBufferSize uint64 +} + +type block struct { + toblockList []toblockKeys + maxEditorBuffer uint64 + maxBufferHandling maxBufferHandling +} + +func NewBlockFilter(maxMatcherBufferSize uint64) filters.Spec { + return &blockSpec{ + MaxMatcherBufferSize: maxMatcherBufferSize, + } +} + +func (*blockSpec) Name() string { + return filters.BlockName +} + +func (bs *blockSpec) CreateFilter(args []interface{}) (filters.Filter, error) { + if len(args) == 0 { + return nil, filters.ErrInvalidFilterParameters + } + + sargs := make([]toblockKeys, 0, len(args)) + for _, w := range args { + switch v := w.(type) { + case string: + sargs = append(sargs, toblockKeys{str: []byte(v)}) + + default: + return nil, filters.ErrInvalidFilterParameters + } + } + + b := &block{ + toblockList: sargs, + maxBufferHandling: maxBufferBestEffort, + maxEditorBuffer: bs.MaxMatcherBufferSize, + } + + return *b, nil +} + +func (b block) Request(ctx filters.FilterContext) { + req := ctx.Request() + if req.ContentLength == 0 { + return + } + + req.Body = newMatcher( + req.Body, + b.toblockList, + b.maxEditorBuffer, + b.maxBufferHandling, + ) +} + +func (block) Response(filters.FilterContext) {} diff --git a/filters/block/block_test.go b/filters/block/block_test.go new file mode 100644 index 0000000000..b0042939e5 --- /dev/null +++ b/filters/block/block_test.go @@ -0,0 +1,137 @@ +package block + +import ( + "net/http" + "strings" + "testing" + + "github.com/zalando/skipper/proxy" +) + +type nonBlockingReader struct { + initialContent []byte +} + +func (r *nonBlockingReader) Read(p []byte) (int, error) { + n := copy(p, r.initialContent) + r.initialContent = r.initialContent[n:] + return n, nil +} + +func (r *nonBlockingReader) Close() error { + return nil +} + +func TestBlock(t *testing.T) { + for _, tt := range []struct { + name string + content string + err error + }{ + { + name: "small string", + content: ".class", + err: proxy.ErrBlocked, + }, + { + name: "small string without match", + content: "foxi", + err: nil, + }, + { + name: "small string with match", + content: "fox.class.foo.blah", + err: proxy.ErrBlocked, + }, + { + name: "long string", + content: strings.Repeat("A", 8192), + err: nil, + }} { + t.Run(tt.name, func(t *testing.T) { + r := &nonBlockingReader{initialContent: []byte(tt.content)} + toblockList := []toblockKeys{{str: []byte(".class")}} + + bmb := newMatcher(r, toblockList, 2097152, maxBufferBestEffort) + + t.Logf("Content: %s", r.initialContent) + p := make([]byte, len(r.initialContent)) + n, err := bmb.Read(p) + if err != nil { + if err == proxy.ErrBlocked { + t.Logf("Stop! Request has some blocked content!") + } else { + t.Errorf("Failed to read: %v", err) + } + } else if n != len(tt.content) { + t.Errorf("Failed to read content length %d, got %d", len(tt.content), n) + } + + }) + } + +} + +func BenchmarkBlock(b *testing.B) { + + fake := func(source string, len int) string { + return strings.Repeat(source[:2], len) // partially matches target + } + + fakematch := func(source string, len int) string { + return strings.Repeat(source, len) // matches target + } + + for _, tt := range []struct { + name string + tomatch []byte + bm []byte + }{ + { + name: "Small Stream without blocking", + tomatch: []byte(".class"), + bm: []byte(fake(".class", 1<<20)), // Test with 1Mib + }, + { + name: "Small Stream with blocking", + tomatch: []byte(".class"), + bm: []byte(fakematch(".class", 1<<20)), + }, + { + name: "Medium Stream without blocking", + tomatch: []byte(".class"), + bm: []byte(fake(".class", 1<<24)), // Test with ~10Mib + }, + { + name: "Medium Stream with blocking", + tomatch: []byte(".class"), + bm: []byte(fakematch(".class", 1<<24)), + }, + { + name: "Large Stream without blocking", + tomatch: []byte(".class"), + bm: []byte(fake(".class", 1<<27)), // Test with ~100Mib + }, + { + name: "Large Stream with blocking", + tomatch: []byte(".class"), + bm: []byte(fakematch(".class", 1<<27)), + }} { + b.Run(tt.name, func(b *testing.B) { + target := &nonBlockingReader{initialContent: tt.bm} + r := &http.Request{ + Body: target, + } + toblockList := []toblockKeys{{str: tt.tomatch}} + bmb := newMatcher(r.Body, toblockList, 2097152, maxBufferBestEffort) + p := make([]byte, len(target.initialContent)) + b.Logf("Number of loops: %b", b.N) + for n := 0; n < b.N; n++ { + _, err := bmb.Read(p) + if err != nil { + return + } + } + }) + } +} diff --git a/filters/block/matcher.go b/filters/block/matcher.go new file mode 100644 index 0000000000..f07f0b6b99 --- /dev/null +++ b/filters/block/matcher.go @@ -0,0 +1,210 @@ +package block + +import ( + "bytes" + "errors" + "io" + "sync" + + "github.com/zalando/skipper/metrics" + "github.com/zalando/skipper/proxy" +) + +type toblockKeys struct{ str []byte } + +const ( + readBufferSize uint64 = 8192 +) + +type maxBufferHandling int + +const ( + maxBufferBestEffort maxBufferHandling = iota + maxBufferAbort +) + +// matcher provides a reader that wraps an input reader, and blocks the request +// if a pattern was found. +// +// It reads enough data until at least a complete match of the +// pattern is met or the maxBufferSize is reached. When the pattern matches the entire +// buffered input, the replaced content is returned to the caller when maxBufferSize is +// reached. This also means that more replacements can happen than if we edited the +// entire content in one piece, but this is necessary to be able to use the matcher for +// input with unknown length. +// +// When the maxBufferHandling is set to maxBufferAbort, then the streaming is aborted +// and the rest of the payload is dropped. +// +// To limit the number of repeated scans over the buffered data, the size of the +// additional data read from the input grows exponentially with every iteration that +// didn't result with any matched data blocked. If there was any matched data +// the read size is reset to the initial value. +// +// When the input returns an error, e.g. EOF, the matcher finishes matching the buffered +// data, blocks or return it to the caller. +// +// When the matcher is closed, it doesn't read anymore from the input or return any +// buffered data. If the input implements io.Closer, closing the matcher closes the +// input, too. +// +type matcher struct { + once sync.Once + input io.ReadCloser + toblockList []toblockKeys + maxBufferSize uint64 + maxBufferHandling maxBufferHandling + readBuffer []byte + + ready *bytes.Buffer + pending *bytes.Buffer + + metrics metrics.Metrics + + err error + closed bool +} + +var ( + ErrMatcherBufferFull = errors.New("matcher buffer full") +) + +func newMatcher( + input io.ReadCloser, + toblockList []toblockKeys, + maxBufferSize uint64, + mbh maxBufferHandling, +) *matcher { + + rsize := readBufferSize + if maxBufferSize < rsize { + rsize = maxBufferSize + } + + return &matcher{ + once: sync.Once{}, + input: input, + toblockList: toblockList, + maxBufferSize: maxBufferSize, + maxBufferHandling: mbh, + readBuffer: make([]byte, rsize), + pending: bytes.NewBuffer(nil), + ready: bytes.NewBuffer(nil), + metrics: metrics.Default, + } +} + +func (m *matcher) readNTimes(times int) (bool, error) { + var consumedInput bool + for i := 0; i < times; i++ { + n, err := m.input.Read(m.readBuffer) + m.pending.Write(m.readBuffer[:n]) + if n > 0 { + consumedInput = true + } + + if err != nil { + return consumedInput, err + } + + } + + return consumedInput, nil +} + +func (m *matcher) match(b []byte) (int, error) { + var consumed int + + for _, s := range m.toblockList { + if bytes.Contains(b, s.str) { + b = nil + return 0, proxy.ErrBlocked + } + } + consumed += len(b) + return consumed, nil + +} + +func (m *matcher) fill(requested int) error { + readSize := 1 + for m.ready.Len() < requested { + consumedInput, err := m.readNTimes(readSize) + if !consumedInput { + io.CopyBuffer(m.ready, m.pending, m.readBuffer) + return err + } + + if uint64(m.pending.Len()) > m.maxBufferSize { + switch m.maxBufferHandling { + case maxBufferAbort: + return ErrMatcherBufferFull + default: + _, err := m.match(m.pending.Bytes()) + if err != nil { + return err + } + m.pending.Reset() + readSize = 1 + } + } + + readSize *= 2 + } + return nil +} + +func (m *matcher) Read(p []byte) (int, error) { + if m.closed { + return 0, ErrClosed + } + + if m.ready.Len() == 0 && m.err != nil { + return 0, m.err + } + + if m.ready.Len() < len(p) { + m.err = m.fill(len(p)) + } + + if m.err == ErrMatcherBufferFull { + return 0, ErrMatcherBufferFull + } + + if m.err == proxy.ErrBlocked { + m.metrics.IncCounter("blocked.requests") + return 0, proxy.ErrBlocked + } + + n, _ := m.ready.Read(p) + + if n == 0 && len(p) > 0 && m.err != nil { + return 0, m.err + } + + n, err := m.match(p) + + if err != nil { + m.closed = true + + if err == proxy.ErrBlocked { + m.metrics.IncCounter("blocked.requests") + } + + return 0, err + } + + return n, nil +} + +// Close closes the undelrying reader if it implements io.Closer. +func (m *matcher) Close() error { + var err error + m.once.Do(func() { + m.closed = true + if c, ok := m.input.(io.Closer); ok { + err = c.Close() + } + }) + return err +} diff --git a/filters/filters.go b/filters/filters.go index 52f92163b7..d5a5a5e7e4 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -214,6 +214,7 @@ const ( RandomContentName = "randomContent" RepeatContentName = "repeatContent" BackendTimeoutName = "backendTimeout" + BlockName = "blockContent" LatencyName = "latency" BandwidthName = "bandwidth" ChunksName = "chunks" diff --git a/proxy/proxy.go b/proxy/proxy.go index 86473725f4..3bad1abee2 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -259,6 +259,7 @@ const ( ) var ( + ErrBlocked = errors.New("blocked string match found in body") errRouteLookupFailed = &proxyError{err: errRouteLookup} errCircuitBreakerOpen = &proxyError{ err: errors.New("circuit breaker open"), @@ -926,7 +927,14 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co ctx.proxySpan.LogKV("http_roundtrip", EndEvent) if err != nil { - p.tracing.setTag(ctx.proxySpan, ErrorTag, true) + + if errors.Is(err, ErrBlocked) { + p.tracing.setTag(ctx.proxySpan, BlockTag, true) + p.tracing.setTag(ctx.proxySpan, HTTPStatusCodeTag, uint16(http.StatusBadRequest)) + return nil, &proxyError{err: err, code: http.StatusBadRequest} + } else { + p.tracing.setTag(ctx.proxySpan, ErrorTag, true) + } // Check if the request has been cancelled or timed out // The roundtrip error `err` may be different: diff --git a/proxy/tracing.go b/proxy/tracing.go index 6881769c74..3cafde2128 100644 --- a/proxy/tracing.go +++ b/proxy/tracing.go @@ -10,6 +10,7 @@ const ( ClientRequestStateTag = "client.request" ComponentTag = "component" ErrorTag = "error" + BlockTag = "blocked" FlowIDTag = "flow_id" HostnameTag = "hostname" HTTPHostTag = "http.host" diff --git a/skipper.go b/skipper.go index bcd2ef82ec..d4f0b1eacf 100644 --- a/skipper.go +++ b/skipper.go @@ -31,6 +31,7 @@ import ( "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/apiusagemonitoring" "github.com/zalando/skipper/filters/auth" + block "github.com/zalando/skipper/filters/block" "github.com/zalando/skipper/filters/builtin" "github.com/zalando/skipper/filters/fadein" logfilter "github.com/zalando/skipper/filters/log" @@ -798,6 +799,9 @@ type Options struct { // MaxAuditBody sets the maximum read size of the body read by the audit log filter MaxAuditBody int + // MaxMatcherBufferSize sets the maximum read buffer size of blockContent filter defaults to 2MiB + MaxMatcherBufferSize uint64 + // EnableSwarm enables skipper fleet communication, required by e.g. // the cluster ratelimiter EnableSwarm bool @@ -1415,6 +1419,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { o.CustomFilters = append(o.CustomFilters, logfilter.NewAuditLog(o.MaxAuditBody), + block.NewBlockFilter(o.MaxMatcherBufferSize), auth.NewBearerInjector(sp), auth.NewJwtValidationWithOptions(tio), auth.TokenintrospectionWithOptions(auth.NewOAuthTokenintrospectionAnyClaims, tio), From c511dfcdad5232bf9dfc818e97888b84c9198e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 7 Sep 2022 10:28:25 +0200 Subject: [PATCH 0072/1047] enable govulncheck (#2072) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- .github/workflows/master.yaml | 1 + Makefile | 4 ++++ delivery.yaml | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 825258125e..24659837fd 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -21,4 +21,5 @@ jobs: - run: make check-race - run: make cicheck - run: make gosec + - run: make govulncheck - run: make publish-coverage diff --git a/Makefile b/Makefile index 7de5d11997..a1ce580b7f 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,7 @@ deps: ./etcd/install.sh $(TEST_ETCD_VERSION) @go install honnef.co/go/tools/cmd/staticcheck@latest @go install github.com/securego/gosec/v2/cmd/gosec@latest + @go install golang.org/x/vuln/cmd/govulncheck@latest vet: $(SOURCES) go vet $(PACKAGES) @@ -148,6 +149,9 @@ staticcheck: $(SOURCES) gosec: $(SOURCES) gosec -quiet -exclude="G101,G104,G304,G402" ./... +govulncheck: $(SOURCES) + govulncheck ./... + fmt: $(SOURCES) @gofmt -w -s $(SOURCES) diff --git a/delivery.yaml b/delivery.yaml index f388c1f797..a2874659da 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -35,7 +35,7 @@ pipeline: fi export IMAGE ARM_IMAGE ARM64_IMAGE MULTIARCH_IMAGE - make deps cicheck staticcheck gosec + make deps cicheck staticcheck gosec govulncheck git status git diff cd packaging && make docker.build.amd64 && git status && git diff && make docker.push.amd64 && make docker.push.multiarch From 6a0a59076ae4a67220ae9711980bc7527d3af011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 7 Sep 2022 16:01:31 +0200 Subject: [PATCH 0073/1047] Feature/scheduler filter fifo (#2044) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: fifo() scheduler filter to limit concurrency based on semaphore. Fifo is reused after routing updates and use rwmutex to guard reconfiguration. fix TestNewLifo fix staticcheck ST1005 ST1008 add filters.lifo/lifoGroup/fifo tests add scheduler.FifoQueue tests Signed-off-by: Sandor Szücs --- config/config.go | 3 + docs/reference/filters.md | 27 ++- filters/builtin/builtin.go | 1 + filters/filters.go | 1 + filters/scheduler/fifo.go | 152 +++++++++++++ filters/scheduler/fifo_test.go | 394 +++++++++++++++++++++++++++++++++ filters/scheduler/lifo_test.go | 189 +++++++++++++--- go.mod | 7 + go.sum | 15 ++ proxy/proxy.go | 9 + scheduler/fifo_test.go | 89 ++++++++ scheduler/scheduler.go | 321 ++++++++++++++++++++++++--- scheduler/scheduler_test.go | 150 +++++++++++-- skipper.go | 4 + 14 files changed, 1287 insertions(+), 75 deletions(-) create mode 100644 filters/scheduler/fifo.go create mode 100644 filters/scheduler/fifo_test.go create mode 100644 scheduler/fifo_test.go diff --git a/config/config.go b/config/config.go index 81e4b6c914..ee0e7b821e 100644 --- a/config/config.go +++ b/config/config.go @@ -58,6 +58,7 @@ type Config struct { Breakers breakerFlags `yaml:"breaker"` EnableRatelimiters bool `yaml:"enable-ratelimits"` Ratelimits ratelimitFlags `yaml:"ratelimits"` + EnableRouteFIFOMetrics bool `yaml:"enable-route-fifo-metrics"` EnableRouteLIFOMetrics bool `yaml:"enable-route-lifo-metrics"` MetricsFlavour *listFlag `yaml:"metrics-flavour"` FilterPlugins *pluginFlag `yaml:"filter-plugin"` @@ -328,6 +329,7 @@ func NewConfig() *Config { flag.Var(&cfg.Breakers, "breaker", breakerUsage) flag.BoolVar(&cfg.EnableRatelimiters, "enable-ratelimits", false, enableRatelimitsUsage) flag.Var(&cfg.Ratelimits, "ratelimits", ratelimitsUsage) + flag.BoolVar(&cfg.EnableRouteFIFOMetrics, "enable-route-fifo-metrics", false, "enable metrics for the individual route FIFO queues") flag.BoolVar(&cfg.EnableRouteLIFOMetrics, "enable-route-lifo-metrics", false, "enable metrics for the individual route LIFO queues") flag.Var(cfg.MetricsFlavour, "metrics-flavour", "Metrics flavour is used to change the exposed metrics format. Supported metric formats: 'codahale' and 'prometheus', you can select both of them") flag.Var(cfg.FilterPlugins, "filter-plugin", "set a custom filter plugins to load, a comma separated list of name and arguments") @@ -694,6 +696,7 @@ func (c *Config) ToOptions() skipper.Options { BreakerSettings: c.Breakers, EnableRatelimiters: c.EnableRatelimiters, RatelimitSettings: c.Ratelimits, + EnableRouteFIFOMetrics: c.EnableRouteFIFOMetrics, EnableRouteLIFOMetrics: c.EnableRouteLIFOMetrics, MetricsFlavours: c.MetricsFlavour.values, FilterPlugins: c.FilterPlugins.values, diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 5aa0cb506b..e9f2767612 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -2403,6 +2403,29 @@ E.g.: apiUsageMonitoring.custom.my-app.{unknown}.{unknown}.GET.{no-match}.*.*.http_count ``` +## fifo + +This Filter is similar to the [lifo](#lifo) filter in regards to +parameters and status codes. + +It turned out that `lifo()` filter can hurt performance at high load. +On AWS instance c6g.8xlarge [lifo](#lifo) filter had a limit of 21000 +requests per second on a single instance. The `fifo()` filter had not +hit a limit at 30000 requests per second. +If you use TCP-LIFO, then request processing is already in LIFO style. + +Parameters: + +* MaxConcurrency specifies how many goroutines are allowed to work on this queue (int) +* MaxQueueSize sets the queue size (int) +* Timeout sets the timeout to get request scheduled (time) + +Example: + +``` +fifo(100, 150, "10s") +``` + ## lifo This Filter changes skipper to handle the route with a bounded last in @@ -2414,7 +2437,9 @@ idea of a LIFO queue is based on Dropbox bandaid proxy, which is not opensource. Dropbox shared their idea in a [public blogpost](https://blogs.dropbox.com/tech/2018/03/meet-bandaid-the-dropbox-service-proxy/). All bounded scheduler filters will respond requests with server status error -codes in case of overrun. All scheduler filters return HTTP status code: +codes in case of overrun. + +All scheduler filters return HTTP status code: - 502, if the specified timeout is reached, because a request could not be scheduled fast enough - 503, if the queue is full diff --git a/filters/builtin/builtin.go b/filters/builtin/builtin.go index cf512dbda3..879170886c 100644 --- a/filters/builtin/builtin.go +++ b/filters/builtin/builtin.go @@ -208,6 +208,7 @@ func MakeRegistry() filters.Registry { accesslog.NewEnableAccessLog(), auth.NewForwardToken(), auth.NewForwardTokenField(), + scheduler.NewFifo(), scheduler.NewLIFO(), scheduler.NewLIFOGroup(), rfc.NewPath(), diff --git a/filters/filters.go b/filters/filters.go index d5a5a5e7e4..2fc998dd1d 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -288,6 +288,7 @@ const ( SetDynamicBackendScheme = "setDynamicBackendScheme" SetDynamicBackendUrl = "setDynamicBackendUrl" ApiUsageMonitoringName = "apiUsageMonitoring" + FifoName = "fifo" LifoName = "lifo" LifoGroupName = "lifoGroup" RfcPathName = "rfcPath" diff --git a/filters/scheduler/fifo.go b/filters/scheduler/fifo.go new file mode 100644 index 0000000000..f8d20fef09 --- /dev/null +++ b/filters/scheduler/fifo.go @@ -0,0 +1,152 @@ +package scheduler + +import ( + "fmt" + "net/http" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/scheduler" +) + +const ( + fifoKey string = "fifo" +) + +type ( + fifoSpec struct{} + fifoFilter struct { + config scheduler.Config + queue *scheduler.FifoQueue + } +) + +func NewFifo() filters.Spec { + return &fifoSpec{} +} + +func (*fifoSpec) Name() string { + return filters.FifoName +} + +// CreateFilter creates a fifoFilter, that will use a semaphore based +// queue for handling requests to limit concurrency of a route. The first +// parameter is maxConcurrency the second maxQueueSize and the third +// timeout. +func (s *fifoSpec) CreateFilter(args []interface{}) (filters.Filter, error) { + if len(args) != 3 { + return nil, filters.ErrInvalidFilterParameters + } + + cc, err := intArg(args[0]) + if err != nil { + return nil, err + } + if cc < 1 { + return nil, fmt.Errorf("maxconcurrency requires value >0, %w", filters.ErrInvalidFilterParameters) + } + + qs, err := intArg(args[1]) + if err != nil { + return nil, err + } + if qs < 0 { + return nil, fmt.Errorf("maxqueuesize requires value >=0, %w", filters.ErrInvalidFilterParameters) + } + + d, err := durationArg(args[2]) + if err != nil { + return nil, err + } + if d < 1*time.Millisecond { + return nil, fmt.Errorf("timeout requires value >=1ms, %w", filters.ErrInvalidFilterParameters) + } + + return &fifoFilter{ + config: scheduler.Config{ + MaxConcurrency: cc, + MaxQueueSize: qs, + Timeout: d, + }, + }, nil +} + +func (f *fifoFilter) Config() scheduler.Config { + return f.config +} + +func (f *fifoFilter) GetQueue() *scheduler.FifoQueue { + return f.queue +} + +func (f *fifoFilter) SetQueue(fq *scheduler.FifoQueue) { + f.queue = fq +} + +// Request is the filter.Filter interface implementation. Request will +// increase the number of inflight requests and respond to the caller, +// if the bounded queue returns an error. Status code by Error: +// +// - 503 if queue full +// - 502 if queue timeout +// - 500 if error unknown +func (f *fifoFilter) Request(ctx filters.FilterContext) { + c := ctx.Request().Context() + done, err := f.queue.Wait(c) + if err != nil { + if span := opentracing.SpanFromContext(c); span != nil { + ext.Error.Set(span, true) + span.LogKV("fifo error", fmt.Sprintf("Failed to wait for fifo queue: %v", err)) + } + log.Debugf("Failed to wait for fifo queue: %v", err) + + switch err { + case scheduler.ErrQueueFull: + ctx.Serve(&http.Response{ + StatusCode: http.StatusServiceUnavailable, + Status: "Queue Full - https://opensource.zalando.com/skipper/operation/operation/#scheduler", + }) + return + case scheduler.ErrQueueTimeout: + ctx.Serve(&http.Response{ + StatusCode: http.StatusBadGateway, + Status: "Queue Timeout - https://opensource.zalando.com/skipper/operation/operation/#scheduler", + }) + return + case scheduler.ErrClientCanceled: + // This case is handled in the proxy with status code 499 + return + + default: + log.Errorf("Unknown error in fifo() please create an issue https://github.com/zalando/skipper/issues/new/choose: %v", err) + ctx.Serve(&http.Response{ + StatusCode: http.StatusInternalServerError, + Status: "Unknown error in fifo https://opensource.zalando.com/skipper/operation/operation/#scheduler, please create an issue https://github.com/zalando/skipper/issues/new/choose", + }) + return + + } + } + + // ok + pending, _ := ctx.StateBag()[fifoKey].([]func()) + ctx.StateBag()[fifoKey] = append(pending, done) +} + +// Response will decrease the number of inflight requests to release +// the concurrency reservation for the request. +func (f *fifoFilter) Response(ctx filters.FilterContext) { + pending, ok := ctx.StateBag()[fifoKey].([]func()) + if !ok { + return + } + last := len(pending) - 1 + if last < 0 { + return + } + pending[last]() + ctx.StateBag()[fifoKey] = pending[:last] +} diff --git a/filters/scheduler/fifo_test.go b/filters/scheduler/fifo_test.go new file mode 100644 index 0000000000..ee22cc4a6c --- /dev/null +++ b/filters/scheduler/fifo_test.go @@ -0,0 +1,394 @@ +package scheduler + +import ( + "fmt" + "io" + "log" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/metrics/metricstest" + "github.com/zalando/skipper/proxy" + "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/routing/testdataclient" + "github.com/zalando/skipper/scheduler" + + vegeta "github.com/tsenart/vegeta/lib" +) + +func TestCreateFifoFilter(t *testing.T) { + for _, tt := range []struct { + name string + args []interface{} + wantParseErr bool + }{ + { + name: "fifo no args", + wantParseErr: true, + }, + { + name: "fifo 1 arg", + args: []interface{}{ + 3, + }, + wantParseErr: true, + }, + { + name: "fifo 2 args", + args: []interface{}{ + 3, + 5, + }, + wantParseErr: true, + }, + { + name: "fifo simple ok 3 args", + args: []interface{}{ + 3, + 5, + "1s", + }, + }, + { + name: "fifo wrong type arg1", + args: []interface{}{ + "3", + 5, + "1s", + }, + wantParseErr: true, + }, + { + name: "fifo wrong type arg2", + args: []interface{}{ + 3, + "5", + "1s", + }, + wantParseErr: true, + }, + { + name: "fifo wrong time.Duration string arg3", + args: []interface{}{ + 3, + 5, + "1sa", + }, + wantParseErr: true, + }, + { + name: "fifo wrong type arg3", + args: []interface{}{ + 3, + 5, + 1, + }, + wantParseErr: true, + }, + { + name: "fifo too many args", + args: []interface{}{ + 3, + 5, + "1s", + "foo", + }, + wantParseErr: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + spec := &fifoSpec{} + ff, err := spec.CreateFilter(tt.args) + if err != nil && !tt.wantParseErr { + t.Fatalf("Failed to parse filter: %v", err) + } + if err == nil && tt.wantParseErr { + t.Fatal("Failed to get wanted error on create filter") + } + + if _, ok := ff.(*fifoFilter); !ok && err == nil { + t.Fatal("Failed to convert filter to *fifoFilter") + } + }) + } +} + +func TestFifo(t *testing.T) { + for _, tt := range []struct { + name string + args []interface{} + freq int + per time.Duration + backendTime time.Duration + clientTimeout time.Duration + wantConfig scheduler.Config + wantParseErr bool + wantOkRate float64 + epsilon float64 + }{ + { + name: "fifo defaults", + args: []interface{}{}, + wantParseErr: true, + }, + { + name: "fifo simple ok", + args: []interface{}{ + 3, + 5, + "1s", + }, + freq: 20, + per: 100 * time.Millisecond, + backendTime: 1 * time.Millisecond, + clientTimeout: time.Second, + wantConfig: scheduler.Config{ + MaxConcurrency: 3, + MaxQueueSize: 5, + Timeout: time.Second, + }, + wantParseErr: false, + wantOkRate: 1.0, + epsilon: 1, + }, + { + name: "fifo with reaching max concurrency and queue timeouts", + args: []interface{}{ + 3, + 5, + "10ms", + }, + freq: 200, + per: 100 * time.Millisecond, + backendTime: 10 * time.Millisecond, + clientTimeout: time.Second, + wantConfig: scheduler.Config{ + MaxConcurrency: 3, + MaxQueueSize: 5, + Timeout: 10 * time.Millisecond, + }, + wantParseErr: false, + wantOkRate: 0.1, + epsilon: 1, + }, + { + name: "fifo with reaching max concurrency and queue full", + args: []interface{}{ + 1, + 1, + "250ms", + }, + freq: 200, + per: 100 * time.Millisecond, + backendTime: 100 * time.Millisecond, + clientTimeout: time.Second, + wantConfig: scheduler.Config{ + MaxConcurrency: 1, + MaxQueueSize: 1, + Timeout: 250 * time.Millisecond, + }, + wantParseErr: false, + wantOkRate: 0.001, + epsilon: 1, + }, + } { + t.Run(tt.name, func(t *testing.T) { + fs := NewFifo() + if fs.Name() != filters.FifoName { + t.Fatalf("Failed to get name got %s want %s", fs.Name(), filters.FifoName) + } + + // no parse error + ff, err := fs.CreateFilter(tt.args) + if err != nil && !tt.wantParseErr { + t.Fatalf("Failed to parse filter: %v", err) + } + if err == nil && tt.wantParseErr { + t.Fatalf("want parse error but hav no: %v", err) + } + if tt.wantParseErr { + return + } + + // validate config + if f, ok := ff.(*fifoFilter); ok { + config := f.Config() + if config != tt.wantConfig { + t.Fatalf("Failed to get Config, got: %v, want: %v", config, tt.wantConfig) + } + if f.queue != f.GetQueue() { + t.Fatal("Failed to get expected queue") + } + } + + metrics := &metricstest.MockMetrics{} + reg := scheduler.RegistryWith(scheduler.Options{ + Metrics: metrics, + EnableRouteFIFOMetrics: true, + }) + defer reg.Close() + + fr := make(filters.Registry) + fr.Register(fs) + + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(tt.backendTime) + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + })) + + var fmtStr string + switch len(tt.args) { + case 0: + fmtStr = `aroute: * -> fifo() -> "%s"` + case 1: + fmtStr = `aroute: * -> fifo(%v) -> "%s"` + case 2: + fmtStr = `aroute: * -> fifo(%v, %v) -> "%s"` + case 3: + fmtStr = `aroute: * -> fifo(%v, %v, "%v") -> "%s"` + default: + t.Fatalf("Test not possible %d >3", len(tt.args)) + } + + args := append(tt.args, backend.URL) + doc := fmt.Sprintf(fmtStr, args...) + t.Logf("%s", doc) + + dc, err := testdataclient.NewDoc(doc) + if err != nil { + t.Fatalf("Failed to create testdataclient: %v", err) + } + ro := routing.Options{ + SignalFirstLoad: true, + FilterRegistry: fr, + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{reg}, + } + rt := routing.New(ro) + defer rt.Close() + <-rt.FirstLoad() + + tracer := &testTracer{MockTracer: mocktracer.New()} + pr := proxy.WithParams(proxy.Params{ + Routing: rt, + OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + }) + defer pr.Close() + + ts := httptest.NewServer(pr) + defer ts.Close() + + reqURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("Failed to parse url %s: %v", ts.URL, err) + } + + rsp, err := http.DefaultClient.Get(reqURL.String()) + if err != nil { + t.Fatalf("Failed to get response from %s: %v", reqURL.String(), err) + } + if rsp.StatusCode != http.StatusOK { + t.Fatalf("Failed to get valid response from endpoint: %d", rsp.StatusCode) + } + + va := newVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) + // buf := bytes.NewBuffer(make([]byte, 0, 1024)) + // va.Attack(buf, 3*time.Second, tt.name) + va.Attack(io.Discard, 1*time.Second, tt.name) + //t.Logf("buf: %v", buf.String()) + + t.Logf("Success [0..1]: %0.2f", va.metrics.Success) + t.Logf("requests: %d", va.metrics.Requests) + got := va.metrics.Success * float64(va.metrics.Requests) + want := tt.wantOkRate * float64(va.metrics.Requests) + if got < want { + t.Fatalf("OK rate too low got 0 { + t.Fatal("no OK") + } + if !ok && tt.wantOkRate == 0 { + count499, ok := va.metrics.StatusCodes["0"] + if !ok || va.metrics.Requests != uint64(count499) { + t.Fatalf("want all 499 client cancel but %d != %d", va.metrics.Requests, count499) + } + } + if float64(countOK) < want { + t.Fatalf("OK too low got %s() -> "%s"`, args...) + case 3: + doc = fmt.Sprintf(`aroute: * -> %s(%v) -> "%s"`, args...) + case 4: + doc = fmt.Sprintf(`aroute: * -> %s(%v, %v) -> "%s"`, args...) + case 5: + doc = fmt.Sprintf(`aroute: * -> %s(%v, %v, "%v") -> "%s"`, args...) + case 6: + doc = fmt.Sprintf(`aroute: * -> %s("%v", %v, %v, "%v") -> "%s"`, args...) + default: + t.Fatalf("(%d): %v", len(args), args) + } + println("doc:", doc) + t.Logf("doc: %s", doc) + + dc, err := testdataclient.NewDoc(doc) + if err != nil { + t.Fatalf("Failed to create testdataclient: %v", err) + } + + metrics := &metricstest.MockMetrics{} + reg := scheduler.RegistryWith(scheduler.Options{ + Metrics: metrics, + EnableRouteLIFOMetrics: true, + }) + defer reg.Close() + fr := make(filters.Registry) fr.Register(l) - r := &eskip.Route{Filters: []*eskip.Filter{{Name: l.Name(), Args: tt.args}}, Backend: backend.URL} - proxy := proxytest.New(fr, r) - reqURL, err := url.Parse(proxy.URL) + ro := routing.Options{ + SignalFirstLoad: true, + FilterRegistry: fr, + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{reg}, + } + rt := routing.New(ro) + defer rt.Close() + <-rt.FirstLoad() + + pr := proxy.WithParams(proxy.Params{Routing: rt}) + defer pr.Close() + + ts := httptest.NewServer(pr) + defer ts.Close() + + reqURL, err := url.Parse(ts.URL) if err != nil { - t.Errorf("Failed to parse url %s: %v", proxy.URL, err) + t.Fatalf("Failed to parse url %s: %v", ts.URL, err) } + req, err := http.NewRequest("GET", reqURL.String(), nil) if err != nil { - t.Error(err) - return + t.Fatal(err) } for i := 0; i < config.MaxQueueSize+config.MaxConcurrency+1; i++ { @@ -279,7 +416,7 @@ func TestNewLIFO(t *testing.T) { defer rsp.Body.Close() if rsp.StatusCode != tt.wantCode { - t.Errorf("lifo filter failed got=%d, expected=%d, route=%s", rsp.StatusCode, tt.wantCode, r) + t.Errorf("lifo filter failed got=%d, expected=%d", rsp.StatusCode, tt.wantCode) buf := make([]byte, rsp.ContentLength) if n, err := rsp.Body.Read(buf); err != nil || int64(n) != rsp.ContentLength { t.Errorf("Failed to read content: %v, %d, want: %d", err, int64(n), rsp.ContentLength) diff --git a/go.mod b/go.mod index 2156d7a317..db2bdebc32 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/szuecs/routegroup-client v0.21.0 github.com/testcontainers/testcontainers-go v0.12.0 github.com/tidwall/gjson v1.12.1 + github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible github.com/yookoala/gofast v0.6.0 @@ -67,9 +68,11 @@ require ( github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect github.com/containerd/cgroups v1.0.3 // indirect github.com/containerd/containerd v1.6.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 // indirect github.com/docker/distribution v2.8.0+incompatible // indirect github.com/docker/docker v20.10.11+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -88,10 +91,12 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-sockaddr v1.0.0 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect + github.com/influxdata/tdigest v0.0.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect github.com/looplab/fsm v0.3.0 // indirect github.com/magiconair/properties v1.8.5 // indirect + github.com/mailru/easyjson v0.7.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/moby/sys/mount v0.2.0 // indirect github.com/moby/sys/mountinfo v0.5.0 // indirect @@ -109,6 +114,7 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/shirou/gopsutil/v3 v3.21.2 // indirect + github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.9 // indirect @@ -119,6 +125,7 @@ require ( golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect google.golang.org/grpc v1.43.0 // indirect diff --git a/go.sum b/go.sum index 03e99677d6..bf7c3f5361 100644 --- a/go.sum +++ b/go.sum @@ -130,6 +130,8 @@ github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= +github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= @@ -299,6 +301,8 @@ github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBl github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 h1:XOPLOMn/zT4jIgxfxSsoXPxkrzz0FaCHwp33x5POJ+Q= +github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 h1:0wH6nO9QEa02Qx8sIQGw6ieKdz+BXjpccSOo9vXNl4U= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA= github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1 h1:De28BM16VaADXA2/F5qY+khGHWZkT70zLijZw8YYIYY= @@ -575,6 +579,8 @@ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= +github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= github.com/instana/go-sensor v1.38.3 h1:/PdHEDveLmUCvK+O6REvSv8kKljX8vaj9JMjMeCHAWk= github.com/instana/go-sensor v1.38.3/go.mod h1:E42MelHWFz11qqaLwvgt0j98v2s2O/bq22UDkGaG0Gg= github.com/instana/testify v1.6.2-0.20200721153833-94b1851f4d65 h1:T25FL3WEzgmKB0m6XCJNZ65nw09/QIp3T1yXr487D+A= @@ -632,6 +638,7 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -859,6 +866,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d h1:X4+kt6zM/OVO6gbJdAfJR60MGPsqCzbtXNnjoGqdfAs= +github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -900,6 +909,8 @@ github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2bi github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tsenart/vegeta v12.7.0+incompatible h1:sGlrv11EMxQoKOlDuMWR23UdL90LE5VlhKw/6PWkZmU= +github.com/tsenart/vegeta v12.7.0+incompatible/go.mod h1:Smz/ZWfhKRcyDDChZkG3CyTHdj87lHzio/HOCkbndXM= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= @@ -995,6 +1006,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -1288,7 +1300,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= diff --git a/proxy/proxy.go b/proxy/proxy.go index 3bad1abee2..e23fd23fe3 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1054,7 +1054,16 @@ func (p *Proxy) do(ctx *context) error { return errMaxLoopbacksReached } + // this can be deleted after fixing + // https://github.com/zalando/skipper/issues/1238 problem + // happens if we get proxy errors, for example connect errors, + // which would block responses until fifo() timeouts. defer func() { + pendingFIFO, _ := ctx.StateBag()[scheduler.FIFOKey].([]func()) + for _, done := range pendingFIFO { + done() + } + pendingLIFO, _ := ctx.StateBag()[scheduler.LIFOKey].([]func()) for _, done := range pendingLIFO { done() diff --git a/scheduler/fifo_test.go b/scheduler/fifo_test.go new file mode 100644 index 0000000000..29fd7bd137 --- /dev/null +++ b/scheduler/fifo_test.go @@ -0,0 +1,89 @@ +package scheduler + +import ( + "context" + "testing" + "time" + + "github.com/zalando/skipper/metrics" +) + +func TestFifo(t *testing.T) { + waitForStatus := func(t *testing.T, fq *FifoQueue, s QueueStatus) { + t.Helper() + timeout := time.After(120 * time.Millisecond) + for { + time.Sleep(time.Millisecond) + if fq != nil && fq.Status() == s { + return + } + + select { + case <-timeout: + t.Fatal("failed to reach status") + default: + } + } + } + + t.Run("queue full", func(t *testing.T) { + reg := RegistryWith(Options{ + MetricsUpdateTimeout: 100 * time.Millisecond, + EnableRouteFIFOMetrics: true, + Metrics: metrics.Default, + }) + cfg := Config{ + MaxConcurrency: 1, + MaxQueueSize: 2, + Timeout: 500 * time.Millisecond, + CloseTimeout: 1000 * time.Millisecond, + } + fq := reg.newFifoQueue("", cfg) + ctx := context.Background() + f, err := fq.Wait(ctx) + if err != nil { + t.Fatalf("Failed to call wait: %v", err) + } + f() + + go fq.Wait(ctx) + go fq.Wait(ctx) + go fq.Wait(ctx) + waitForStatus(t, fq, QueueStatus{ActiveRequests: 1, QueuedRequests: 1}) + + ch := make(chan struct{}) + go func() { + ch <- struct{}{} + fq.Wait(ctx) + }() + <-ch + + f, err = fq.Wait(ctx) + if err != ErrQueueFull { + t.Fatalf("Failed to get ErrQueueFull: %v", err) + } + if err == nil { + f() + } + waitForStatus(t, fq, QueueStatus{ActiveRequests: 1, QueuedRequests: 2}) + }) + + t.Run("semaphore actions and close", func(t *testing.T) { + reg := NewRegistry() + cfg := Config{ + MaxConcurrency: 1, + MaxQueueSize: 2, + Timeout: 200 * time.Millisecond, + CloseTimeout: 100 * time.Millisecond, + } + fq := reg.newFifoQueue("foo", cfg) + ctx := context.Background() + f, err := fq.Wait(ctx) + if err == nil { + f() + } + fq.close() + fq.close() + }) + +} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 5fe06da0d1..1be2a03634 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -3,6 +3,8 @@ package scheduler import ( + "context" + "errors" "fmt" "sync" "time" @@ -13,13 +15,23 @@ import ( "github.com/zalando/skipper/filters" "github.com/zalando/skipper/metrics" "github.com/zalando/skipper/routing" + "go.uber.org/atomic" + "golang.org/x/sync/semaphore" ) // note: Config must stay comparable because it is used to detect changes in route specific LIFO config const ( - // Key used during routing to pass lifo values from the filters to the proxy. + // LIFOKey used during routing to pass lifo values from the filters to the proxy. LIFOKey = "lifo" + // FIFOKey used during routing to pass fifo values from the filters to the proxy. + FIFOKey = "fifo" +) + +var ( + ErrQueueFull = errors.New("queue full") + ErrQueueTimeout = errors.New("queue timeout") + ErrClientCanceled = errors.New("client canceled") ) // Config can be used to provide configuration of the registry. @@ -69,17 +81,114 @@ type Queue struct { queuedRequestsMetricsKey string } +// FifoQueue objects implement a FIFO queue for handling requests, +// with a maximum allowed concurrency and queue size. Currently, they +// can be used from the fifo filters in the filters/scheduler package +// only. +type FifoQueue struct { + queue *fifoQueue + config Config + metrics metrics.Metrics + activeRequestsMetricsKey string + errorFullMetricsKey string + errorOtherMetricsKey string + errorTimeoutMetricsKey string + queuedRequestsMetricsKey string +} + +type fifoQueue struct { + mu sync.RWMutex + counter *atomic.Uint64 + sem *semaphore.Weighted + timeout time.Duration + maxQueueSize uint64 + maxConcurrency uint64 + closed bool +} + +func (fq *fifoQueue) status() QueueStatus { + fq.mu.RLock() + maxConcurrency := fq.maxConcurrency + fq.mu.RUnlock() + + all := fq.counter.Load() + + var queued, active uint64 + if all > maxConcurrency { + queued = all - maxConcurrency + active = maxConcurrency + } else { + queued = 0 + active = all + } + + return QueueStatus{ + ActiveRequests: int(active), + QueuedRequests: int(queued), + Closed: fq.closed, + } +} + +// Reconfigure does nothing, because it creates a data race that would +// require more mutex than we want to have +func (fq *fifoQueue) reconfigure(c Config) { + fq.mu.Lock() + defer fq.mu.Unlock() + fq.maxConcurrency = uint64(c.MaxConcurrency) + fq.maxQueueSize = uint64(c.MaxQueueSize) + fq.timeout = c.Timeout +} + +func (fq *fifoQueue) wait(ctx context.Context) (func(), error) { + fq.mu.RLock() + maxConcurrency := fq.maxConcurrency + maxQueueSize := fq.maxQueueSize + timeout := fq.timeout + fq.mu.RUnlock() + + // handle queue + all := fq.counter.Inc() + defer fq.counter.Dec() + // queue full? + if all > maxConcurrency+maxQueueSize { + return nil, ErrQueueFull + } + + // limit concurrency + c, done := context.WithTimeout(ctx, timeout) + defer done() + if err := fq.sem.Acquire(c, 1); err != nil { + switch err { + case context.DeadlineExceeded: + return nil, ErrQueueTimeout + case context.Canceled: + return nil, ErrClientCanceled + default: + // does not exist yet in Go stdlib as of Go1.18.4 + return nil, err + } + } + return func() { + fq.sem.Release(1) + }, nil + +} + // Options provides options for the registry. type Options struct { - // MetricsUpdateTimeout defines the frequency of how often the LIFO metrics - // are updated when they are enabled. Defaults to 1s. + // MetricsUpdateTimeout defines the frequency of how often the + // FIFO and LIFO metrics are updated when they are enabled. + // Defaults to 1s. MetricsUpdateTimeout time.Duration // EnableRouteLIFOMetrics enables collecting metrics about the LIFO queues. EnableRouteLIFOMetrics bool - // Metrics must be provided to the registry in order to collect the LIFO metrics. + // EnableRouteFIFOMetrics enables collecting metrics about the FIFO queues. + EnableRouteFIFOMetrics bool + + // Metrics must be provided to the registry in order to collect the FIFO and LIFO metrics. Metrics metrics.Metrics } @@ -98,9 +207,11 @@ type Registry struct { measuring bool quit chan struct{} - mu sync.Mutex - queues map[queueId]*Queue - deleted map[*Queue]time.Time + mu sync.Mutex + lifoQueues map[queueId]*Queue + lifoDeleted map[*Queue]time.Time + fifoQueues map[queueId]*FifoQueue + fifoDeleted map[*FifoQueue]time.Time } type queueId struct { @@ -111,6 +222,22 @@ type queueId struct { // Amount of time to wait before closing the deleted queues var queueCloseDelay = 1 * time.Minute +// FIFOFilter is the interface that needs to be implemented by the filters that +// use a FIFO queue maintained by the registry. +type FIFOFilter interface { + + // SetQueue will be used by the registry to pass in the right queue to + // the filter. + SetQueue(*FifoQueue) + + // GetQueue is currently used only by tests. + GetQueue() *FifoQueue + + // Config will be called by the registry once during processing the + // routing to get the right queue settings from the filter. + Config() Config +} + // LIFOFilter is the interface that needs to be implemented by the filters that // use a LIFO queue maintained by the registry. type LIFOFilter interface { @@ -140,6 +267,47 @@ type GroupedLIFOFilter interface { HasConfig() bool } +// Wait blocks until a request can be processed or needs to be +// rejected. It returns done() and an error. When it can be +// processed, calling done indicates that it has finished. It is +// mandatory to call done() the request was processed. When the +// request needs to be rejected, an error will be returned and done +// will be nil. +func (fq *FifoQueue) Wait(ctx context.Context) (func(), error) { + f, err := fq.queue.wait(ctx) + if fq.metrics != nil { + switch err { + case ErrQueueFull: + fq.metrics.IncCounter(fq.errorFullMetricsKey) + case ErrQueueTimeout: + fq.metrics.IncCounter(fq.errorTimeoutMetricsKey) + case ErrClientCanceled: + // This case is handled in the proxy with status code 499 + default: + fq.metrics.IncCounter(fq.errorOtherMetricsKey) + } + } + return f, err +} + +// Status returns the current status of a queue. +func (fq *FifoQueue) Status() QueueStatus { + return fq.queue.status() +} + +// Config returns the configuration that the queue was created with. +func (fq *FifoQueue) Config() Config { + return fq.config +} + +func (fq *FifoQueue) reconfigure() { + fq.queue.reconfigure(fq.config) +} + +func (fq *FifoQueue) close() { + fq.queue.closed = true +} + // Wait blocks until a request can be processed or needs to be rejected. // When it can be processed, calling done indicates that it has finished. // It is mandatory to call done() the request was processed. When the @@ -194,10 +362,12 @@ func RegistryWith(o Options) *Registry { } return &Registry{ - options: o, - quit: make(chan struct{}), - queues: make(map[queueId]*Queue), - deleted: make(map[*Queue]time.Time), + options: o, + quit: make(chan struct{}), + fifoQueues: make(map[queueId]*FifoQueue), + fifoDeleted: make(map[*FifoQueue]time.Time), + lifoQueues: make(map[queueId]*Queue), + lifoDeleted: make(map[*Queue]time.Time), } } @@ -206,11 +376,57 @@ func NewRegistry() *Registry { return RegistryWith(Options{}) } +func (r *Registry) getFifoQueue(id queueId, c Config) *FifoQueue { + r.mu.Lock() + defer r.mu.Unlock() + + fq, ok := r.fifoQueues[id] + if ok { + if fq.config != c { + fq.config = c + fq.reconfigure() + } + } else { + fq = r.newFifoQueue(id.name, c) + r.fifoQueues[id] = fq + } + return fq +} + +func (r *Registry) newFifoQueue(name string, c Config) *FifoQueue { + q := &FifoQueue{ + config: c, + queue: &fifoQueue{ + counter: atomic.NewUint64(0), + sem: semaphore.NewWeighted(int64(c.MaxConcurrency)), + maxConcurrency: uint64(c.MaxConcurrency), + maxQueueSize: uint64(c.MaxQueueSize), + timeout: c.Timeout, + }, + } + + if r.options.EnableRouteFIFOMetrics { + if name == "" { + name = "unknown" + } + + q.activeRequestsMetricsKey = fmt.Sprintf("fifo.%s.active", name) + q.queuedRequestsMetricsKey = fmt.Sprintf("fifo.%s.queued", name) + q.errorFullMetricsKey = fmt.Sprintf("fifo.%s.error.full", name) + q.errorOtherMetricsKey = fmt.Sprintf("fifo.%s.error.other", name) + q.errorTimeoutMetricsKey = fmt.Sprintf("fifo.%s.error.timeout", name) + q.metrics = r.options.Metrics + r.measure() + } + + return q +} + func (r *Registry) getQueue(id queueId, c Config) *Queue { r.mu.Lock() defer r.mu.Unlock() - q, ok := r.queues[id] + q, ok := r.lifoQueues[id] if ok { if q.config != c { q.config = c @@ -218,7 +434,7 @@ func (r *Registry) getQueue(id queueId, c Config) *Queue { } } else { q = r.newQueue(id.name, c) - r.queues[id] = q + r.lifoQueues[id] = q } return q } @@ -258,17 +474,31 @@ func (r *Registry) deleteUnused(inUse map[queueId]struct{}) { now := time.Now() closeCutoff := now.Add(-queueCloseDelay) - for q, deleted := range r.deleted { + // fifo + for q, deleted := range r.fifoDeleted { if deleted.Before(closeCutoff) { - delete(r.deleted, q) + delete(r.fifoDeleted, q) q.close() } } + for id, q := range r.fifoQueues { + if _, ok := inUse[id]; !ok { + delete(r.fifoQueues, id) + r.fifoDeleted[q] = now + } + } - for id, q := range r.queues { + // lifo + for q, deleted := range r.lifoDeleted { + if deleted.Before(closeCutoff) { + delete(r.lifoDeleted, q) + q.close() + } + } + for id, q := range r.lifoQueues { if _, ok := inUse[id]; !ok { - delete(r.queues, id) - r.deleted[q] = now + delete(r.lifoQueues, id) + r.lifoDeleted[q] = now } } } @@ -285,11 +515,28 @@ type registryPreProcessor struct{} func (registryPreProcessor) Do(routes []*eskip.Route) []*eskip.Route { for _, r := range routes { lifoCount := 0 + fifoCount := 0 for _, f := range r.Filters { - if f.Name == filters.LifoName { + switch f.Name { + case filters.FifoName: + fifoCount++ + case filters.LifoName: lifoCount++ } } + // remove all but last fifo instances + if fifoCount > 1 { + old := r.Filters + r.Filters = make([]*eskip.Filter, 0, len(old)-fifoCount+1) + for _, f := range old { + if fifoCount > 1 && f.Name == filters.FifoName { + log.Debugf("Removing non-last %v from %s", f, r.Id) + fifoCount-- + } else { + r.Filters = append(r.Filters, f) + } + } + } // remove all but last lifo instances if lifoCount > 1 { old := r.Filters @@ -317,8 +564,15 @@ func (r *Registry) Do(routes []*routing.Route) []*routing.Route { for i, ri := range routes { rr[i] = ri - var lifoCount int for _, fi := range ri.Filters { + if ff, ok := fi.Filter.(FIFOFilter); ok { + id := queueId{ri.Id, false} + inUse[id] = struct{}{} + fq := r.getFifoQueue(id, ff.Config()) + ff.SetQueue(fq) + continue + } + if glf, ok := fi.Filter.(GroupedLIFOFilter); ok { groupName := glf.Group() groups[groupName] = append(groups[groupName], glf) @@ -330,8 +584,6 @@ func (r *Registry) Do(routes []*routing.Route) []*routing.Route { continue } - lifoCount++ - id := queueId{ri.Id, false} inUse[id] = struct{}{} @@ -339,10 +591,6 @@ func (r *Registry) Do(routes []*routing.Route) []*routing.Route { lf.SetQueue(q) } - - if lifoCount > 1 { - log.Warnf("Found multiple lifo filters on route: %q", ri.Id) - } } for name, group := range groups { @@ -402,7 +650,13 @@ func (r *Registry) updateMetrics() { r.mu.Lock() defer r.mu.Unlock() - for _, q := range r.queues { + for _, q := range r.fifoQueues { + s := q.Status() + r.options.Metrics.UpdateGauge(q.activeRequestsMetricsKey, float64(s.ActiveRequests)) + r.options.Metrics.UpdateGauge(q.queuedRequestsMetricsKey, float64(s.QueuedRequests)) + } + + for _, q := range r.lifoQueues { s := q.Status() r.options.Metrics.UpdateGauge(q.activeRequestsMetricsKey, float64(s.ActiveRequests)) r.options.Metrics.UpdateGauge(q.queuedRequestsMetricsKey, float64(s.QueuedRequests)) @@ -420,13 +674,18 @@ func (r *Registry) Close() { r.mu.Lock() defer r.mu.Unlock() - for q := range r.deleted { - delete(r.deleted, q) + for q := range r.fifoDeleted { + delete(r.fifoDeleted, q) + q.close() + } + + for q := range r.lifoDeleted { + delete(r.lifoDeleted, q) q.close() } - for id, q := range r.queues { - delete(r.queues, id) + for id, q := range r.lifoQueues { + delete(r.lifoQueues, id) q.close() } diff --git a/scheduler/scheduler_test.go b/scheduler/scheduler_test.go index cfba7a298a..74598fc49e 100644 --- a/scheduler/scheduler_test.go +++ b/scheduler/scheduler_test.go @@ -35,6 +35,11 @@ func TestScheduler(t *testing.T) { doc: `r1: * -> setPath("/bar") -> "http://www.example.org"`, wantErr: false, }, + { + name: "one scheduler filter fifo", + doc: `f2: * -> fifo(10, 12, "10s") -> "http://www.example.org"`, + wantErr: false, + }, { name: "one scheduler filter lifo", doc: `l2: * -> lifo(10, 12, "10s") -> "http://www.example.org"`, @@ -45,6 +50,11 @@ func TestScheduler(t *testing.T) { doc: `r2: * -> lifoGroup("r2", 10, 12, "10s") -> "http://www.example.org"`, wantErr: false, }, + { + name: "multiple filters with one scheduler filter fifo", + doc: `f3: * -> setPath("/bar") -> fifo(10, 12, "10s") -> setRequestHeader("X-Foo", "bar") -> "http://www.example.org"`, + wantErr: false, + }, { name: "multiple filters with one scheduler filter lifo", doc: `l3: * -> setPath("/bar") -> lifo(10, 12, "10s") -> setRequestHeader("X-Foo", "bar") -> "http://www.example.org"`, @@ -55,6 +65,12 @@ func TestScheduler(t *testing.T) { doc: `r3: * -> setPath("/bar") -> lifoGroup("r3", 10, 12, "10s") -> setRequestHeader("X-Foo", "bar") -> "http://www.example.org"`, wantErr: false, }, + { + name: "multiple routes with fifo filters do not interfere", + doc: `f4: Path("/f4") -> setPath("/bar") -> fifo(10, 12, "10s") -> "http://www.example.org"; l5: Path("/f5") -> setPath("/foo") -> fifo(15, 2, "11s") -> setRequestHeader("X-Foo", "bar")-> "http://www.example.org";`, + paths: [][]string{{"f4"}, {"f5"}}, + wantErr: false, + }, { name: "multiple routes with lifo filters do not interfere", doc: `l4: Path("/l4") -> setPath("/bar") -> lifo(10, 12, "10s") -> "http://www.example.org"; l5: Path("/l5") -> setPath("/foo") -> lifo(15, 2, "11s") -> setRequestHeader("X-Foo", "bar")-> "http://www.example.org";`, @@ -103,10 +119,25 @@ func TestScheduler(t *testing.T) { if f == nil && !tt.wantErr { t.Fatalf("Filter is nil but we do not expect an error") } + if ff, ffOk := f.Filter.(scheduler.FIFOFilter); ffOk { + cfg := ff.Config() + queue := ff.GetQueue() + if queue == nil { + t.Errorf("FifoQueue is nil") + } + + if cfg != queue.Config() { + t.Errorf("Failed to get fifo queue with configuration, want: %v, got: %v", cfg, queue) + } + + continue + } + lf, ok := f.Filter.(scheduler.LIFOFilter) if !ok { continue } + cfg := lf.Config() queue := lf.GetQueue() if queue == nil { @@ -120,6 +151,7 @@ func TestScheduler(t *testing.T) { } queuesMap := make(map[string][]*scheduler.Queue) + fifoQueuesMap := make(map[string][]*scheduler.FifoQueue) for _, group := range tt.paths { key := group[0] @@ -135,6 +167,21 @@ func TestScheduler(t *testing.T) { t.Fatalf("Filter is nil but we do not expect an error") } + if ff, ffOk := f.Filter.(scheduler.FIFOFilter); ffOk { + cfg := ff.Config() + queue := ff.GetQueue() + if queue == nil { + t.Errorf("FifoQueue is nil") + } + + if cfg != queue.Config() { + t.Errorf("Failed to get fifo queue with configuration, want: %v, got: %v", cfg, queue) + } + + fifoQueuesMap[key] = append(fifoQueuesMap[key], queue) + + } + lf, ok := f.Filter.(scheduler.LIFOFilter) if !ok { continue @@ -154,11 +201,19 @@ func TestScheduler(t *testing.T) { } } - if len(queuesMap[key]) != len(group) { - t.Errorf("Failed to get the right group size %v != %v", len(queuesMap[key]), len(group)) + if len(queuesMap[key]) != len(group) && len(fifoQueuesMap[key]) != len(group) { + t.Errorf("Failed to get the right group size %v != %v && %v != %v", len(queuesMap[key]), len(group), len(fifoQueuesMap[key]), len(group)) } } // check pointers to queue are the same for same group + for k, queues := range fifoQueuesMap { + firstQueue := queues[0] + for _, queue := range queues { + if queue != firstQueue { + t.Errorf("Unexpected different fifo queue in group: %s", k) + } + } + } for k, queues := range queuesMap { firstQueue := queues[0] for _, queue := range queues { @@ -168,6 +223,13 @@ func TestScheduler(t *testing.T) { } } // check pointers to queue of different groups are different + diffFifoQueues := make(map[*scheduler.FifoQueue]struct{}) + for _, queues := range fifoQueuesMap { + diffFifoQueues[queues[0]] = struct{}{} + } + if len(diffFifoQueues) != len(fifoQueuesMap) { + t.Error("Unexpected got pointer to the same fifoqueue for different group") + } diffQueues := make(map[*scheduler.Queue]struct{}) for _, queues := range queuesMap { diffQueues[queues[0]] = struct{}{} @@ -181,11 +243,15 @@ func TestScheduler(t *testing.T) { } func TestConfig(t *testing.T) { - waitForStatus := func(t *testing.T, q *scheduler.Queue, s scheduler.QueueStatus) { + waitForStatus := func(t *testing.T, fq *scheduler.FifoQueue, q *scheduler.Queue, s scheduler.QueueStatus) { t.Helper() timeout := time.After(120 * time.Millisecond) for { - if q.Status() == s { + if q != nil && q.Status() == s { + return + } + + if fq != nil && fq.Status() == s { return } @@ -239,6 +305,13 @@ func TestConfig(t *testing.T) { return f.Filter.(scheduler.LIFOFilter).GetQueue() } + getFifoQueue := func(path string, rt *routing.Routing) *scheduler.FifoQueue { + req := &http.Request{URL: &url.URL{Path: path}} + r, _ := rt.Route(req) + f := r.Filters[0] + return f.Filter.(scheduler.FIFOFilter).GetQueue() + } + t.Run("group config applied", func(t *testing.T) { const doc = ` g1: Path("/one") -> lifoGroup("g", 2, 2) -> ; @@ -270,10 +343,37 @@ func TestConfig(t *testing.T) { t.Error("the queues in the group don't match") } - waitForStatus(t, q1, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + waitForStatus(t, nil, q1, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + }) + + t.Run("update fifo config", func(t *testing.T) { + const doc = `route: * -> fifo(2, 2, "3s") -> ` + rt, dc, close := initTest(doc) + defer close() + + req := &http.Request{URL: &url.URL{}} + r, _ := rt.Route(req) + f := r.Filters[0] + + // fill up the queue: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + q := f.Filter.(scheduler.FIFOFilter).GetQueue() + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + + // change the configuration, should decrease the queue size: + updateDoc(t, dc, `route: * -> fifo(2, 3, "3s") -> `, nil) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 3}) }) - t.Run("update config", func(t *testing.T) { + t.Run("update lifo config", func(t *testing.T) { const doc = `route: * -> lifo(2, 2) -> ` rt, dc, close := initTest(doc) defer close() @@ -289,12 +389,12 @@ func TestConfig(t *testing.T) { go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) q := f.Filter.(scheduler.LIFOFilter).GetQueue() - waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + waitForStatus(t, nil, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) // change the configuration, should decrease the queue size: updateDoc(t, dc, `route: * -> lifo(2, 1) -> `, nil) - waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + waitForStatus(t, nil, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) }) t.Run("update group config", func(t *testing.T) { @@ -322,7 +422,7 @@ func TestConfig(t *testing.T) { go f2.Request(&filtertest.Context{FRequest: req2, FStateBag: make(map[string]interface{})}) q := f1.Filter.(scheduler.LIFOFilter).GetQueue() - waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + waitForStatus(t, nil, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) // change the configuration, should decrease the queue size: updateDoc(t, dc, ` @@ -330,13 +430,14 @@ func TestConfig(t *testing.T) { g2: Path("/two") -> lifoGroup("g") -> ; `, nil) - waitForStatus(t, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + waitForStatus(t, nil, q, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) }) t.Run("queue gets closed when removed after delay", func(t *testing.T) { const doc = ` g1: Path("/one") -> lifo(2, 2) -> ; g2: Path("/two") -> lifo(2, 2) -> ; + fq: Path("/fifo") -> fifo(2, 2, "3s") -> ; ` rt, dc, close := initTest(doc) @@ -344,15 +445,19 @@ func TestConfig(t *testing.T) { q1 := getQueue("/one", rt) q2 := getQueue("/two", rt) + fq := getFifoQueue("/fifo", rt) + t.Logf("fq: %v", fq) - waitForStatus(t, q1, scheduler.QueueStatus{Closed: false}) - waitForStatus(t, q2, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, nil, q1, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, nil, q2, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, fq, nil, scheduler.QueueStatus{Closed: false}) updateDoc(t, dc, "", []string{"g1"}) // Queue is not closed immediately when deleted - waitForStatus(t, q1, scheduler.QueueStatus{Closed: false}) - waitForStatus(t, q2, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, nil, q1, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, nil, q2, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, fq, nil, scheduler.QueueStatus{Closed: false}) // An update triggers closing of the deleted queue if it // was deleted more than testQueueCloseDelay ago @@ -361,9 +466,10 @@ func TestConfig(t *testing.T) { q3 := getQueue("/three", rt) - waitForStatus(t, q1, scheduler.QueueStatus{Closed: true}) - waitForStatus(t, q2, scheduler.QueueStatus{Closed: false}) - waitForStatus(t, q3, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, nil, q1, scheduler.QueueStatus{Closed: true}) + waitForStatus(t, nil, q2, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, nil, q3, scheduler.QueueStatus{Closed: false}) + waitForStatus(t, fq, nil, scheduler.QueueStatus{Closed: false}) }) } @@ -383,11 +489,21 @@ func TestRegistryPreProcessor(t *testing.T) { input: `* -> lifo() -> setPath("/foo") -> `, expect: `* -> lifo() -> setPath("/foo") -> `, }, + { + name: "one fifo", + input: `* -> fifo(2, 2, "3s") -> setPath("/foo") -> `, + expect: `* -> fifo(2, 2, "3s") -> setPath("/foo") -> `, + }, { name: "two lifos", input: `* -> lifo(777) -> lifo() -> setPath("/foo") -> `, expect: `* -> lifo() -> setPath("/foo") -> `, }, + { + name: "two fifos", + input: `* -> fifo(2, 2, "3s") -> fifo(20, 2, "3s") -> setPath("/foo") -> `, + expect: `* -> fifo(20, 2, "3s") -> setPath("/foo") -> `, + }, { name: "three lifos", input: `* -> lifo(777) -> setPath("/foo") -> lifo(999) -> lifo() -> setPath("/bar") -> `, diff --git a/skipper.go b/skipper.go index d4f0b1eacf..779109ceb1 100644 --- a/skipper.go +++ b/skipper.go @@ -618,6 +618,9 @@ type Options struct { // RatelimitSettings contain global and host specific settings for the ratelimiters. RatelimitSettings []ratelimit.Settings + // EnableRouteFIFOMetrics enables metrics for the individual route FIFO queues, if any. + EnableRouteFIFOMetrics bool + // EnableRouteLIFOMetrics enables metrics for the individual route LIFO queues, if any. EnableRouteLIFOMetrics bool @@ -1666,6 +1669,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { schedulerRegistry := scheduler.RegistryWith(scheduler.Options{ Metrics: mtr, + EnableRouteFIFOMetrics: o.EnableRouteFIFOMetrics, EnableRouteLIFOMetrics: o.EnableRouteLIFOMetrics, }) defer schedulerRegistry.Close() From 8b7cd03b2c77c07cd6a50f5ce4e4b572dd5ab23e Mon Sep 17 00:00:00 2001 From: Alexey Zarakovskiy <4928787+azarakovskiy@users.noreply.github.com> Date: Thu, 8 Sep 2022 10:15:05 +0200 Subject: [PATCH 0074/1047] Support K8S labelSelector filters for API requests (#2048) * Support filtering in k8s API with labelSelector (#2045) Signed-off-by: Alexey Zarakovskiy * Drop K8S acronyms Signed-off-by: Alexey Zarakovskiy * Drop useless comments Signed-off-by: Alexey Zarakovskiy * Fix eskip formatting Signed-off-by: Alexey Zarakovskiy * Do not default all labels to ingress label Signed-off-by: Alexey Zarakovskiy * Test multiple selectors Signed-off-by: Alexey Zarakovskiy * Fix lint Signed-off-by: Alexey Zarakovskiy Signed-off-by: Alexey Zarakovskiy --- dataclients/kubernetes/clusterclient.go | 74 +- .../kubernetes/definitions/definitions.go | 1 + dataclients/kubernetes/kube.go | 31 + dataclients/kubernetes/kube_test.go | 702 ++++++++++-------- dataclients/kubernetes/kubernetestest/api.go | 74 +- .../kubernetes/kubernetestest/fixtures.go | 6 + dataclients/kubernetes/path_test.go | 6 +- ...lector-filter-different-per-resource.eskip | 4 + ...elector-filter-different-per-resource.kube | 7 + ...elector-filter-different-per-resource.yaml | 53 ++ ...th-label-selector-filter-is-optional.eskip | 4 + ...ith-label-selector-filter-is-optional.kube | 3 + ...ith-label-selector-filter-is-optional.yaml | 49 ++ ...-with-label-selector-filter-multiple.eskip | 4 + ...g-with-label-selector-filter-multiple.kube | 10 + ...g-with-label-selector-filter-multiple.yaml | 56 ++ .../ing-with-label-selector-filter.eskip | 4 + .../ing-with-label-selector-filter.kube | 7 + .../ing-with-label-selector-filter.yaml | 103 +++ skipper.go | 36 + 20 files changed, 911 insertions(+), 323 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.yaml diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index f27326720e..532ee8d5f6 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -10,9 +10,11 @@ import ( "io" "net" "net/http" + "net/url" "os" "regexp" "sort" + "strings" "time" log "github.com/sirupsen/logrus" @@ -43,6 +45,8 @@ const ( serviceAccountDir = "/var/run/secrets/kubernetes.io/serviceaccount/" serviceAccountTokenKey = "token" serviceAccountRootCAKey = "ca.crt" + labelSelectorFmt = "%s=%s" + labelSelectorQueryFmt = "?labelSelector=%s" ) const RouteGroupsNotInstalledMessage = `RouteGroups CRD is not installed in the cluster. @@ -63,6 +67,12 @@ type clusterClient struct { httpClient *http.Client ingressV1 bool + ingressLabelSelectors string + servicesLabelSelectors string + endpointsLabelSelectors string + secretsLabelSelectors string + routeGroupsLabelSelectors string + loggedMissingRouteGroups bool } @@ -142,17 +152,22 @@ func newClusterClient(o Options, apiURL, ingCls, rgCls string, quit <-chan struc ingressURI = IngressesV1ClusterURI } c := &clusterClient{ - ingressV1: o.KubernetesIngressV1, - ingressesURI: ingressURI, - routeGroupsURI: routeGroupsClusterURI, - servicesURI: ServicesClusterURI, - endpointsURI: EndpointsClusterURI, - secretsURI: SecretsClusterURI, - ingressClass: ingClsRx, - routeGroupClass: rgClsRx, - httpClient: httpClient, - apiURL: apiURL, - certificateRegistry: o.CertificateRegistry, + ingressV1: o.KubernetesIngressV1, + ingressesURI: ingressURI, + routeGroupsURI: routeGroupsClusterURI, + servicesURI: ServicesClusterURI, + endpointsURI: EndpointsClusterURI, + secretsURI: SecretsClusterURI, + ingressClass: ingClsRx, + ingressLabelSelectors: toLabelSelectorQuery(o.IngressLabelSelectors), + servicesLabelSelectors: toLabelSelectorQuery(o.ServicesLabelSelectors), + endpointsLabelSelectors: toLabelSelectorQuery(o.EndpointsLabelSelectors), + secretsLabelSelectors: toLabelSelectorQuery(o.SecretsLabelSelectors), + routeGroupsLabelSelectors: toLabelSelectorQuery(o.RouteGroupsLabelSelectors), + routeGroupClass: rgClsRx, + httpClient: httpClient, + apiURL: apiURL, + certificateRegistry: o.CertificateRegistry, } if o.KubernetesInCluster { @@ -177,6 +192,29 @@ func newClusterClient(o Options, apiURL, ingCls, rgCls string, quit <-chan struc return c, nil } +// serializes a given map of label selectors to a string that can be appended to a request URI to kubernetes +// Examples (note that the resulting value in the query is URL escaped, for readability this is not done in examples): +// [] becomes `` +// ["label": ""] becomes `?labelSelector=label` +// ["label": "value"] becomes `?labelSelector=label=value` +// ["label": "value", "label2": "value2"] becomes `?labelSelector=label=value&label2=value2` +func toLabelSelectorQuery(selectors map[string]string) string { + if len(selectors) == 0 { + return "" + } + + var strs []string + for k, v := range selectors { + if v == "" { + strs = append(strs, k) + } else { + strs = append(strs, fmt.Sprintf(labelSelectorFmt, k, v)) + } + } + + return fmt.Sprintf(labelSelectorQueryFmt, url.QueryEscape(strings.Join(strs, ","))) +} + func (c *clusterClient) setNamespace(namespace string) { if c.ingressV1 { c.ingressesURI = fmt.Sprintf(IngressesV1NamespaceFmt, namespace) @@ -326,7 +364,7 @@ func sortByMetadata(slice interface{}, getMetadata func(int) *definitions.Metada func (c *clusterClient) loadIngresses() ([]*definitions.IngressItem, error) { var il definitions.IngressList - if err := c.getJSON(c.ingressesURI, &il); err != nil { + if err := c.getJSON(c.ingressesURI+c.ingressLabelSelectors, &il); err != nil { log.Debugf("requesting all ingresses failed: %v", err) return nil, err } @@ -340,7 +378,7 @@ func (c *clusterClient) loadIngresses() ([]*definitions.IngressItem, error) { func (c *clusterClient) loadIngressesV1() ([]*definitions.IngressV1Item, error) { var il definitions.IngressV1List - if err := c.getJSON(c.ingressesURI, &il); err != nil { + if err := c.getJSON(c.ingressesURI+c.ingressLabelSelectors, &il); err != nil { log.Debugf("requesting all ingresses failed: %v", err) return nil, err } @@ -354,7 +392,7 @@ func (c *clusterClient) loadIngressesV1() ([]*definitions.IngressV1Item, error) func (c *clusterClient) LoadRouteGroups() ([]*definitions.RouteGroupItem, error) { var rgl definitions.RouteGroupList - if err := c.getJSON(c.routeGroupsURI, &rgl); err != nil { + if err := c.getJSON(c.routeGroupsURI+c.routeGroupsLabelSelectors, &rgl); err != nil { return nil, err } @@ -384,7 +422,8 @@ func (c *clusterClient) LoadRouteGroups() ([]*definitions.RouteGroupItem, error) func (c *clusterClient) loadServices() (map[definitions.ResourceID]*service, error) { var services serviceList - if err := c.getJSON(c.servicesURI, &services); err != nil { + + if err := c.getJSON(c.servicesURI+c.servicesLabelSelectors, &services); err != nil { log.Debugf("requesting all services failed: %v", err) return nil, err } @@ -410,7 +449,8 @@ func (c *clusterClient) loadServices() (map[definitions.ResourceID]*service, err func (c *clusterClient) loadSecrets() (map[definitions.ResourceID]*secret, error) { var secrets secretList - if err := c.getJSON(c.secretsURI, &secrets); err != nil { + + if err := c.getJSON(c.secretsURI+c.secretsLabelSelectors, &secrets); err != nil { log.Debugf("requesting all secrets failed: %v", err) return nil, err } @@ -430,7 +470,7 @@ func (c *clusterClient) loadSecrets() (map[definitions.ResourceID]*secret, error func (c *clusterClient) loadEndpoints() (map[definitions.ResourceID]*endpoint, error) { var endpoints endpointList - if err := c.getJSON(c.endpointsURI, &endpoints); err != nil { + if err := c.getJSON(c.endpointsURI+c.endpointsLabelSelectors, &endpoints); err != nil { log.Debugf("requesting all endpoints failed: %v", err) return nil, err } diff --git a/dataclients/kubernetes/definitions/definitions.go b/dataclients/kubernetes/definitions/definitions.go index d84a9b1529..e8129dc81b 100644 --- a/dataclients/kubernetes/definitions/definitions.go +++ b/dataclients/kubernetes/definitions/definitions.go @@ -14,6 +14,7 @@ type Metadata struct { Created time.Time `json:"creationTimestamp"` Uid string `json:"uid"` Annotations map[string]string `json:"annotations"` + Labels map[string]string `json:"labels"` } func (meta *Metadata) ToResourceID() ResourceID { diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 20ac0ef3ce..a389a47430 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -133,6 +133,37 @@ type Options struct { // skipper will load it. The default value for the RouteGroup class is 'skipper'. RouteGroupClass string + // IngressLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. A label and its value on an Ingress must be match exactly to be loaded by Skipper. + // If the value is irrelevant for a given configuration, it can be left empty. The default + // value is no labels required. + // Examples: + // Config [] will load all Ingresses. + // Config ["skipper-enabled": ""] will load only Ingresses with a label "skipper-enabled", no matter the value. + // Config ["skipper-enabled": "true"] will load only Ingresses with a label "skipper-enabled: true" + // Config ["skipper-enabled": "", "foo": "bar"] will load only Ingresses with both labels while label "foo" must have a value "bar". + IngressLabelSelectors map[string]string + + // ServicesLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + ServicesLabelSelectors map[string]string + + // EndpointsLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + EndpointsLabelSelectors map[string]string + + // SecretsLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + SecretsLabelSelectors map[string]string + + // RouteGroupsLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + RouteGroupsLabelSelectors map[string]string + // ReverseSourcePredicate set to true will do the Source IP // whitelisting for the heartbeat endpoint correctly in AWS. // Amazon's ALB writes the client IP to the last item of the diff --git a/dataclients/kubernetes/kube_test.go b/dataclients/kubernetes/kube_test.go index 6ae6f13db4..6b8ee872a2 100644 --- a/dataclients/kubernetes/kube_test.go +++ b/dataclients/kubernetes/kube_test.go @@ -20,6 +20,7 @@ import ( "reflect" "regexp" "sort" + "strings" "testing" "testing/quick" "time" @@ -51,22 +52,23 @@ func init() { func testEndpointList() *endpointList { eps := make([]*endpoint, 0) - eps = append(eps, testEndpoints("namespace1", "service1", "1.1.1", 1, map[string]int{"port1": 8080})...) - eps = append(eps, testEndpoints("namespace1", "service2", "1.1.2", 1, map[string]int{"port2": 8181})...) - eps = append(eps, testEndpoints("namespace2", "service3", "2.1.3", 1, map[string]int{"port3": 7272})...) - eps = append(eps, testEndpoints("namespace2", "service4", "2.1.4", 1, map[string]int{"port4": 4444, "port5": 5555})...) + eps = append(eps, testEndpoints("namespace1", "service1", map[string]string{}, "1.1.1", 1, map[string]int{"port1": 8080})...) + eps = append(eps, testEndpoints("namespace1", "service2", map[string]string{}, "1.1.2", 1, map[string]int{"port2": 8181})...) + eps = append(eps, testEndpoints("namespace2", "service3", map[string]string{}, "2.1.3", 1, map[string]int{"port3": 7272})...) + eps = append(eps, testEndpoints("namespace2", "service4", map[string]string{}, "2.1.4", 1, map[string]int{"port4": 4444, "port5": 5555})...) return &endpointList{ Items: eps, } } -func testEndpoints(namespace, name, base string, n int, ports map[string]int) []*endpoint { +func testEndpoints(namespace, name string, labels map[string]string, base string, n int, ports map[string]int) []*endpoint { eps := make([]*endpoint, 0, 1) eps = append(eps, &endpoint{ Meta: &definitions.Metadata{ Namespace: namespace, Name: name, + Labels: labels, }, Subsets: []*subset{}, }) @@ -95,28 +97,29 @@ func testEndpoints(namespace, name, base string, n int, ports map[string]int) [] return eps } -func testSecret(namespace, name string, clusterIP string, t string, data map[string]string) *secret { +func testSecret(namespace, name string, labels map[string]string, clusterIP, t string, data map[string]string) *secret { return &secret{ Metadata: &definitions.Metadata{ Namespace: namespace, Name: name, + Labels: labels, }, Type: t, Data: data, } } -func testService(namespace, name string, clusterIP string, ports map[string]int) *service { +func testService(namespace, name string, labels map[string]string, clusterIP string, ports map[string]int) *service { targetPorts := make(map[int]*definitions.BackendPort) for _, v := range ports { targetPorts[v] = &definitions.BackendPort{ Value: v, } } - return testServiceWithTargetPort(namespace, name, clusterIP, ports, targetPorts) + return testServiceWithTargetPort(namespace, name, labels, clusterIP, ports, targetPorts) } -func testServiceWithTargetPort(namespace, name string, clusterIP string, ports map[string]int, targetPorts map[int]*definitions.BackendPort) *service { +func testServiceWithTargetPort(namespace, name string, labels map[string]string, clusterIP string, ports map[string]int, targetPorts map[int]*definitions.BackendPort) *service { sports := make([]*servicePort, 0, len(ports)) for name, port := range ports { sports = append(sports, &servicePort{ @@ -130,6 +133,7 @@ func testServiceWithTargetPort(namespace, name string, clusterIP string, ports m Meta: &definitions.Metadata{ Namespace: namespace, Name: name, + Labels: labels, }, Spec: &serviceSpec{ ClusterIP: clusterIP, @@ -169,7 +173,8 @@ func setAnnotation(i *definitions.IngressItem, key, value string) { i.Metadata.Annotations[key] = value } -func testIngress(ns, name, defaultService, ratelimitCfg, filterString, predicateString, routesString, pathModeString, lbAlgorithm string, defaultPort definitions.BackendPort, traffic float64, rules ...*definitions.Rule) *definitions.IngressItem { +func testIngress(ns, name, defaultService, ratelimitCfg, filterString, predicateString, routesString, pathModeString, lbAlgorithm string, labels map[string]string, + defaultPort definitions.BackendPort, traffic float64, rules ...*definitions.Rule) *definitions.IngressItem { var defaultBackend *definitions.Backend if len(defaultService) != 0 { defaultBackend = &definitions.Backend{ @@ -183,6 +188,7 @@ func testIngress(ns, name, defaultService, ratelimitCfg, filterString, predicate Namespace: ns, Name: name, Annotations: make(map[string]string), + Labels: labels, } i := &definitions.IngressItem{ Metadata: &meta, @@ -216,10 +222,10 @@ func testIngress(ns, name, defaultService, ratelimitCfg, filterString, predicate func testSecrets() *secretList { return &secretList{ Items: []*secret{ - testSecret("namespace1", "secret1", "1.2.3.4", "Opaque", map[string]string{"foo": "bar"}), - testSecret("namespace1", "secret2", "5.6.7.8", "Opaque", map[string]string{"bar": "foo"}), - testSecret("namespace2", "secret3", "9.0.1.2", "Opaque", map[string]string{"foo": "foo"}), - testSecret("namespace2", "secret4", "10.0.1.2", "Opaque", map[string]string{"bar": "bar"}), + testSecret("namespace1", "secret1", map[string]string{}, "1.2.3.4", "Opaque", map[string]string{"foo": "bar"}), + testSecret("namespace1", "secret2", map[string]string{}, "5.6.7.8", "Opaque", map[string]string{"bar": "foo"}), + testSecret("namespace2", "secret3", map[string]string{}, "9.0.1.2", "Opaque", map[string]string{"foo": "foo"}), + testSecret("namespace2", "secret4", map[string]string{}, "10.0.1.2", "Opaque", map[string]string{"bar": "bar"}), }, } } @@ -227,34 +233,21 @@ func testSecrets() *secretList { func testServices() *serviceList { return &serviceList{ Items: []*service{ - testService("namespace1", "service1", "1.2.3.4", map[string]int{"port1": 8080}), - testService("namespace1", "service2", "5.6.7.8", map[string]int{"port2": 8181}), - testService("namespace2", "service3", "9.0.1.2", map[string]int{"port3": 7272}), - testService("namespace2", "service4", "10.0.1.2", map[string]int{"port4": 4444, "port5": 5555}), + testService("namespace1", "service1", map[string]string{}, "1.2.3.4", map[string]int{"port1": 8080}), + testService("namespace1", "service2", map[string]string{}, "5.6.7.8", map[string]int{"port2": 8181}), + testService("namespace2", "service3", map[string]string{}, "9.0.1.2", map[string]int{"port3": 7272}), + testService("namespace2", "service4", map[string]string{}, "10.0.1.2", map[string]int{"port4": 4444, "port5": 5555}), }, } } func testIngresses() []*definitions.IngressItem { return []*definitions.IngressItem{ - testIngress("namespace1", "default-only", "service1", "", "", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0), - testIngress( - "namespace2", - "path-rule-only", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{}, - 1.0, - testRule( - "www.example.org", - testPathRule("/", "service3", definitions.BackendPort{Value: "port3"}), - ), - ), + testIngress("namespace1", "default-only", "service1", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0), + testIngress("namespace2", "path-rule-only", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule( + "www.example.org", + testPathRule("/", "service3", definitions.BackendPort{Value: "port3"}), + )), testIngress( "namespace1", "mega", @@ -265,6 +258,7 @@ func testIngresses() []*definitions.IngressItem { "", "", "", + map[string]string{}, definitions.BackendPort{Value: "port1"}, 1.0, testRule( @@ -278,9 +272,9 @@ func testIngresses() []*definitions.IngressItem { testPathRule("/test2", "service2", definitions.BackendPort{Value: "port2"}), ), ), - testIngress("namespace1", "ratelimit", "service1", "localRatelimit(20,\"1m\")", "", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0), - testIngress("namespace1", "ratelimitAndBreaker", "service1", "", "localRatelimit(20,\"1m\") -> consecutiveBreaker(15)", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0), - testIngress("namespace2", "svcwith2ports", "service4", "", "", "", "", "", "", definitions.BackendPort{Value: 4444}, 1.0), + testIngress("namespace1", "ratelimit", "service1", "localRatelimit(20,\"1m\")", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0), + testIngress("namespace1", "ratelimitAndBreaker", "service1", "", "localRatelimit(20,\"1m\") -> consecutiveBreaker(15)", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0), + testIngress("namespace2", "svcwith2ports", "service4", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 4444}, 1.0), } } @@ -384,6 +378,72 @@ func respondJSON(w io.Writer, v interface{}) error { return err } +func labelsMatch(labelSelectors []string, labels map[string]string) bool { + for _, selector := range labelSelectors { + kv := strings.Split(selector, "=") + if labels[kv[0]] != kv[1] { + return false + } + } + return true +} + +func filterIngressByLabels(labelSelectors []string, ingresses *definitions.IngressList) interface{} { + if len(labelSelectors) == 0 { + return ingresses + } + + result := &definitions.IngressList{} + for _, item := range ingresses.Items { + if labelsMatch(labelSelectors, item.Metadata.Labels) { + result.Items = append(result.Items, item) + } + } + return result +} + +func filterServicesByLabels(labelSelectors []string, services *serviceList) interface{} { + if len(labelSelectors) == 0 { + return services + } + + result := &serviceList{} + for _, item := range services.Items { + if labelsMatch(labelSelectors, item.Meta.Labels) { + result.Items = append(result.Items, item) + } + } + return result +} + +func filterEndpointsByLabels(labelSelectors []string, endpoints *endpointList) interface{} { + if len(labelSelectors) == 0 { + return endpoints + } + + result := &endpointList{} + for _, item := range endpoints.Items { + if labelsMatch(labelSelectors, item.Meta.Labels) { + result.Items = append(result.Items, item) + } + } + return result +} + +func filterSecretsByLabels(labelSelectors []string, secrets *secretList) interface{} { + if len(labelSelectors) == 0 { + return secrets + } + + result := &secretList{} + for _, item := range secrets.Items { + if labelsMatch(labelSelectors, item.Metadata.Labels) { + result.Items = append(result.Items, item) + } + } + return result +} + func (api *testAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { if api.failNext { api.failNext = false @@ -391,24 +451,30 @@ func (api *testAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + queryParam := r.URL.Query().Get("labelSelector") + var labelSelectors []string + if queryParam != "" { + labelSelectors = strings.Split(queryParam, ",") + } + switch r.URL.Path { case IngressesClusterURI: - if err := respondJSON(w, api.ingresses); err != nil { + if err := respondJSON(w, filterIngressByLabels(labelSelectors, api.ingresses)); err != nil { api.test.Error(err) } return case ServicesClusterURI: - if err := respondJSON(w, api.services); err != nil { + if err := respondJSON(w, filterServicesByLabels(labelSelectors, api.services)); err != nil { api.test.Error(err) } return case EndpointsClusterURI: - if err := respondJSON(w, api.endpoints); err != nil { + if err := respondJSON(w, filterEndpointsByLabels(labelSelectors, api.endpoints)); err != nil { api.test.Error(err) } return case SecretsClusterURI: - if err := respondJSON(w, api.secrets); err != nil { + if err := respondJSON(w, filterSecretsByLabels(labelSelectors, api.secrets)); err != nil { api.test.Error(err) } return @@ -908,40 +974,14 @@ func TestIngress(t *testing.T) { api.ingresses.Items = append( api.ingresses.Items, - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), - testIngress( - "namespace1", - "new2", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new2.example.org", - testPathRule("/", "service2", definitions.BackendPort{Value: "port2"}), - ), - ), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/", "service1", definitions.BackendPort{Value: "port1"}), + )), + testIngress("namespace1", "new2", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new2.example.org", + testPathRule("/", "service2", definitions.BackendPort{Value: "port2"}), + )), ) r, d, err := dc.LoadUpdate() @@ -975,40 +1015,14 @@ func TestIngress(t *testing.T) { api.ingresses.Items = append( api.ingresses.Items, - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), - testIngress( - "namespace1", - "new2", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new2.example.org", - testPathRule("/", "service2", definitions.BackendPort{Value: "port2"}), - ), - ), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/", "service1", definitions.BackendPort{Value: "port1"}), + )), + testIngress("namespace1", "new2", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new2.example.org", + testPathRule("/", "service2", definitions.BackendPort{Value: "port2"}), + )), ) api.ingresses.Items[1].Spec.Rules[0].Http.Paths[0].Backend.ServicePort = definitions.BackendPort{Value: 9999} @@ -1051,40 +1065,14 @@ func TestIngress(t *testing.T) { return } - ti1 := testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/", "service1", definitions.BackendPort{Value: "port1"}), - ), - ) - ti2 := testIngress( - "namespace1", - "new2", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new2.example.org", - testPathRule("/", "service2", definitions.BackendPort{Value: "port2"}), - ), - ) + ti1 := testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/", "service1", definitions.BackendPort{Value: "port1"}), + )) + ti2 := testIngress("namespace1", "new2", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new2.example.org", + testPathRule("/", "service2", definitions.BackendPort{Value: "port2"}), + )) // Set class ingress class annotation ti1.Metadata.Annotations = map[string]string{ingressClassKey: defaultIngressClass} ti2.Metadata.Annotations = map[string]string{ingressClassKey: "nginx"} @@ -1100,6 +1088,103 @@ func TestIngress(t *testing.T) { "kube_namespace1__new1__new1_example_org_____service1": "http://1.1.1.0:8080", }) }) + + t.Run("has ingresses, filter out only requested on a server-side", func(t *testing.T) { + api.endpoints = testEndpointList() + api.endpoints.Items = append(api.endpoints.Items, testEndpoints("namespace1", "with-label", + map[string]string{"skipper-ingress": "true"}, "1.1.1", 1, map[string]int{"port1": 8080})...) + + api.services = testServices() + api.services.Items = append(api.services.Items, testService("namespace1", "with-label", + map[string]string{"skipper-ingress": "true"}, "1.1.1.0", map[string]int{"port1": 8080})) + + api.ingresses.Items = testIngresses() + api.ingresses.Items = append(api.ingresses.Items, testIngress("namespace1", "with-label", "with-label", "", "", "", "", "", "", + map[string]string{"skipper-ingress": "true"}, definitions.BackendPort{Value: 8080}, 1.0)) + + dc, err := New(Options{KubernetesURL: api.server.URL, IngressLabelSelectors: map[string]string{"skipper-ingress": "true"}}) + if err != nil { + t.Error(err) + } + + defer dc.Close() + + r, err := dc.LoadAll() + if err != nil { + t.Error("failed to load initial routes", err) + return + } + + checkRoutes(t, r, map[string]string{ + "kube_namespace1__with_label______": "http://1.1.1.0:8080", + }) + }) + + t.Run("has ingresses, filter out only requested on a server-side, multiple labels", func(t *testing.T) { + api.endpoints = testEndpointList() + api.endpoints.Items = append(api.endpoints.Items, testEndpoints("namespace1", "with-label", + map[string]string{"skipper-ingress": "true", "team": "avengers"}, "1.1.1", 1, map[string]int{"port1": 8080})...) + + api.services = testServices() + api.services.Items = append(api.services.Items, testService("namespace1", "with-label", + map[string]string{"skipper-ingress": "true", "team": "avengers"}, "1.1.1.0", map[string]int{"port1": 8080})) + + api.ingresses.Items = testIngresses() + api.ingresses.Items = append(api.ingresses.Items, testIngress("namespace1", "with-label", "with-label", "", "", "", "", "", "", + map[string]string{"skipper-ingress": "true", "team": "avengers"}, definitions.BackendPort{Value: 8080}, 1.0)) + + dc, err := New(Options{KubernetesURL: api.server.URL, IngressLabelSelectors: map[string]string{"skipper-ingress": "true", "team": "avengers"}}) + if err != nil { + t.Error(err) + } + + defer dc.Close() + + r, err := dc.LoadAll() + if err != nil { + t.Error("failed to load initial routes", err) + return + } + + checkRoutes(t, r, map[string]string{ + "kube_namespace1__with_label______": "http://1.1.1.0:8080", + }) + }) + + t.Run("has ingresses, filter out only requested on a server-side, different labels per resource", func(t *testing.T) { + api.endpoints = testEndpointList() + api.endpoints.Items = append(api.endpoints.Items, testEndpoints("namespace1", "with-label", + map[string]string{"skipper-ingress": "true", "team": "avengers"}, "1.1.1", 1, map[string]int{"port1": 8080})...) + + api.services = testServices() + api.services.Items = append(api.services.Items, testService("namespace1", "with-label", + map[string]string{"skipper-ingress": "true", "group": "theavengers"}, "1.1.1.0", map[string]int{"port1": 8080})) + + api.ingresses.Items = testIngresses() + api.ingresses.Items = append(api.ingresses.Items, testIngress("namespace1", "with-label", "with-label", "", "", "", "", "", "", + map[string]string{"skipper-ingress": "true"}, definitions.BackendPort{Value: 8080}, 1.0)) + + dc, err := New(Options{KubernetesURL: api.server.URL, + IngressLabelSelectors: map[string]string{"skipper-ingress": "true"}, + ServicesLabelSelectors: map[string]string{"skipper-ingress": "true", "group": "theavengers"}, + EndpointsLabelSelectors: map[string]string{"skipper-ingress": "true", "team": "avengers"}, + }) + if err != nil { + t.Error(err) + } + + defer dc.Close() + + r, err := dc.LoadAll() + if err != nil { + t.Error("failed to load initial routes", err) + return + } + + checkRoutes(t, r, map[string]string{ + "kube_namespace1__with_label______": "http://1.1.1.0:8080", + }) + }) } func TestConvertPathRule(t *testing.T) { @@ -1126,40 +1211,14 @@ func TestConvertPathRule(t *testing.T) { api.ingresses.Items = append( api.ingresses.Items, - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/test1", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/test2", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/test1", "service1", definitions.BackendPort{Value: "port1"}), + )), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/test2", "service1", definitions.BackendPort{Value: "port1"}), + )), ) r, d, err := dc.LoadUpdate() @@ -1201,23 +1260,10 @@ func TestConvertPathRuleEastWestEnabled(t *testing.T) { api.ingresses.Items = append( api.ingresses.Items, - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/test1", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/test1", "service1", definitions.BackendPort{Value: "port1"}), + )), ) r, d, err := dc.LoadUpdate() @@ -1256,40 +1302,14 @@ func TestConvertPathRuleEastWestEnabled(t *testing.T) { api.ingresses.Items = append( api.ingresses.Items, - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/test1", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), - testIngress( - "namespace1", - "new1", - "", - "", - "", - "", - "", - "", - "", - definitions.BackendPort{Value: ""}, - 1.0, - testRule( - "new1.example.org", - testPathRule("/test2", "service1", definitions.BackendPort{Value: "port1"}), - ), - ), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/test1", "service1", definitions.BackendPort{Value: "port1"}), + )), + testIngress("namespace1", "new1", "", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: ""}, 1.0, testRule( + "new1.example.org", + testPathRule("/test2", "service1", definitions.BackendPort{Value: "port1"}), + )), ) r, d, err := dc.LoadUpdate() @@ -1897,6 +1917,124 @@ func TestScoping(t *testing.T) { assert.Equal(t, "/api/v1/namespaces/test/endpoints", client.endpointsURI) } +func TestLabelSelectorsSet(t *testing.T) { + tests := []struct { + name string + options Options + expectedIngressSelector string + expectedServicesSelector string + expectedEndpointsSelector string + expectedSecretsSelector string + expectedRouteGroupsSelector string + }{ + { + name: "different labels for different resources", + options: Options{ + IngressLabelSelectors: map[string]string{"skipper-enabled": "true"}, + ServicesLabelSelectors: map[string]string{"best-services": "true"}, + EndpointsLabelSelectors: map[string]string{"endpoints-needed": "true"}, + SecretsLabelSelectors: map[string]string{"relevant-secrets": "true"}, + RouteGroupsLabelSelectors: map[string]string{"routegroups-skipper": "true"}, + }, + expectedIngressSelector: "?labelSelector=skipper-enabled%3Dtrue", + expectedServicesSelector: "?labelSelector=best-services%3Dtrue", + expectedEndpointsSelector: "?labelSelector=endpoints-needed%3Dtrue", + expectedSecretsSelector: "?labelSelector=relevant-secrets%3Dtrue", + expectedRouteGroupsSelector: "?labelSelector=routegroups-skipper%3Dtrue", + }, + { + name: "multiple labels can be specified", + options: Options{ + IngressLabelSelectors: map[string]string{"skipper-enabled": "true", "team": "avengers"}, + ServicesLabelSelectors: map[string]string{"best-services": "true", "group": "marvel"}, + EndpointsLabelSelectors: map[string]string{"endpoints-needed": "true", "hero": "thor"}, + SecretsLabelSelectors: map[string]string{"relevant-secrets": "true", "iron": "man"}, + RouteGroupsLabelSelectors: map[string]string{"routegroups-skipper": "true", "black": "widow"}, + }, + expectedIngressSelector: "?labelSelector=skipper-enabled%3Dtrue%2Cteam%3Davengers", + expectedServicesSelector: "?labelSelector=best-services%3Dtrue%2Cgroup%3Dmarvel", + expectedEndpointsSelector: "?labelSelector=endpoints-needed%3Dtrue%2Chero%3Dthor", + expectedSecretsSelector: "?labelSelector=iron%3Dman%2Crelevant-secrets%3Dtrue", + expectedRouteGroupsSelector: "?labelSelector=black%3Dwidow%2Croutegroups-skipper%3Dtrue", + }, + { + name: "if not set, it defaults to an empty string", + options: Options{}, + expectedIngressSelector: "", + expectedServicesSelector: "", + expectedEndpointsSelector: "", + expectedSecretsSelector: "", + expectedRouteGroupsSelector: "", + }, + { + name: "if not set, it defaults to an empty string and doesn't default to Ingress labels", + options: Options{IngressLabelSelectors: map[string]string{"skipper-enabled": "true"}}, + expectedIngressSelector: "?labelSelector=skipper-enabled%3Dtrue", + expectedServicesSelector: "", + expectedEndpointsSelector: "", + expectedSecretsSelector: "", + expectedRouteGroupsSelector: "", + }, + { + name: "value can be skipped", + options: Options{ + IngressLabelSelectors: map[string]string{"skipper-enabled": ""}, + ServicesLabelSelectors: map[string]string{"skipper-enabled": ""}, + EndpointsLabelSelectors: map[string]string{"skipper-enabled": ""}, + SecretsLabelSelectors: map[string]string{"skipper-enabled": ""}, + RouteGroupsLabelSelectors: map[string]string{"skipper-enabled": ""}, + }, + expectedIngressSelector: "?labelSelector=skipper-enabled", + expectedServicesSelector: "?labelSelector=skipper-enabled", + expectedEndpointsSelector: "?labelSelector=skipper-enabled", + expectedSecretsSelector: "?labelSelector=skipper-enabled", + expectedRouteGroupsSelector: "?labelSelector=skipper-enabled", + }, + { + name: "labels are not required at all", + options: Options{}, + expectedIngressSelector: "", + expectedServicesSelector: "", + expectedEndpointsSelector: "", + expectedSecretsSelector: "", + expectedRouteGroupsSelector: "", + }, + } + + // in case of multiple labels, the order of the resulting query isn't guaranteed + // this solves the issue for the test as we split queries again and compare string slices + sortedSlice := func(query string) []string { + if query == "" { + return []string{} + } + + params := strings.Split(query, "=") + require.Len(t, params, 2) + + labels := strings.Split(params[1], "%2C") + + result := []string{params[0]} + result = append(result, labels...) + + sort.Strings(result) + + return result + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + client, err := New(test.options) + require.NoError(t, err) + + assert.Equal(t, sortedSlice(test.expectedIngressSelector), sortedSlice(client.ClusterClient.ingressLabelSelectors)) + assert.Equal(t, sortedSlice(test.expectedServicesSelector), sortedSlice(client.ClusterClient.servicesLabelSelectors)) + assert.Equal(t, sortedSlice(test.expectedEndpointsSelector), sortedSlice(client.ClusterClient.endpointsLabelSelectors)) + assert.Equal(t, sortedSlice(test.expectedSecretsSelector), sortedSlice(client.ClusterClient.secretsLabelSelectors)) + assert.Equal(t, sortedSlice(test.expectedRouteGroupsSelector), sortedSlice(client.ClusterClient.routeGroupsLabelSelectors)) + }) + } +} + // generateSSCert only for testing purposes func generateSSCert() []byte { //create root CA @@ -2544,7 +2682,7 @@ func TestSkipperPredicate(t *testing.T) { api := newTestAPI(t, nil, &definitions.IngressList{}) defer api.Close() - ingWithPredicate := testIngress("namespace1", "predicate", "service1", "", "", "QueryParam(\"query\", \"^example$\")", "", "", "", definitions.BackendPort{Value: 8080}, 1.0) + ingWithPredicate := testIngress("namespace1", "predicate", "service1", "", "", "QueryParam(\"query\", \"^example$\")", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0) t.Run("check ingress predicate", func(t *testing.T) { api.services = testServices() @@ -2575,7 +2713,7 @@ func TestSkipperPredicateEastWest(t *testing.T) { api := newTestAPI(t, nil, &definitions.IngressList{}) defer api.Close() - ingWithPredicate := testIngress("namespace1", "predicate", "service1", "", "", "QueryParam(\"query\", \"^example$\")", "", "", "", definitions.BackendPort{Value: 8080}, 1.0) + ingWithPredicate := testIngress("namespace1", "predicate", "service1", "", "", "QueryParam(\"query\", \"^example$\")", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0) t.Run("check ingress predicate", func(t *testing.T) { api.services = testServices() @@ -2626,30 +2764,22 @@ func TestSkipperCustomRoutes(t *testing.T) { expectedRoutes map[string]string }{{ msg: "ingress with 1 host definitions and 1 additional custom route", - endpoints: testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), + endpoints: testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), }, - ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", - `Method("OPTIONS") -> `, - "", "", definitions.BackendPort{}, 1.0, - testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), - )}, + ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `Method("OPTIONS") -> `, "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})))}, expectedRoutes: map[string]string{ "kube_foo__qux__www1_example_org_____bar": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathRegexp(/^\\//) -> \"http://1.1.1.0:8181\"", "kube_foo__qux__0__www1_example_org_____": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathRegexp(/^\\//) && Method(\"OPTIONS\") -> ", }, }, { msg: "ingress with 1 host definitions with path and 1 additional custom route", - endpoints: testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), + endpoints: testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), }, - ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", - `Method("OPTIONS") -> `, - "", "", definitions.BackendPort{}, 1.0, - testRule("www1.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})), - )}, + ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `Method("OPTIONS") -> `, "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})))}, expectedRoutes: map[string]string{ "kube_foo__qux__www1_example_org___a_path__bar": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathRegexp(/^(\\/a\\/path)/) -> \"http://1.1.1.0:8181\"", "kube_foo__qux__0__www1_example_org_a_path____": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathRegexp(/^(\\/a\\/path)/) && Method(\"OPTIONS\") -> ", @@ -2657,13 +2787,13 @@ func TestSkipperCustomRoutes(t *testing.T) { }, }, { msg: "ingress with 2 host definitions and 1 additional custom route", - endpoints: testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), + endpoints: testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), }, ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `Method("OPTIONS") -> `, - "", "", definitions.BackendPort{}, 1.0, + "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), testRule("www2.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), )}, @@ -2675,13 +2805,13 @@ func TestSkipperCustomRoutes(t *testing.T) { }, }, { msg: "ingress with 2 host definitions with path and 1 additional custom route", - endpoints: testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), + endpoints: testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), }, ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `Method("OPTIONS") -> `, - "", "", definitions.BackendPort{}, 1.0, + "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})), testRule("www2.example.org", testPathRule("/another/path", "bar", definitions.BackendPort{Value: "baz"})), )}, @@ -2695,21 +2825,15 @@ func TestSkipperCustomRoutes(t *testing.T) { }, }, { msg: "ingress with 3 host definitions with one path and 3 additional custom routes", - endpoints: append(testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), - testEndpoints("foo", "baz", "1.1.2", 1, map[string]int{"baz": 8181})...), + endpoints: append(testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), + testEndpoints("foo", "baz", map[string]string{}, "1.1.2", 1, map[string]int{"baz": 8181})...), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), - testService("foo", "baz", "1.2.3.6", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "baz", map[string]string{}, "1.2.3.6", map[string]int{"baz": 8181}), }, - ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", - `a: Method("OPTIONS") -> ; + ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `a: Method("OPTIONS") -> ; b: Cookie("alpha", /^enabled$/) -> "http://1.1.2.0:8181"; - c: Path("/a/path/somewhere") -> "https://some.other-url.org/a/path/somewhere";`, - "", "", definitions.BackendPort{}, 1.0, - testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), - testRule("www2.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), - testRule("www3.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})), - )}, + c: Path("/a/path/somewhere") -> "https://some.other-url.org/a/path/somewhere";`, "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), testRule("www2.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), testRule("www3.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})))}, expectedRoutes: map[string]string{ "kube_foo__qux__www1_example_org_____bar": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathRegexp(/^\\//) -> \"http://1.1.1.0:8181\"", "kube_foo__qux_a_0__www1_example_org_____": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathRegexp(/^\\//) && Method(\"OPTIONS\") -> ", @@ -2729,21 +2853,15 @@ func TestSkipperCustomRoutes(t *testing.T) { }, }, { msg: "ingress with 3 host definitions with one without path and 3 additional custom routes", - endpoints: append(testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), - testEndpoints("foo", "baz", "1.1.2", 1, map[string]int{"baz": 8181})...), + endpoints: append(testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), + testEndpoints("foo", "baz", map[string]string{}, "1.1.2", 1, map[string]int{"baz": 8181})...), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), - testService("foo", "baz", "1.2.3.6", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "baz", map[string]string{}, "1.2.3.6", map[string]int{"baz": 8181}), }, - ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", - `a: Method("OPTIONS") -> ; + ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `a: Method("OPTIONS") -> ; b: Cookie("alpha", /^enabled$/) -> "http://1.1.2.0:8181"; - c: Path("/a/path/somewhere") -> "https://some.other-url.org/a/path/somewhere";`, - "", "", definitions.BackendPort{}, 1.0, - testRule("www1.example.org", testPathRule("", "bar", definitions.BackendPort{Value: "baz"})), - testRule("www2.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), - testRule("www3.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})), - )}, + c: Path("/a/path/somewhere") -> "https://some.other-url.org/a/path/somewhere";`, "", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("", "bar", definitions.BackendPort{Value: "baz"})), testRule("www2.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), testRule("www3.example.org", testPathRule("/a/path", "bar", definitions.BackendPort{Value: "baz"})))}, expectedRoutes: map[string]string{ "kube_foo__qux__www1_example_org____bar": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) -> \"http://1.1.1.0:8181\"", "kube_foo__qux_a_0__www1_example_org____": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && Method(\"OPTIONS\") -> ", @@ -2763,30 +2881,22 @@ func TestSkipperCustomRoutes(t *testing.T) { }, }, { msg: "ingress with 1 host definitions and 1 additional custom route, changed pathmode to PathSubtree", - endpoints: testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), + endpoints: testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), }, - ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", - `Method("OPTIONS") -> `, - "path-prefix", "", definitions.BackendPort{}, 1.0, - testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), - )}, + ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `Method("OPTIONS") -> `, "path-prefix", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})))}, expectedRoutes: map[string]string{ "kube_foo__qux__www1_example_org_____bar": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathSubtree(\"/\") -> \"http://1.1.1.0:8181\"", "kube_foo__qux__0__www1_example_org_____": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && Method(\"OPTIONS\") && PathSubtree(\"/\") -> ", }, }, { msg: "ingress with 1 host definitions and 1 additional custom route with path predicate, changed pathmode to PathSubtree", - endpoints: testEndpoints("foo", "bar", "1.1.1", 1, map[string]int{"baz": 8181}), + endpoints: testEndpoints("foo", "bar", map[string]string{}, "1.1.1", 1, map[string]int{"baz": 8181}), services: []*service{ - testService("foo", "bar", "1.2.3.4", map[string]int{"baz": 8181}), + testService("foo", "bar", map[string]string{}, "1.2.3.4", map[string]int{"baz": 8181}), }, - ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", - `Path("/foo") -> `, - "path-prefix", "", definitions.BackendPort{}, 1.0, - testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})), - )}, + ingresses: []*definitions.IngressItem{testIngress("foo", "qux", "", "", "", "", `Path("/foo") -> `, "path-prefix", "", map[string]string{}, definitions.BackendPort{}, 1.0, testRule("www1.example.org", testPathRule("/", "bar", definitions.BackendPort{Value: "baz"})))}, expectedRoutes: map[string]string{ "kube_foo__qux__www1_example_org_____bar": "Host(/^(www1[.]example[.]org[.]?(:[0-9]+)?)$/) && PathSubtree(\"/\") -> \"http://1.1.1.0:8181\"", }, @@ -3002,10 +3112,8 @@ func TestSkipperDefaultFilters(t *testing.T) { }) t.Run("check default filters are applied to the route", func(t *testing.T) { - api.services = &serviceList{Items: []*service{testService("namespace1", "service1", "1.2.3.4", map[string]int{"port1": 8080})}} - api.ingresses = &definitions.IngressList{Items: []*definitions.IngressItem{testIngress("namespace1", "default-only", - "service1", "", "", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0, - testRule("www.example.org", testPathRule("/", "service1", definitions.BackendPort{Value: 8080})))}} + api.services = &serviceList{Items: []*service{testService("namespace1", "service1", map[string]string{}, "1.2.3.4", map[string]int{"port1": 8080})}} + api.ingresses = &definitions.IngressList{Items: []*definitions.IngressItem{testIngress("namespace1", "default-only", "service1", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0, testRule("www.example.org", testPathRule("/", "service1", definitions.BackendPort{Value: 8080})))}} defaultFiltersDir, err := os.MkdirTemp("", "filters") if err != nil { @@ -3041,10 +3149,8 @@ func TestSkipperDefaultFilters(t *testing.T) { t.Run("check default filters are prepended to the ingress filters", func(t *testing.T) { api.endpoints = testEndpointList() - api.services = &serviceList{Items: []*service{testServiceWithTargetPort("namespace1", "service1", "1.2.3.4", map[string]int{"port1": 8080}, map[int]*definitions.BackendPort{8080: {Value: 8080}})}} - api.ingresses = &definitions.IngressList{Items: []*definitions.IngressItem{testIngress("namespace1", "default-only", - "service1", "", "localRatelimit(20,\"1m\")", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0, - testRule("www.example.org", testPathRule("/", "service1", definitions.BackendPort{Value: "port1"})))}} + api.services = &serviceList{Items: []*service{testServiceWithTargetPort("namespace1", "service1", map[string]string{}, "1.2.3.4", map[string]int{"port1": 8080}, map[int]*definitions.BackendPort{8080: {Value: 8080}})}} + api.ingresses = &definitions.IngressList{Items: []*definitions.IngressItem{testIngress("namespace1", "default-only", "service1", "", "localRatelimit(20,\"1m\")", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0, testRule("www.example.org", testPathRule("/", "service1", definitions.BackendPort{Value: "port1"})))}} // store default configuration in the file dir, err := os.MkdirTemp("", "filters") diff --git a/dataclients/kubernetes/kubernetestest/api.go b/dataclients/kubernetes/kubernetestest/api.go index 9e6819da53..5327850b65 100644 --- a/dataclients/kubernetes/kubernetestest/api.go +++ b/dataclients/kubernetes/kubernetestest/api.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "regexp" + "strings" yaml2 "github.com/ghodss/yaml" "gopkg.in/yaml.v2" @@ -154,15 +155,15 @@ func (a *api) ServeHTTP(w http.ResponseWriter, r *http.Request) { var b []byte switch parts[3] { case "services": - b = ns.services + b = filterBySelectors(ns.services, parseSelectors(r)) case "ingresses": - b = ns.ingresses + b = filterBySelectors(ns.ingresses, parseSelectors(r)) case "routegroups": - b = ns.routeGroups + b = filterBySelectors(ns.routeGroups, parseSelectors(r)) case "endpoints": - b = ns.endpoints + b = filterBySelectors(ns.endpoints, parseSelectors(r)) case "secrets": - b = ns.secrets + b = filterBySelectors(ns.secrets, parseSelectors(r)) default: w.WriteHeader(http.StatusNotFound) return @@ -171,6 +172,69 @@ func (a *api) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write(b) } +// Parses an optional parameter with `label selectors` into a map if present or, if not present, returns nil. +func parseSelectors(r *http.Request) map[string]string { + rawSelector := r.URL.Query().Get("labelSelector") + if rawSelector == "" { + return nil + } + + selectors := map[string]string{} + for _, selector := range strings.Split(rawSelector, ",") { + kv := strings.Split(selector, "=") + selectors[kv[0]] = kv[1] + } + + return selectors +} + +// Filters all resources that are already set in k8s namespace using the given selectors map. +// All resources are initially set to `namespace` as slices of bytes and for most tests it's not needed to make it any more complex. +// This helper function deserializes resources, finds a metadata with labels in them and check if they have all +// requested labels. If they do, they are returned. +func filterBySelectors(resources []byte, selectors map[string]string) []byte { + if len(selectors) == 0 { + return resources + } + + labels := struct { + Items []struct { + Metadata struct { + Labels map[string]string `json:"labels"` + } `json:"metadata"` + } `json:"items"` + }{} + + // every resource but top level is deserialized because we need access to the indexed array + allItems := struct { + Items []interface{} `json:"items"` + }{} + + if json.Unmarshal(resources, &labels) != nil || json.Unmarshal(resources, &allItems) != nil { + return resources + } + + // go over each item's label and check if all selectors with their values are present + var filteredItems []interface{} + for idx, item := range labels.Items { + allMatch := true + for k, v := range selectors { + label, ok := item.Metadata.Labels[k] + allMatch = allMatch && ok && label == v + } + if allMatch { + filteredItems = append(filteredItems, allItems.Items[idx]) + } + } + + var result []byte + if err := itemsJSON(&result, filteredItems); err != nil { + return resources + } + + return result +} + func initNamespace(kinds map[string][]interface{}) (ns namespace, err error) { if err = itemsJSON(&ns.services, kinds["Service"]); err != nil { return diff --git a/dataclients/kubernetes/kubernetestest/fixtures.go b/dataclients/kubernetes/kubernetestest/fixtures.go index 8dce1fe26a..6040e2d74b 100644 --- a/dataclients/kubernetes/kubernetestest/fixtures.go +++ b/dataclients/kubernetes/kubernetestest/fixtures.go @@ -44,6 +44,9 @@ type kubeOptionsParser struct { AllowedExternalNames []string `yaml:"allowedExternalNames"` IngressClass string `yaml:"kubernetes-ingress-class"` KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` + IngressesLabels map[string]string `yaml:"kubernetes-ingresses-label-selector"` + ServicesLabels map[string]string `yaml:"kubernetes-services-label-selector"` + EndpointsLabels map[string]string `yaml:"kubernetes-endpoints-label-selector"` } func baseNoExt(n string) string { @@ -225,6 +228,9 @@ func testFixture(t *testing.T, f fixtureSet) { o.BackendNameTracingTag = kop.BackendNameTracingTag o.IngressClass = kop.IngressClass o.CertificateRegistry = cr + o.IngressLabelSelectors = kop.IngressesLabels + o.ServicesLabelSelectors = kop.ServicesLabels + o.EndpointsLabelSelectors = kop.EndpointsLabels aen, err := compileRegexps(kop.AllowedExternalNames) if err != nil { diff --git a/dataclients/kubernetes/path_test.go b/dataclients/kubernetes/path_test.go index 2d8733ceab..0371350941 100644 --- a/dataclients/kubernetes/path_test.go +++ b/dataclients/kubernetes/path_test.go @@ -80,7 +80,7 @@ func TestPathMatchingModes(t *testing.T) { setIngressWithPath := func(p string, annotations ...string) { i := testIngress( - "namespace1", "ingress1", "service1", "", "", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0, + "namespace1", "ingress1", "service1", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0, testRule("www.example.org", testPathRule(p, "service1", definitions.BackendPort{Value: 8080})), ) @@ -345,12 +345,12 @@ func TestIngressSpecificMode(t *testing.T) { defer api.Close() ingressWithDefault := testIngress( - "namespace1", "ingress1", "service1", "", "", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0, + "namespace1", "ingress1", "service1", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0, testRule("www.example.org", testPathRule("^/foo", "service1", definitions.BackendPort{Value: 8080})), ) ingressWithCustom := testIngress( - "namespace1", "ingress1", "service1", "", "", "", "", "", "", definitions.BackendPort{Value: 8080}, 1.0, + "namespace1", "ingress1", "service1", "", "", "", "", "", "", map[string]string{}, definitions.BackendPort{Value: 8080}, 1.0, testRule("www.example.org", testPathRule("/bar", "service1", definitions.BackendPort{Value: 8080})), ) ingressWithCustom.Metadata.Annotations[pathModeAnnotationKey] = pathPrefixString diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.eskip new file mode 100644 index 0000000000..43d164f153 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.eskip @@ -0,0 +1,4 @@ +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setPath("/foo") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.kube new file mode 100644 index 0000000000..922dfc57d9 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.kube @@ -0,0 +1,7 @@ +ingressv1: true +kubernetes-ingresses-label-selector: + ingress-enabled: true +kubernetes-services-label-selector: + service-enabled: true +kubernetes-endpoints-label-selector: + endpoint-enabled: true diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.yaml new file mode 100644 index 0000000000..12977adf4c --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-different-per-resource.yaml @@ -0,0 +1,53 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux + labels: + ingress-enabled: "true" + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar + port: + name: baz +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar + labels: + service-enabled: "true" +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + endpoint-enabled: "true" + namespace: foo + name: bar +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: baz + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.eskip new file mode 100644 index 0000000000..43d164f153 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.eskip @@ -0,0 +1,4 @@ +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setPath("/foo") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.kube new file mode 100644 index 0000000000..b81557b863 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.kube @@ -0,0 +1,3 @@ +ingressv1: true +kubernetes-ingresses-label-selector: + skipper-enabled: true diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.yaml new file mode 100644 index 0000000000..db44ade026 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-is-optional.yaml @@ -0,0 +1,49 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux + labels: + skipper-enabled: "true" + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar + port: + name: baz +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + namespace: foo + name: bar +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: baz + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.eskip new file mode 100644 index 0000000000..43d164f153 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.eskip @@ -0,0 +1,4 @@ +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setPath("/foo") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.kube new file mode 100644 index 0000000000..9630005e81 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.kube @@ -0,0 +1,10 @@ +ingressv1: true +kubernetes-ingresses-label-selector: + skipper-enabled: true + special-label: true +kubernetes-services-label-selector: + skipper-enabled: true + special-label: true +kubernetes-endpoints-label-selector: + skipper-enabled: true + special-label: true diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.yaml new file mode 100644 index 0000000000..2d27b99c36 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-multiple.yaml @@ -0,0 +1,56 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux + labels: + skipper-enabled: "true" + special-label: "true" + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar + port: + name: baz +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar + labels: + skipper-enabled: "true" + special-label: "true" +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + namespace: foo + name: bar + labels: + skipper-enabled: "true" + special-label: "true" +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: baz + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.eskip new file mode 100644 index 0000000000..43d164f153 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.eskip @@ -0,0 +1,4 @@ +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setPath("/foo") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.kube new file mode 100644 index 0000000000..4e0e7619b4 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.kube @@ -0,0 +1,7 @@ +ingressv1: true +kubernetes-ingresses-label-selector: + skipper-enabled: true +kubernetes-services-label-selector: + skipper-enabled: true +kubernetes-endpoints-label-selector: + skipper-enabled: true diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.yaml new file mode 100644 index 0000000000..fd9a72d409 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter.yaml @@ -0,0 +1,103 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux + labels: + skipper-enabled: "true" + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar + port: + name: baz +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar + labels: + skipper-enabled: "true" +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + skipper-enabled: "true" + namespace: foo + name: bar +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: baz + port: 8080 + protocol: TCP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux-no-label + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar-no-label + port: + name: baz-no-label +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar-no-label +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz-no-label + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + skipper-enabled: "true" + namespace: foo + name: bar-no-label +subsets: + - addresses: + - ip: 10.2.9.105 + - ip: 10.2.9.106 + ports: + - name: baz-no-label + port: 8080 + protocol: TCP diff --git a/skipper.go b/skipper.go index 779109ceb1..9254d7e6ef 100644 --- a/skipper.go +++ b/skipper.go @@ -198,6 +198,37 @@ type Options struct { // annotation, or which an empty annotation, will be loaded too. KubernetesRouteGroupClass string + // KubernetesIngressLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. A label and its value on an Ingress must be match exactly to be loaded by Skipper. + // If the value is irrelevant for a given configuration, it can be left empty. The default + // value is no labels required. + // Examples: + // Config [] will load all Ingresses. + // Config ["skipper-enabled": ""] will load only Ingresses with a label "skipper-enabled", no matter the value. + // Config ["skipper-enabled": "true"] will load only Ingresses with a label "skipper-enabled: true" + // Config ["skipper-enabled": "", "foo": "bar"] will load only Ingresses with both labels while label "foo" must have a value "bar". + KubernetesIngressLabelSelectors map[string]string + + // KubernetesServicesLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + KubernetesServicesLabelSelectors map[string]string + + // KubernetesEndpointsLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + KubernetesEndpointsLabelSelectors map[string]string + + // KubernetesSecretsLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + KubernetesSecretsLabelSelectors map[string]string + + // KubernetesRouteGroupsLabelSelectors is a map of kubernetes labels to their values that must be present on a resource to be loaded + // by the client. Read documentation for IngressLabelSelectors for examples and more details. + // The default value is no labels required. + KubernetesRouteGroupsLabelSelectors map[string]string + // PathMode controls the default interpretation of ingress paths in cases // when the ingress doesn't specify it with an annotation. KubernetesPathMode kubernetes.PathMode @@ -956,6 +987,11 @@ func createDataClients(o Options, auth innkeeper.Authentication, cr *certregistr KubernetesEastWestRangePredicates: o.KubernetesEastWestRangePredicates, HTTPSRedirectCode: o.KubernetesHTTPSRedirectCode, IngressClass: o.KubernetesIngressClass, + IngressLabelSelectors: o.KubernetesIngressLabelSelectors, + ServicesLabelSelectors: o.KubernetesServicesLabelSelectors, + EndpointsLabelSelectors: o.KubernetesEndpointsLabelSelectors, + SecretsLabelSelectors: o.KubernetesSecretsLabelSelectors, + RouteGroupsLabelSelectors: o.KubernetesRouteGroupsLabelSelectors, OnlyAllowedExternalNames: o.KubernetesOnlyAllowedExternalNames, OriginMarker: o.EnableRouteCreationMetrics, PathMode: o.KubernetesPathMode, From 54ff9d009ad4e09295480f51c8d7ec0afeb2be67 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 9 Sep 2022 16:29:25 +0200 Subject: [PATCH 0075/1047] godoc: reformat all sources with gofmt 1.19 (#2074) * godoc: reformat all sources with gofmt 1.19 `gofmt` misinterprets code blocks of `eskip` examples starting with `*` as a bullet list. This change adds route id to all such eskip examples such that subsequent reformatting with gofmt 1.18 and 1.19 does not produce any changes. Signed-off-by: Alexander Yastrebov * ignore statickcheck error Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- circuit/doc.go | 26 +- circuit/registry.go | 2 +- cmd/eskip/doc.go | 20 +- cmd/skipper/main.go | 2 +- dataclients/kubernetes/clusterclient.go | 9 +- dataclients/kubernetes/definitions/doc.go | 4 +- dataclients/kubernetes/doc.go | 259 +++++++++-------- dataclients/kubernetes/ingressv1.go | 18 +- dataclients/kubernetes/ingressv1beta1.go | 18 +- dataclients/routestring/string.go | 3 +- doc.go | 213 +++++++------- eskip/doc.go | 36 +-- eskip/eq.go | 4 - eskip/eskip.go | 18 +- eskip/template.go | 3 +- filters/accesslog/control.go | 8 +- filters/accesslog/disable.go | 3 +- filters/accesslog/doc.go | 6 +- filters/apiusagemonitoring/doc.go | 9 +- filters/auth/basic.go | 2 +- filters/auth/doc.go | 322 +++++++++++----------- filters/auth/jwt_validation.go | 2 +- filters/auth/oidc.go | 33 ++- filters/auth/tokeninfo.go | 4 +- filters/auth/tokenintrospection.go | 2 +- filters/auth/webhook.go | 5 +- filters/block/matcher.go | 1 - filters/builtin/builtin.go | 1 + filters/builtin/compress.go | 9 +- filters/builtin/decompress.go | 1 - filters/builtin/headertoquery.go | 2 +- filters/builtin/inlinecontent.go | 5 +- filters/builtin/inlinecontentifstatus.go | 3 +- filters/builtin/query.go | 2 +- filters/builtin/querytoheader.go | 7 +- filters/circuit/breaker.go | 4 +- filters/cookie/cookie.go | 10 +- filters/cors/doc.go | 2 +- filters/diag/diag.go | 36 +-- filters/doc.go | 9 +- filters/flowid/doc.go | 19 +- filters/log/log.go | 2 +- filters/ratelimit/leakybucket.go | 3 +- filters/ratelimit/ratelimit.go | 57 ++-- filters/rfc/rfc.go | 2 - filters/scheduler/doc.go | 1 - filters/scheduler/lifo.go | 1 - filters/sed/doc.go | 10 +- filters/sed/editor.go | 1 - filters/serve/serve.go | 9 +- filters/shedder/admission.go | 12 +- filters/tee/doc.go | 2 +- filters/tee/tee.go | 3 +- filters/tracing/spanname.go | 3 +- loadbalancer/doc.go | 44 ++- logging/doc.go | 14 +- metrics/doc.go | 4 +- net/net.go | 4 +- oauth/oauth.go | 4 +- predicates/auth/jwt.go | 12 +- predicates/cookie/cookie.go | 3 +- predicates/forwarded/forwarded.go | 12 +- predicates/interval/interval.go | 9 +- predicates/methods/methods.go | 12 +- predicates/query/query.go | 25 +- predicates/source/source.go | 16 +- predicates/traffic/traffic.go | 73 +++-- proxy/doc.go | 20 +- ratelimit/doc.go | 48 ++-- rfc/patchpath.go | 3 +- routing/doc.go | 15 +- routing/matcher.go | 1 - scheduler/scheduler.go | 1 - secrets/readers.go | 8 +- swarm/doc.go | 6 +- tracing/tracing.go | 34 +-- 76 files changed, 754 insertions(+), 862 deletions(-) diff --git a/circuit/doc.go b/circuit/doc.go index 70c37a4965..dbd1f466ae 100644 --- a/circuit/doc.go +++ b/circuit/doc.go @@ -9,7 +9,7 @@ The circuit breakers are always assigned to backend hosts, so that the outcome o affects the circuit breaker behavior of another host. Besides hosts, individual routes can have separate circuit breakers, too. -Breaker Type - Consecutive Failures +# Breaker Type - Consecutive Failures This breaker opens when the proxy couldn't connect to a backend or received a >=500 status code at least N times in a row. When open, the proxy returns 503 - Service Unavailable response during the breaker timeout. After this @@ -17,14 +17,14 @@ timeout, the breaker goes into half-open state, in which it expects that M numbe requests in the half-open state are accepted concurrently. If any of the requests during the half-open state fails, the breaker goes back to open state. If all succeed, it goes to closed state again. -Breaker Type - Failure Rate +# Breaker Type - Failure Rate The "rate breaker" works similar to the "consecutive breaker", but instead of considering N consecutive failures for going open, it maintains a sliding window of the last M events, both successes and failures, and opens only when the number of failures reaches N within the window. This way the sliding window is not time based and allows the same breaker characteristics for low and high rate traffic. -Usage +# Usage When imported as a package, the Registry can be used to hold the circuit breakers and their settings. On a higher level, the circuit breaker settings can be simply passed to skipper as part of the skipper.Options @@ -63,7 +63,7 @@ Setting global values happens the same way as setting host values, but leaving t route based values happens with filters in the route definitions. (https://godoc.org/github.com/zalando/skipper/filters/circuit) -Settings - Type +# Settings - Type It can be ConsecutiveFailures, FailureRate or Disabled, where the first two values select which breaker to use, while the Disabled value can override a global or host configuration disabling the circuit breaker for the @@ -71,46 +71,46 @@ specific host or route. Command line name: type. Possible command line values: consecutive, rate, disabled. -Settings - Host +# Settings - Host The Host field indicates to which backend host should the current set of settings be applied. Leaving it empty indicates global settings. Command line name: host. -Settings - Window +# Settings - Window The window value sets the size of the sliding counter window of the failure rate breaker. Command line name: window. Possible command line values: any positive integer. -Settings - Failures +# Settings - Failures The failures value sets the max failure count for both the "consecutive" and "rate" breakers. Command line name: failures. Possible command line values: any positive integer. -Settings - Timeout +# Settings - Timeout With the timeout we can set how long the breaker should stay open, before becoming half-open. Command line name: timeout. Possible command line values: any positive integer as milliseconds or a duration string, e.g. 15m30s. -Settings - Half-Open Requests +# Settings - Half-Open Requests Defines the number of requests expected to succeed while the circuit breaker is in the half-open state. Command line name: half-open-requests. Possible command line values: any positive integer. -Settings - Idle TTL +# Settings - Idle TTL Defines the idle timeout after which a circuit breaker gets recycled, if it hasn't been used. Command line name: idle-ttl. Possible command line values: any positive integer as milliseconds or a duration string, e.g. 15m30s. -Filters +# Filters The following circuit breaker filters are supported: consecutiveBreaker(), rateBreaker() and disableBreaker(). @@ -129,7 +129,7 @@ route that it appears in. disableBreaker() -Proxy Usage +# Proxy Usage The proxy, when circuit breakers are configured, uses them for backend connections. It checks the breaker for the current backend host if it's closed before making backend requests. It reports the outcome of the request to @@ -139,7 +139,7 @@ breaker is open, the proxy doesn't try to make backend requests, and returns a r X-Circuit-Open: true -Registry +# Registry The active circuit breakers are stored in a registry. They are created on-demand, for the requested settings. The registry synchronizes access to the shared circuit breakers. When the registry detects that a circuit diff --git a/circuit/registry.go b/circuit/registry.go index d403e31e54..eb388ab626 100644 --- a/circuit/registry.go +++ b/circuit/registry.go @@ -96,7 +96,7 @@ func (r *Registry) get(s BreakerSettings) *Breaker { // Get returns a circuit breaker for the provided settings. The BreakerSettings object is used here as a key, // but typically it is enough to just set its Host field: // -// r.Get(BreakerSettings{Host: backendHost}) +// r.Get(BreakerSettings{Host: backendHost}) // // The key will be filled up with the defaults and the matching circuit breaker will be returned if it exists, // or a new one will be created if not. diff --git a/cmd/eskip/doc.go b/cmd/eskip/doc.go index 356936313b..38f2ba9dc9 100644 --- a/cmd/eskip/doc.go +++ b/cmd/eskip/doc.go @@ -18,41 +18,41 @@ formatted routes from and to different data sources. For command line help, enter: - eskip -help + eskip -help -Examples +# Examples Check if an eskip file has valid syntax: - eskip check routes.eskip + eskip check routes.eskip Print routes stored in etcd: - eskip print -etcd-urls https://etcd.example.org + eskip print -etcd-urls https://etcd.example.org Print routes as JSON: - eskip print -json + eskip print -json Insert/update routes in etcd from an eskip file: - eskip upsert routes.eskip + eskip upsert routes.eskip Sync routes from an eskip file to etcd: - eskip reset routes.eskip + eskip reset routes.eskip Delete routes from etcd: - eskip delete -ids route1,route2,route3 + eskip delete -ids route1,route2,route3 Delete all routes from etcd: - eskip print | eskip delete + eskip print | eskip delete Copy all routes in etcd under a different prefix: - eskip print | eskip upsert -etcd-prefix /skipper-backup + eskip print | eskip upsert -etcd-prefix /skipper-backup (Where -etcd-urls is not set for write operations like upsert, reset and delete, the default etcd cluster urls are used: diff --git a/cmd/skipper/main.go b/cmd/skipper/main.go index ba30378dbd..b39d7db6a4 100644 --- a/cmd/skipper/main.go +++ b/cmd/skipper/main.go @@ -4,7 +4,7 @@ set of filters. For the list of command line options, run: - skipper -help + skipper -help For details about the usage and extensibility of skipper, please see the documentation of the root skipper package. diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index 532ee8d5f6..aed07eec6d 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -194,10 +194,11 @@ func newClusterClient(o Options, apiURL, ingCls, rgCls string, quit <-chan struc // serializes a given map of label selectors to a string that can be appended to a request URI to kubernetes // Examples (note that the resulting value in the query is URL escaped, for readability this is not done in examples): -// [] becomes `` -// ["label": ""] becomes `?labelSelector=label` -// ["label": "value"] becomes `?labelSelector=label=value` -// ["label": "value", "label2": "value2"] becomes `?labelSelector=label=value&label2=value2` +// +// [] becomes `` +// ["label": ""] becomes `?labelSelector=label` +// ["label": "value"] becomes `?labelSelector=label=value` +// ["label": "value", "label2": "value2"] becomes `?labelSelector=label=value&label2=value2` func toLabelSelectorQuery(selectors map[string]string) string { if len(selectors) == 0 { return "" diff --git a/dataclients/kubernetes/definitions/doc.go b/dataclients/kubernetes/definitions/doc.go index 96feb8a047..1eda8fd7f2 100644 --- a/dataclients/kubernetes/definitions/doc.go +++ b/dataclients/kubernetes/definitions/doc.go @@ -1,3 +1,3 @@ -/* Package definitions provides type definitions, parsing, marshaling and -validation for Kubernetes resources used by Skipper. */ +// Package definitions provides type definitions, parsing, marshaling and +// validation for Kubernetes resources used by Skipper. package definitions diff --git a/dataclients/kubernetes/doc.go b/dataclients/kubernetes/doc.go index f8b1b290ec..18e9b1a314 100644 --- a/dataclients/kubernetes/doc.go +++ b/dataclients/kubernetes/doc.go @@ -16,7 +16,7 @@ Both Kube-ingress-aws-controller and Skipper Kubernetes are part of the larger p https://github.com/zalando-incubator/kubernetes-on-aws/ -Ingress shutdown by healthcheck +# Ingress shutdown by healthcheck The Kubernetes ingress client catches TERM signals when the ProvideHealthcheck option is enabled, and reports failing healthcheck after the signal was received. This means that, when the Ingress client is responsible for @@ -24,149 +24,149 @@ the healthcheck of the cluster, and the Skipper process receives the TERM signal immediately, but will start reporting failures on healthcheck requests. Until it gets killed by the kubelet, Skipper keeps serving the requests in this case. -Example - Ingress +# Example - Ingress A basic ingress specification: - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with ratelimiting + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with ratelimiting The example shows 50 calls per minute are allowed to each skipper instance for the given ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/ratelimit: ratelimit(50, "1m") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with client based ratelimiting + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/ratelimit: ratelimit(50, "1m") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with client based ratelimiting The example shows 3 calls per minute per client, based on X-Forwarded-For header or IP incase there is no X-Forwarded-For header set, are allowed to each skipper instance for the given ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/ratelimit: localRatelimit(3, "1m") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/ratelimit: localRatelimit(3, "1m") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 The example shows 500 calls per hour per client, based on Authorization header set, are allowed to each skipper instance for the given ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/ratelimit: localRatelimit(500, "1h", "auth") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with custom skipper filter configuration + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/ratelimit: localRatelimit(500, "1h", "auth") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with custom skipper filter configuration The example shows the use of 2 filters from skipper for the implicitly defined route in ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-filter: localRatelimit(50, "10m") -> requestCookie("test-session", "abc") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with custom skipper Predicate configuration + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-filter: localRatelimit(50, "10m") -> requestCookie("test-session", "abc") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with custom skipper Predicate configuration The example shows the use of a skipper predicates for the implicitly defined route in ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-predicate: QueryParam("query", "^example$") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with custom skipper Routes configuration + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-predicate: QueryParam("query", "^example$") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with custom skipper Routes configuration The example shows the use of custom skipper routes which be additional to the routes generated for the ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-routes: | - Method("OPTIONS") -> - setResponseHeader("Access-Control-Allow-Origin", "*") -> - setResponseHeader("Access-Control-Allow-Methods", "GET, OPTIONS") -> - setResponseHeader("Access-Control-Allow-Headers", "Authorization") -> - status(200) -> - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with shadow traffic + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-routes: | + Method("OPTIONS") -> + setResponseHeader("Access-Control-Allow-Origin", "*") -> + setResponseHeader("Access-Control-Allow-Methods", "GET, OPTIONS") -> + setResponseHeader("Access-Control-Allow-Headers", "Authorization") -> + status(200) -> + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with shadow traffic This will send production traffic to app-default.example.org and copies incoming requests to https://app.shadow.example.org, but drops @@ -174,20 +174,19 @@ responses from shadow URL. This is helpful to test your next generation software with production workload. See also https://godoc.org/github.com/zalando/skipper/filters/tee for details. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-filter: tee("https://app.shadow.example.org") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-filter: tee("https://app.shadow.example.org") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 */ package kubernetes diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index 8befeb42f6..8826bba5b6 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -214,23 +214,23 @@ func (ing *ingress) addEndpointsRuleV1(ic ingressContext, host string, prule *de // rule backends. // The traffic is calculated based on the following rules: // -// * if no weight is defined for a backend it will get weight 0. -// * if no weights are specified for all backends of a path, then traffic will -// be distributed equally. +// - if no weight is defined for a backend it will get weight 0. +// - if no weights are specified for all backends of a path, then traffic will +// be distributed equally. // // Each traffic weight is relative to the number of backends per path. If there // are multiple backends per path the weight will be relative to the number of // remaining backends for the path e.g. if the weight is specified as // -// backend-1: 0.2 -// backend-2: 0.6 -// backend-3: 0.2 +// backend-1: 0.2 +// backend-2: 0.6 +// backend-3: 0.2 // // then the weight will be calculated to: // -// backend-1: 0.2 -// backend-2: 0.75 -// backend-3: 1.0 +// backend-1: 0.2 +// backend-2: 0.75 +// backend-3: 1.0 // // where for a weight of 1.0 no Traffic predicate will be generated. func computeBackendWeightsV1(backendWeights map[string]float64, rule *definitions.RuleV1) { diff --git a/dataclients/kubernetes/ingressv1beta1.go b/dataclients/kubernetes/ingressv1beta1.go index cb81148c75..e46ed95e33 100644 --- a/dataclients/kubernetes/ingressv1beta1.go +++ b/dataclients/kubernetes/ingressv1beta1.go @@ -213,23 +213,23 @@ func (ing *ingress) addEndpointsRule(ic ingressContext, host string, prule *defi // rule backends. // The traffic is calculated based on the following rules: // -// * if no weight is defined for a backend it will get weight 0. -// * if no weights are specified for all backends of a path, then traffic will -// be distributed equally. +// - if no weight is defined for a backend it will get weight 0. +// - if no weights are specified for all backends of a path, then traffic will +// be distributed equally. // // Each traffic weight is relative to the number of backends per path. If there // are multiple backends per path the weight will be relative to the number of // remaining backends for the path e.g. if the weight is specified as // -// backend-1: 0.2 -// backend-2: 0.6 -// backend-3: 0.2 +// backend-1: 0.2 +// backend-2: 0.6 +// backend-3: 0.2 // // then the weight will be calculated to: // -// backend-1: 0.2 -// backend-2: 0.75 -// backend-3: 1.0 +// backend-1: 0.2 +// backend-2: 0.75 +// backend-3: 1.0 // // where for a weight of 1.0 no Traffic predicate will be generated. func computeBackendWeights(backendWeights map[string]float64, rule *definitions.Rule) { diff --git a/dataclients/routestring/string.go b/dataclients/routestring/string.go index d68fc6b986..cb3dff6016 100644 --- a/dataclients/routestring/string.go +++ b/dataclients/routestring/string.go @@ -3,8 +3,7 @@ // // Usage from the command line: // -// skipper -inline-routes '* -> inlineContent("Hello, world!") -> ' -// +// skipper -inline-routes '* -> inlineContent("Hello, world!") -> ' package routestring import ( diff --git a/doc.go b/doc.go index 67c49ae9e1..da3573ad20 100644 --- a/doc.go +++ b/doc.go @@ -20,35 +20,33 @@ predicates or data sources. For further information read Skipper took the core design and inspiration from Vulcand: https://github.com/mailgun/vulcand. - -Quickstart +# Quickstart Skipper is 'go get' compatible. If needed, create a 'go workspace' first: - mkdir ws - cd ws - export GOPATH=$(pwd) - export PATH=$PATH:$GOPATH/bin + mkdir ws + cd ws + export GOPATH=$(pwd) + export PATH=$PATH:$GOPATH/bin Get the Skipper packages: - go get github.com/zalando/skipper/... + go get github.com/zalando/skipper/... Create a file with a route: - echo 'hello: Path("/hello") -> "https://www.example.org"' > example.eskip + echo 'hello: Path("/hello") -> "https://www.example.org"' > example.eskip Optionally, verify the syntax of the file: - eskip check example.eskip + eskip check example.eskip Start Skipper and make an HTTP request: - skipper -routes-file example.eskip & - curl localhost:9090/hello - + skipper -routes-file example.eskip & + curl localhost:9090/hello -Routing Mechanism +# Routing Mechanism The core of Skipper's request processing is implemented by a reverse proxy in the 'proxy' package. The proxy receives the incoming request, @@ -81,8 +79,7 @@ calculated decision. For further details, see the 'proxy' and 'filters' package documentation. - -Matching Requests +# Matching Requests Finding a request's route happens by matching the request attributes to the conditions in the route's definitions. Such definitions may have the @@ -108,8 +105,7 @@ meaning, that a request must fulfill each condition to match a route. For further details, see the 'routing' package documentation. - -Filters - Augmenting Requests +# Filters - Augmenting Requests Filters are applied in order of definition to the request and in reverse order to the response. They are used to modify request and response @@ -120,8 +116,7 @@ parameters, that are set specifically to the route. For further details, see the 'filters' package documentation. - -Service Backends +# Service Backends Each route has one of the following backends: HTTP endpoint, shunt, loopback or dynamic. @@ -143,8 +138,7 @@ serves as a form of an internal redirect. A dynamic route means that the final target will be defined in a filter. One of the filters in the chain must set the target backend url explicitly. - -Route Definitions +# Route Definitions Route definitions consist of the following: @@ -164,15 +158,13 @@ parser.) For further details, see the 'eskip' package documentation - -Authentication and Authorization +# Authentication and Authorization Skipper has filter implementations of basic auth and OAuth2. It can be integrated with tokeninfo based OAuth2 providers. For details, see: https://godoc.org/github.com/zalando/skipper/filters/auth. - -Data Sources +# Data Sources Skipper's route definitions of Skipper are loaded from one or more data sources. It can receive incremental updates from those data sources at @@ -200,8 +192,7 @@ updates. Skipper can use additional data sources, provided by extensions. Sources must implement the DataClient interface in the routing package. - -Circuit Breaker +# Circuit Breaker Skipper provides circuit breakers, configured either globally, based on backend hosts or based on individual routes. It supports two types of @@ -209,8 +200,7 @@ circuit breaker behavior: open on N consecutive failures, or open on N failures out of M requests. For details, see: https://godoc.org/github.com/zalando/skipper/circuit. - -Running Skipper +# Running Skipper Skipper can be started with the default executable command 'skipper', or as a library built into an application. The easiest way to start Skipper @@ -222,23 +212,21 @@ default executable as well, as a command line flag. E.g. EtcdUrls becomes -etcd-urls as a comma separated list. For command line help, enter: - skipper -help + skipper -help An additional utility, eskip, can be used to verify, print, update and delete routes from/to files or etcd (Innkeeper on the roadmap). See the cmd/eskip command package, and/or enter in the command line: - eskip -help - + eskip -help -Extending Skipper +# Extending Skipper Skipper doesn't use dynamically loaded plugins, however, it can be used as a library, and it can be extended with custom predicates, filters and/or custom data sources. - -Custom Predicates +# Custom Predicates To create a custom predicate, one needs to implement the PredicateSpec interface in the routing package. Instances of the PredicateSpec are @@ -247,45 +235,44 @@ objects as referenced in eskip routes, with concrete arguments. Example, randompredicate.go: - package main + package main - import ( - "github.com/zalando/skipper/routing" - "math/rand" - "net/http" - ) + import ( + "github.com/zalando/skipper/routing" + "math/rand" + "net/http" + ) - type randomSpec struct {} + type randomSpec struct {} - type randomPredicate struct { - chance float64 - } + type randomPredicate struct { + chance float64 + } - func (s *randomSpec) Name() string { return "Random" } + func (s *randomSpec) Name() string { return "Random" } - func (s *randomSpec) Create(args []interface{}) (routing.Predicate, error) { - p := &randomPredicate{.5} - if len(args) > 0 { - if c, ok := args[0].(float64); ok { - p.chance = c - } - } + func (s *randomSpec) Create(args []interface{}) (routing.Predicate, error) { + p := &randomPredicate{.5} + if len(args) > 0 { + if c, ok := args[0].(float64); ok { + p.chance = c + } + } - return p, nil - } + return p, nil + } - func (p *randomPredicate) Match(_ *http.Request) bool { - return rand.Float64() < p.chance - } + func (p *randomPredicate) Match(_ *http.Request) bool { + return rand.Float64() < p.chance + } In the above example, a custom predicate is created, that can be referenced in eskip definitions with the name 'Random': - Random(.33) -> "https://test.example.org"; - * -> "https://www.example.org" - + Random(.33) -> "https://test.example.org"; + * -> "https://www.example.org" -Custom Filters +# Custom Filters To create a custom filter we need to implement the Spec interface of the filters package. 'Spec' is the specification of a filter, and it is used @@ -294,48 +281,47 @@ processed. Example, hellofilter.go: - package main + package main - import ( - "fmt" - "github.com/zalando/skipper/filters" - ) + import ( + "fmt" + "github.com/zalando/skipper/filters" + ) - type helloSpec struct {} + type helloSpec struct {} - type helloFilter struct { - who string - } + type helloFilter struct { + who string + } - func (s *helloSpec) Name() string { return "hello" } + func (s *helloSpec) Name() string { return "hello" } - func (s *helloSpec) CreateFilter(config []interface{}) (filters.Filter, error) { - if len(config) == 0 { - return nil, filters.ErrInvalidFilterParameters - } + func (s *helloSpec) CreateFilter(config []interface{}) (filters.Filter, error) { + if len(config) == 0 { + return nil, filters.ErrInvalidFilterParameters + } - if who, ok := config[0].(string); ok { - return &helloFilter{who}, nil - } else { - return nil, filters.ErrInvalidFilterParameters - } - } + if who, ok := config[0].(string); ok { + return &helloFilter{who}, nil + } else { + return nil, filters.ErrInvalidFilterParameters + } + } - func (f *helloFilter) Request(ctx filters.FilterContext) {} + func (f *helloFilter) Request(ctx filters.FilterContext) {} - func (f *helloFilter) Response(ctx filters.FilterContext) { - ctx.Response().Header.Set("X-Hello", fmt.Sprintf("Hello, %s!", f.who)) - } + func (f *helloFilter) Response(ctx filters.FilterContext) { + ctx.Response().Header.Set("X-Hello", fmt.Sprintf("Hello, %s!", f.who)) + } The above example creates a filter specification, and in the routes where they are included, the filter instances will set the 'X-Hello' header for each and every response. The name of the filter is 'hello', and in a route definition it is referenced as: - * -> hello("world") -> "https://www.example.org" + r: * -> hello("world") -> "https://www.example.org"; - -Custom Build +# Custom Build The easiest way to create a custom Skipper variant is to implement the required filters (as in the example above) by importing the Skipper @@ -343,45 +329,43 @@ package, and starting it with the 'Run' command. Example, hello.go: - package main + package main - import ( - "log" + import ( + "log" - "github.com/zalando/skipper" - "github.com/zalando/skipper/filters" - "github.com/zalando/skipper/routing" - ) + "github.com/zalando/skipper" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/routing" + ) - func main() { - log.Fatal(skipper.Run(skipper.Options{ - Address: ":9090", - RoutesFile: "routes.eskip", - CustomPredicates: []routing.PredicateSpec{&randomSpec{}}, - CustomFilters: []filters.Spec{&helloSpec{}}})) - } + func main() { + log.Fatal(skipper.Run(skipper.Options{ + Address: ":9090", + RoutesFile: "routes.eskip", + CustomPredicates: []routing.PredicateSpec{&randomSpec{}}, + CustomFilters: []filters.Spec{&helloSpec{}}})) + } A file containing the routes, routes.eskip: - random: - Random(.05) -> hello("fish?") -> "https://fish.example.org"; - hello: - * -> hello("world") -> "https://www.example.org" + random: + Random(.05) -> hello("fish?") -> "https://fish.example.org"; + hello: + * -> hello("world") -> "https://www.example.org" Start the custom router: - go run hello.go - + go run hello.go -Proxy Package Used Individually +# Proxy Package Used Individually The 'Run' function in the root Skipper package starts its own listener but it doesn't provide the best composability. The proxy package, however, provides a standard http.Handler, so it is possible to use it in a more complex solution as a building block for routing. - -Logging and Metrics +# Logging and Metrics Skipper provides detailed logging of failures, and access logs in Apache log format. Skipper also collects detailed performance metrics, and @@ -389,8 +373,7 @@ exposes them on a separate listener endpoint for pulling snapshots. For details, see the 'logging' and 'metrics' packages documentation. - -Performance Considerations +# Performance Considerations The router's performance depends on the environment and on the used filters. Under ideal circumstances, and without filters, the biggest @@ -401,7 +384,7 @@ lookup tree in a single structure. Benchmarks for the tree lookup can be run by: - go test github.com/zalando/skipper/routing -bench=Tree + go test github.com/zalando/skipper/routing -bench=Tree In case more aggressive scale is needed, it is possible to setup Skipper in a cascade model, with multiple Skipper instances for specific route diff --git a/eskip/doc.go b/eskip/doc.go index ffd0ece177..37a4b9082f 100644 --- a/eskip/doc.go +++ b/eskip/doc.go @@ -3,8 +3,7 @@ Package eskip implements an in-memory representation of Skipper routes and a DSL for describing Skipper route expressions, route definitions and complete routing tables. - -Grammar Summary +# Grammar Summary A routing table is built up from 0 or more route definitions. The definitions are separated by ';'. A route definition contains one route @@ -32,8 +31,7 @@ A route expression example: modPath("^/api", "") -> requestHeader("X-Type", "external") -> "https://api.example.org" - -Match Expressions - Predicates +# Match Expressions - Predicates A match expression contains one or more predicates. An incoming request must fulfill each of them to match the route. The predicates are @@ -99,8 +97,7 @@ Catch all predicate. Former, deprecated form of the catch all predicate. - -Custom Predicates +# Custom Predicates Eskip supports custom route matching predicates, that can be implemented in extensions. The notation of custom predicates is the same as of the @@ -114,8 +111,7 @@ responsibility of the implementation to validate them. (See the documentation of the routing package.) - -Filters +# Filters Filters are used to augment the incoming requests and the outgoing responses, or do other useful or fun stuff. Filters can have different @@ -176,15 +172,13 @@ extendable primarily by implementing custom filters, for details about how to create custom filters, please, refer to the documentation of the root skipper package. - -Naming conventions +# Naming conventions Note, that the naming of predicates and filters follows the following convention: both predicates and filters are written in camel case, and predicates start with upper case, while filters start with lower case. - -Backend +# Backend There are four backend types: network endpoint address, shunt, loopback and dynamic. @@ -223,8 +217,7 @@ dynamic: The dynamic backend means that a filter must be present in the filter chain which must set the target url explicitly. - -Comments +# Comments An eskip document can contain comments. The rule for comments is simple: everything is a comment that starts with '//' and ends with a new-line @@ -236,22 +229,19 @@ Example with comments: route1: Path("/api") -> "https://api.example.org"; route2: * -> // everything else 404 - -Regular expressions +# Regular expressions The matching predicates and the built-in filters that use regular expressions, use the go stdlib regexp, which uses re2: https://github.com/google/re2/wiki/Syntax - -Parsing Filters +# Parsing Filters The eskip.ParseFilters method can be used to parse a chain of filters, without the matcher and backend part of a full route expression. - -Parsing +# Parsing Parsing a routing table or a route expression happens with the eskip.Parse function. In case of grammar error, it returns an error with @@ -262,15 +252,13 @@ The eskip parser does not validate the routes against all semantic rules, e.g., whether a filter or a custom predicate implementation is available. This validation happens during processing the parsed definitions. - -Serializing +# Serializing Serializing a single route happens by calling its String method. Serializing a complete routing table happens by calling the eskip.String method. - -JSON +# JSON Both serializing and parsing is possible via the standard json.Marshal and json.Unmarshal functions. diff --git a/eskip/eq.go b/eskip/eq.go index cc1a013691..49d18658e6 100644 --- a/eskip/eq.go +++ b/eskip/eq.go @@ -135,7 +135,6 @@ func eq2Lists(left, right []*Route) bool { // If there are multiple methods, only the last one is considered, to // reproduce the route matching (even if how it works, may not be the // most expected in regard of the method predicates). -// func Eq(r ...*Route) bool { for i := 1; i < len(r); i++ { if !eq2(r[i-1], r[i]) { @@ -150,7 +149,6 @@ func Eq(r ...*Route) bool { // by each list are equal by Eq(). Repeated route IDs are considered invalid // and EqLists always returns false in this case. The order of the routes in // the lists doesn't matter. -// func EqLists(r ...[]*Route) bool { rc := make([][]*Route, len(r)) for i := range rc { @@ -175,7 +173,6 @@ func EqLists(r ...[]*Route) bool { // standard, non-legacy representation of the predicates and the backends. // Canonical creates a copy of the route, but doesn't necessarily creates a // copy of every field. See also Copy(). -// func Canonical(r *Route) *Route { if r == nil { return nil @@ -272,7 +269,6 @@ func Canonical(r *Route) *Route { // keeping the order. The returned slice is a new slice of the input // slice but the routes in the slice and their fields are not necessarily // all copied. See more at CopyRoutes() and Canonical(). -// func CanonicalList(l []*Route) []*Route { if len(l) == 0 { return nil diff --git a/eskip/eskip.go b/eskip/eskip.go index fa9a66802d..07c26e02b3 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -28,10 +28,10 @@ var ( // --edit-route='/Source[(](.*)[)]/ClientIP($1)/', which will change // routes as you can see: // -// # input -// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> -// # actual route -// edit_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> +// # input +// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; +// # actual route +// edit_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; func NewEditor(reg *regexp.Regexp, repl string) *Editor { return &Editor{ reg: reg, @@ -50,11 +50,11 @@ type Editor struct { // --clone-route='/Source[(](.*)[)]/ClientIP($1)/', which will change // routes as you can see: // -// # input -// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> -// # actual route -// clone_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> -// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> +// # input +// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; +// # actual route +// clone_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; +// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; func NewClone(reg *regexp.Regexp, repl string) *Clone { return &Clone{ reg: reg, diff --git a/eskip/template.go b/eskip/template.go index 8551baa873..0c57c57668 100644 --- a/eskip/template.go +++ b/eskip/template.go @@ -35,8 +35,7 @@ type TemplateContext interface { // New parses a template string and returns a reusable *Template object. // The template string can contain named placeholders of the format: // -// Hello, ${who}! -// +// Hello, ${who}! func NewTemplate(template string) *Template { matches := placeholderRegexp.FindAllStringSubmatch(template, -1) placeholders := make([]string, len(matches)) diff --git a/filters/accesslog/control.go b/filters/accesslog/control.go index ce4e3860bf..cdfc1a1a74 100644 --- a/filters/accesslog/control.go +++ b/filters/accesslog/control.go @@ -58,8 +58,8 @@ type disableAccessLog struct{} // Optionally takes in response code prefixes as arguments. When provided, access log is disabled // only if response code matches one of the arguments. // -// disableAccessLog() or -// disableAccessLog(1, 20, 301) to disable logs for 1xx, 20x and 301 codes +// disableAccessLog() or +// disableAccessLog(1, 20, 301) to disable logs for 1xx, 20x and 301 codes func NewDisableAccessLog() filters.Spec { return &disableAccessLog{} } @@ -76,8 +76,8 @@ type enableAccessLog struct{} // Optionally takes in response code prefixes as arguments. When provided, access log is enabled // only if response code matches one of the arguments. // -// enableAccessLog() -// enableAccessLog(1, 20, 301) to enable logs for 1xx, 20x and 301 codes +// enableAccessLog() +// enableAccessLog(1, 20, 301) to enable logs for 1xx, 20x and 301 codes func NewEnableAccessLog() filters.Spec { return &enableAccessLog{} } diff --git a/filters/accesslog/disable.go b/filters/accesslog/disable.go index a1bf32543c..dbc59c9ce6 100644 --- a/filters/accesslog/disable.go +++ b/filters/accesslog/disable.go @@ -15,7 +15,8 @@ type accessLogDisabled struct { // NewAccessLogDisabled creates a filter spec for overriding the state of the AccessLogDisabled setting. (By default global setting is used.) // -// accessLogDisabled("false") +// accessLogDisabled("false") +// // Deprecated: use disableAccessLog or enableAccessLog func NewAccessLogDisabled() filters.Spec { return &accessLogDisabled{} diff --git a/filters/accesslog/doc.go b/filters/accesslog/doc.go index 99dc479bff..177330a3f8 100644 --- a/filters/accesslog/doc.go +++ b/filters/accesslog/doc.go @@ -1,7 +1,7 @@ /* Package accesslog provides request filters that give the ability to override AccessLogDisabled setting. -How It Works +# How It Works There are two filters that change the state of access log "disableAccessLog" and "enableAccessLog". If "disableAccessLog" is present access log entries for this route won't be produced even if global AccessLogDisabled is false. Otherwise, if @@ -10,8 +10,8 @@ is true. Usage - enableAccessLog() - disableAccessLog() + enableAccessLog() + disableAccessLog() Note: accessLogDisabled("true") filter is deprecated in favor of "disableAccessLog" and "enableAccessLog" */ diff --git a/filters/apiusagemonitoring/doc.go b/filters/apiusagemonitoring/doc.go index 03063a0ee0..73d824800f 100644 --- a/filters/apiusagemonitoring/doc.go +++ b/filters/apiusagemonitoring/doc.go @@ -1,8 +1,6 @@ /* - Package apiusagemonitoring provides filters gathering metrics around API calls - Feature switch & Dependencies This feature is by default not enabled. To enable it, the flag `-enable-api-usage-monitoring` @@ -14,8 +12,7 @@ flavour. Per instance: This will enable the API monitoring filter through Prometheus metrics. - -Configuration +# Configuration Due to its structured configuration, the filter accepts one parameter of type string containing a JSON object. @@ -23,8 +20,7 @@ containing a JSON object. Details and examples can be found at https://opensource.zalando.com/skipper/reference/filters/#apiUsageMonitoring (or in this project, under `docs/reference/filters.md`). - -Development Helpers +# Development Helpers The spec and filter log detailed operation information at `DEBUG` level. The -application-log-level=DEBUG switch is desirable for debugging usage of the filter. @@ -40,6 +36,5 @@ Command line example for executing locally: -metrics-flavour prometheus \ -histogram-metric-buckets=".01,.025,.05,.075,.1,.2,.3,.4,.5,.75,1,2,3,4,5,7,10,15,20,30,60,120,300,600" \ -application-log-level=DEBUG - */ package apiusagemonitoring diff --git a/filters/auth/basic.go b/filters/auth/basic.go index 515a6e09a1..957e1a67bd 100644 --- a/filters/auth/basic.go +++ b/filters/auth/basic.go @@ -26,7 +26,7 @@ func NewBasicAuth() *basicSpec { return &basicSpec{} } -//We do not touch response at all +// We do not touch response at all func (a *basic) Response(filters.FilterContext) {} // check basic auth diff --git a/filters/auth/doc.go b/filters/auth/doc.go index 82e9e5654f..9692ffcc70 100644 --- a/filters/auth/doc.go +++ b/filters/auth/doc.go @@ -1,7 +1,7 @@ /* Package auth provides authentication related filters. -Basic - Check Basic Authentication +# Basic - Check Basic Authentication The filter accepts two parameters, the first mandatory one is the path to the htpasswd file usually used with Apache or nginx. The second one is the optional realm name that will be displayed in the browser. Each incoming @@ -18,7 +18,7 @@ Embedding the filter in routes: -> basicAuth("/path/to/htpasswd", "My Website") -> "https://my-internal.example.org"; -OAuth2 - Check Bearer Tokens +# OAuth2 - Check Bearer Tokens The auth filter takes the incoming request, and tries to extract the Bearer token from the Authorization header. Then it validates against @@ -28,7 +28,7 @@ check if it has at least one of the predefined scopes. If any of the expectations are not met, it doesn't forward the request to the target endpoint, but returns with status 401. -OAuth2 - Provider Configuration - Tokeninfo +# OAuth2 - Provider Configuration - Tokeninfo To enable OAuth2 tokeninfo filters you have to set the CLI argument -oauth2-tokeninfo-url=. Scopes and key value pairs @@ -45,40 +45,40 @@ tokeninfo timeout is 2s. Example json output of the tokeninfo response could be: - { - "access_token": "", - "client_id": "ztoken", - "cn": "Jane Doe", - "expires_in": "300", - "grant_type": "password", - "realm": "/employees", - "scope": [ - "uid", - "foo-r", - "bar-w", - "qux-rw" - ], - "token_type": "Bearer", - "uid": "jdoe" - } - -OAuth2 - oauthTokeninfoAnyScope filter + { + "access_token": "", + "client_id": "ztoken", + "cn": "Jane Doe", + "expires_in": "300", + "grant_type": "password", + "realm": "/employees", + "scope": [ + "uid", + "foo-r", + "bar-w", + "qux-rw" + ], + "token_type": "Bearer", + "uid": "jdoe" + } + +# OAuth2 - oauthTokeninfoAnyScope filter The filter oauthTokeninfoAnyScope allows access if one of the scopes is satisfied by the request. - a: Path("/a") -> oauthTokeninfoAnyScope("uid") -> "https://internal.example.org/"; - b: Path("/b") -> oauthTokeninfoAnyScope("uid", "bar") -> "https://internal.example.org/"; + a: Path("/a") -> oauthTokeninfoAnyScope("uid") -> "https://internal.example.org/"; + b: Path("/b") -> oauthTokeninfoAnyScope("uid", "bar") -> "https://internal.example.org/"; -OAuth - oauthTokeninfoAllScope() filter +# OAuth - oauthTokeninfoAllScope() filter The filter oauthTokeninfoAllScope allows access if all of the scopes are satisfied by the request: - a: Path("/a") -> oauthTokeninfoAllScope("uid") -> "https://internal.example.org/"; - b: Path("/b") -> oauthTokeninfoAllScope("uid", "bar") -> "https://internal.example.org/"; + a: Path("/a") -> oauthTokeninfoAllScope("uid") -> "https://internal.example.org/"; + b: Path("/b") -> oauthTokeninfoAllScope("uid", "bar") -> "https://internal.example.org/"; -OAuth - oauthTokeninfoAnyKV() filter +# OAuth - oauthTokeninfoAnyKV() filter The filter oauthTokeninfoAnyKV allows access if the token information returned by OAuthTokeninfoURL has the given key and the given @@ -88,34 +88,34 @@ The following route has a filter definition, that one of the keys "uid" or "foo" has the value "jdoe" or "bar". Additionally the second will check if there is a "realm" "/employees": - a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; - b: Path("/") -> oauthTokeninfoAnyKV("realm","/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + b: Path("/") -> oauthTokeninfoAnyKV("realm","/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; The same as route `a` above, but you also allow "uid=mstar" to access: - a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "uid", "mstar") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "uid", "mstar") -> "https://internal.example.org/"; Example json output of this tokeninfo response: - { - "access_token": "", - "client_id": "ztoken", - "cn": "Jane Doe", - "expires_in": "300", - "grant_type": "password", - "realm": "/employees", - "scope": [ - "uid", - "foo-r", - "bar-w", - "qux-rw" - ], - "token_type": "Bearer", - "uid": "jdoe" - } - -OAuth - oauthTokeninfoAllKV() filter + { + "access_token": "", + "client_id": "ztoken", + "cn": "Jane Doe", + "expires_in": "300", + "grant_type": "password", + "realm": "/employees", + "scope": [ + "uid", + "foo-r", + "bar-w", + "qux-rw" + ], + "token_type": "Bearer", + "uid": "jdoe" + } + +# OAuth - oauthTokeninfoAllKV() filter The filter oauthTokeninfoAllKV allows access if the token information returned by OAuthTokeninfoURL has the given key and the given value. @@ -125,35 +125,34 @@ the key value pairs match. Here "uid" has to have the value "jdoe" and "foo" has to have the value "bar". Additionally the second will check if there is a "realm" "/employees": - a: Path("/") -> oauthTokeninfoAllKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; - b: Path("/") -> oauthTokeninfoAllKV("realm", "/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokeninfoAllKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + b: Path("/") -> oauthTokeninfoAllKV("realm", "/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; Example json output of this information response: - { - "access_token": "", - "client_id": "ztoken", - "cn": "John Doe", - "expires_in": "300", - "grant_type": "password", - "foo": "bar", - "realm": "/employees", - "scope": [ - "uid", - "foo-r", - "bar-w", - "qux-rw" - ], - "token_type": "Bearer", - "uid": "jdoe" - } - + { + "access_token": "", + "client_id": "ztoken", + "cn": "John Doe", + "expires_in": "300", + "grant_type": "password", + "foo": "bar", + "realm": "/employees", + "scope": [ + "uid", + "foo-r", + "bar-w", + "qux-rw" + ], + "token_type": "Bearer", + "uid": "jdoe" + } In case you are using any of the above 4 filters in your custom build, you can call the `Close()` method to close the `quit` channel and free up goroutines, to avoid goroutine leak -OAuth2 - Provider Configuration - Tokenintrospection +# OAuth2 - Provider Configuration - Tokenintrospection Provider configuration is dynamically done by https://tools.ietf.org/html/draft-ietf-oauth-discovery-06#section-5, @@ -164,58 +163,58 @@ filter configurations. Example response from the openid-configuration endpoint: - { - "issuer" : "https://issuer.example.com", - "token_endpoint" : "https://issuer.example.com/token", - "introspection_endpoint": "https://issuer.example.com/token/introspect", - "revocation_endpoint" : "https://issuer.example.com/token/revoke", - "authorization_endpoint": "https://issuer.example.com/login", - "userinfo_endpoint" : "https://issuer.example.com/userinfo", - "jwks_uri" : "https://issuer.example.com/token/certs", - "response_types_supported": [ - "code", - "token", - "id_token", - "code token", - "code id_token", - "token id_token", - "code token id_token", - "none" - ], - "subject_types_supported": [ - "public" - ], - "id_token_signing_alg_values_supported": [ - "RS256" - ], - "scopes_supported": [ - "openid", - "email", - "profile" - ], - "token_endpoint_auth_methods_supported": [ - "client_secret_post", - "client_secret_basic" - ], - "claims_supported": [ - "aud", - "email", - "email_verified", - "exp", - "family_name", - "given_name", - "iat", - "iss", - "locale", - "name", - "picture", - "sub" - ], - "code_challenge_methods_supported": [ - "plain", - "S256" - ] - } + { + "issuer" : "https://issuer.example.com", + "token_endpoint" : "https://issuer.example.com/token", + "introspection_endpoint": "https://issuer.example.com/token/introspect", + "revocation_endpoint" : "https://issuer.example.com/token/revoke", + "authorization_endpoint": "https://issuer.example.com/login", + "userinfo_endpoint" : "https://issuer.example.com/userinfo", + "jwks_uri" : "https://issuer.example.com/token/certs", + "response_types_supported": [ + "code", + "token", + "id_token", + "code token", + "code id_token", + "token id_token", + "code token id_token", + "none" + ], + "subject_types_supported": [ + "public" + ], + "id_token_signing_alg_values_supported": [ + "RS256" + ], + "scopes_supported": [ + "openid", + "email", + "profile" + ], + "token_endpoint_auth_methods_supported": [ + "client_secret_post", + "client_secret_basic" + ], + "claims_supported": [ + "aud", + "email", + "email_verified", + "exp", + "family_name", + "given_name", + "iat", + "iss", + "locale", + "name", + "picture", + "sub" + ], + "code_challenge_methods_supported": [ + "plain", + "S256" + ] + } Additionally, you can also pass CLI argument -oauth2-tokenintrospect-timeout= to control the @@ -226,30 +225,28 @@ All oauthTokenintrospection* filters will work on the tokenintrospect response. Example json output of the tokenintrospect response could be: - - { - "access_token": "", - "client_id": "ztoken", - "name": "Jane Doe", - "expires_in": "300", - "grant_type": "password", - "active": true, - "sub": "a-sub", - "iss": "https://issuer.example.com" - "realm": "/employees", - "claims": { - "uid": "jdoe", - "email": "jdoe@example.com" - }, - "scope": [ - "email", - "foo-r", - ], - "token_type": "Bearer", - } - - -OAuth2 - oauthTokenintrospectionAnyClaims filter + { + "access_token": "", + "client_id": "ztoken", + "name": "Jane Doe", + "expires_in": "300", + "grant_type": "password", + "active": true, + "sub": "a-sub", + "iss": "https://issuer.example.com" + "realm": "/employees", + "claims": { + "uid": "jdoe", + "email": "jdoe@example.com" + }, + "scope": [ + "email", + "foo-r", + ], + "token_type": "Bearer", + } + +# OAuth2 - oauthTokenintrospectionAnyClaims filter The filter oauthTokenintrospectionAnyClaims can be configured with claims validated from the openid-configuration `claims_supported` and @@ -263,9 +260,9 @@ in the filter. The following route has a filter definition, that will check if there is one of the following claims in the token: "uid" or "email": - a: Path("/") -> oauthTokenintrospectionAnyClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAnyClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; -OAuth2 - oauthTokenintrospectionAllClaims filter +# OAuth2 - oauthTokenintrospectionAllClaims filter The filter oauthTokenintrospectionAllClaims can be configured with claims validated from the openid-configuration `claims_supported` and @@ -279,9 +276,9 @@ in the filter. The following route has a filter definition, that will check if there all of the following claims in the token: "uid" and "email": - a: Path("/") -> oauthTokenintrospectionAllClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAllClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; -OAuth2 - oauthTokenintrospectionAnyKV filter +# OAuth2 - oauthTokenintrospectionAnyKV filter The filter oauthTokenintrospectionAnyKV will use the `introspection_endpoint` endpoint from the openid-configuration to @@ -295,13 +292,13 @@ The following route has a filter definition, that will check if there one of the following key-value pairs in the token: "uid=jdoe" or "iss=https://issuer.example.com": - a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; The same as route `a` above, but you also allow "uid=mstar" to access: - a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "uid", "mstar", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "uid", "mstar", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; -OAuth2 - oauthTokenintrospectionAllKV filter +# OAuth2 - oauthTokenintrospectionAllKV filter The filter oauthTokenintrospectionAllKV will use the `introspection_endpoint` endpoint from the openid-configuration to @@ -315,9 +312,9 @@ The following route has a filter definition, that will check if there are all of the following key-value pairs in the token: "uid=jdoe" or "iss=https://issuer.example.com": - a: Path("/") -> oauthTokenintrospectionAllKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAllKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; -OpenID - oauthOidcUserInfo filter +# OpenID - oauthOidcUserInfo filter The filter oauthOidcUserInfo is a filter for OAuth Implicit Flow authentication of users through OpenID Connect. It verifies that the token provided by the user upon authentication contains all the fields specified in the filter. @@ -325,7 +322,7 @@ It verifies that the token provided by the user upon authentication contains all a: Path("/") -> oauthOidcUserInfo("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", "http://callback.com/auth/provider/callback", "scope1 scope2", "field1 field2") -> "https://internal.example.org"; -OpenID - oauthOidcAnyClaims filter +# OpenID - oauthOidcAnyClaims filter The filter oauthOidcAnyClaims is a filter for OAuth Implicit Flow authentication scheme for users through OpenID Connect. It verifies that the token provided by the user upon authentication with the authentication provider contains at @@ -342,32 +339,31 @@ of the claims specified in the filter. a: Path("/") -> oauthOidcAllClaims("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", "http://callback.com/auth/provider/callback", "scope1 scope2", "claim1 claim2") -> "https://internal.example.org"; -OAuth - auditLog() filter +# OAuth - auditLog() filter The filter auditLog allows you to have an audit log for all requests. This filter should be always set, before checking with auth filters. To see only permitted access, you can set the auditLog() filter after the auth filter. - a: Path("/only-allowed-audit-log") -> oauthTokeninfoAnyScope("bar-w") -> auditLog() -> "https://internal.example.org/"; - b: Path("/all-access-requests-audit-log") -> auditLog() -> oauthTokeninfoAnyScope("foo-r") -> "https://internal.example.org/"; + a: Path("/only-allowed-audit-log") -> oauthTokeninfoAnyScope("bar-w") -> auditLog() -> "https://internal.example.org/"; + b: Path("/all-access-requests-audit-log") -> auditLog() -> oauthTokeninfoAnyScope("foo-r") -> "https://internal.example.org/"; -Webhook - webhook() filter +# Webhook - webhook() filter The filter webhook allows you to have a custom authentication and authorization endpoint for a route. Headers from the webhook response can be copyied into the continuing request by specifying the headers to copy as an optional second argument to the filter - a: Path("/only-allowed-by-webhook") -> webhook("https://custom-webhook.example.org/auth") -> "https://protected-backend.example.org/"; - b: Path("/copy-webhook-headers") -> webhook("https://custom-webhook.example.org/auth", "X-Copy-This-Header") -> "https://protected-backend.example.org/"; + a: Path("/only-allowed-by-webhook") -> webhook("https://custom-webhook.example.org/auth") -> "https://protected-backend.example.org/"; + b: Path("/copy-webhook-headers") -> webhook("https://custom-webhook.example.org/auth", "X-Copy-This-Header") -> "https://protected-backend.example.org/"; -Forward Token - forwardToken() filter +# Forward Token - forwardToken() filter The filter is used to forward the result of token introspection or token info to the backend. a: Path("/tokeninfo-protected") -> oauthTokeninfoAnyScope("uid") -> forwardToken("X-Tokeninfo-Forward") -> "https://internal.example.org"; b: Path("tokenintrospection-protected") -> oauthTokenintrospectionAnyKV("uid") -> forwardToken("X-Tokenintrospection-Forward") -> "http://internal.example.org"; - */ package auth diff --git a/filters/auth/jwt_validation.go b/filters/auth/jwt_validation.go index 664e62cef4..07a005cea8 100644 --- a/filters/auth/jwt_validation.go +++ b/filters/auth/jwt_validation.go @@ -33,7 +33,7 @@ var refreshRateLimit = time.Minute * 5 var refreshTimeout = time.Second * 10 var refreshUnknownKID = true -//the map of jwks keyfunctions stored per jwksUri +// the map of jwks keyfunctions stored per jwksUri var jwksMap map[string]*keyfunc.JWKS = make(map[string]*keyfunc.JWKS) func NewJwtValidationWithOptions(o TokenintrospectionOptions) filters.Spec { diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index d11828be7a..ed0b02b35c 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -71,9 +71,9 @@ type azureGraphGroups struct { // Filter parameter: // -// oauthOidc...("https://oidc-provider.example.com", "client_id", "client_secret", -// "http://target.example.com/subpath/callback", "email profile", "name email picture", -// "parameter=value", "X-Auth-Authorization:claims.email") +// oauthOidc...("https://oidc-provider.example.com", "client_id", "client_secret", +// "http://target.example.com/subpath/callback", "email profile", "name email picture", +// "parameter=value", "X-Auth-Authorization:claims.email") const ( paramIdpURL int = iota paramClientID @@ -170,12 +170,12 @@ func NewOAuthOidcAllClaims(secretsFile string, secretsRegistry secrets.Encrypter // CreateFilter creates an OpenID Connect authorization filter. // // first arg: a provider, for example "https://accounts.google.com", -// which has the path /.well-known/openid-configuration +// which has the path /.well-known/openid-configuration // // Example: // -// oauthOidcAllClaims("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", -// "http://callback.com/auth/provider/callback", "scope1 scope2", "claim1 claim2", "", "", "") -> "https://internal.example.org"; +// oauthOidcAllClaims("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", +// "http://callback.com/auth/provider/callback", "scope1 scope2", "claim1 claim2", "", "", "") -> "https://internal.example.org"; func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) { sargs, err := getStrings(args) if err != nil { @@ -949,17 +949,16 @@ func (f *tokenOidcFilter) getTokenWithExchange(state *OauthState, ctx filters.Fi // https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims // Example: // -// { -// "_claim_names": { -// "groups": "src1" -// }, -// "_claim_sources": { -// "src1": { -// "endpoint": "https://graph.windows.net/.../getMemberObjects" -// } -// } -// } -// +// { +// "_claim_names": { +// "groups": "src1" +// }, +// "_claim_sources": { +// "src1": { +// "endpoint": "https://graph.windows.net/.../getMemberObjects" +// } +// } +// } func (f *tokenOidcFilter) handleDistributedClaims(ctx context.Context, idToken *oidc.IDToken, oauth2Token *oauth2.Token, claimsMap map[string]interface{}) error { // https://github.com/coreos/go-oidc/issues/171#issuecomment-1044286153 var distClaims distributedClaims diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index e89bac2a53..44ecdfd014 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -134,7 +134,6 @@ func NewOAuthTokeninfoAnyKV(OAuthTokeninfoURL string, OAuthTokeninfoTimeout time // Use one of the base initializer functions as the first argument: // NewOAuthTokeninfoAllScope, NewOAuthTokeninfoAnyScope, // NewOAuthTokeninfoAllKV or NewOAuthTokeninfoAnyKV. -// func TokeninfoWithOptions(create func(string, time.Duration) filters.Spec, o TokeninfoOptions) filters.Spec { s := create(o.URL, o.Timeout) ts, ok := s.(*tokeninfoSpec) @@ -167,8 +166,7 @@ func (s *tokeninfoSpec) Name() string { // type. The shown example for checkOAuthTokeninfoAllScopes will grant // access only to tokens, that have scopes read-x and write-y: // -// s.CreateFilter("read-x", "write-y") -// +// s.CreateFilter("read-x", "write-y") func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) { sargs, err := getStrings(args) if err != nil { diff --git a/filters/auth/tokenintrospection.go b/filters/auth/tokenintrospection.go index cfd070923f..4b995f20a3 100644 --- a/filters/auth/tokenintrospection.go +++ b/filters/auth/tokenintrospection.go @@ -149,7 +149,7 @@ func NewOAuthTokenintrospectionAllClaims(timeout time.Duration) filters.Spec { return newOAuthTokenintrospectionFilter(checkOAuthTokenintrospectionAllClaims, timeout) } -//Secure Introspection Point +// Secure Introspection Point func NewSecureOAuthTokenintrospectionAnyKV(timeout time.Duration) filters.Spec { return newSecureOAuthTokenintrospectionFilter(checkSecureOAuthTokenintrospectionAnyKV, timeout) } diff --git a/filters/auth/webhook.go b/filters/auth/webhook.go index 3a7374652c..acfa052db0 100644 --- a/filters/auth/webhook.go +++ b/filters/auth/webhook.go @@ -58,9 +58,8 @@ func (*webhookSpec) Name() string { // string. The second, optional, argument is a comma separated list of // headers to forward from from webhook response. // -// s.CreateFilter("https://my-auth-service.example.org/auth") -// s.CreateFilter("https://my-auth-service.example.org/auth", "X-Auth-User,X-Auth-User-Roles") -// +// s.CreateFilter("https://my-auth-service.example.org/auth") +// s.CreateFilter("https://my-auth-service.example.org/auth", "X-Auth-User,X-Auth-User-Roles") func (ws *webhookSpec) CreateFilter(args []interface{}) (filters.Filter, error) { if l := len(args); l == 0 || l > 2 { return nil, filters.ErrInvalidFilterParameters diff --git a/filters/block/matcher.go b/filters/block/matcher.go index f07f0b6b99..b64050328b 100644 --- a/filters/block/matcher.go +++ b/filters/block/matcher.go @@ -47,7 +47,6 @@ const ( // When the matcher is closed, it doesn't read anymore from the input or return any // buffered data. If the input implements io.Closer, closing the matcher closes the // input, too. -// type matcher struct { once sync.Once input io.ReadCloser diff --git a/filters/builtin/builtin.go b/filters/builtin/builtin.go index 879170886c..53e1ad800c 100644 --- a/filters/builtin/builtin.go +++ b/filters/builtin/builtin.go @@ -203,6 +203,7 @@ func MakeRegistry() filters.Registry { tracing.NewBaggageToTagFilter(), tracing.NewTag(), tracing.NewStateBagToTag(), + //lint:ignore SA1019 due to backward compatibility accesslog.NewAccessLogDisabled(), accesslog.NewDisableAccessLog(), accesslog.NewEnableAccessLog(), diff --git a/filters/builtin/compress.go b/filters/builtin/compress.go index 6ab4aeae90..f28f281921 100644 --- a/filters/builtin/compress.go +++ b/filters/builtin/compress.go @@ -99,7 +99,7 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // // Example: // -// * -> compress() -> "https://www.example.org" +// r: * -> compress() -> "https://www.example.org"; // // The filter, when executed on the response path, checks if the response // entity can be compressed. To decide, it checks the Content-Encoding, the @@ -116,12 +116,12 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // types as filter arguments. When extending the defaults, the first argument needs // to be "...". E.g. to compress tiff in addition to the defaults: // -// * -> compress("...", "image/tiff") -> "https://www.example.org" +// r: * -> compress("...", "image/tiff") -> "https://www.example.org"; // // To reset the supported types, e.g. to compress only HTML, the "..." argument // needs to be omitted: // -// * -> compress("text/html") -> "https://www.example.org" +// r: * -> compress("text/html") -> "https://www.example.org"; // // It is possible to control the compression level, by setting it as the first // filter argument, in front of the MIME types. The default compression level is @@ -129,7 +129,7 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // 0 means no-compression, 1 means best-speed and 11 means best-compression. // Example: // -// * -> compress(9, "image/tiff") -> "https://www.example.org" +// r: * -> compress(9, "image/tiff") -> "https://www.example.org"; // // The filter also checks the incoming request, if it accepts the supported // encodings, explicitly stated in the Accept-Encoding header. The filter currently @@ -147,7 +147,6 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // encoding and sets the Vary: Accept-Encoding header, if missing. // // The compression happens in a streaming way, using only a small internal buffer. -// func NewCompress() filters.Spec { c, err := NewCompressWithOptions(CompressOptions{supportedEncodings}) if err != nil { diff --git a/filters/builtin/decompress.go b/filters/builtin/decompress.go index 2888b8cf28..adfd41ac46 100644 --- a/filters/builtin/decompress.go +++ b/filters/builtin/decompress.go @@ -172,7 +172,6 @@ func (e decodingError) Error() string { // decompression may fail after all the filters were processed. // // The filter does not need any parameters. -// func NewDecompress() filters.Spec { return decompress{} } diff --git a/filters/builtin/headertoquery.go b/filters/builtin/headertoquery.go index df68fc3208..945a647fc1 100644 --- a/filters/builtin/headertoquery.go +++ b/filters/builtin/headertoquery.go @@ -17,7 +17,7 @@ type ( // NewHeaderToQuery creates a filter which converts the headers // from the incoming Request to query params // -// headerToQuery("X-Foo-Header", "foo-query-param") +// headerToQuery("X-Foo-Header", "foo-query-param") // // The above filter will set the "foo-query-param" query param // to the value of "X-Foo-Header" header, to the request diff --git a/filters/builtin/inlinecontent.go b/filters/builtin/inlinecontent.go index ae43e32207..61d52fa380 100644 --- a/filters/builtin/inlinecontent.go +++ b/filters/builtin/inlinecontent.go @@ -18,18 +18,17 @@ type inlineContent struct { // // Usage of the filter: // -// * -> status(420) -> inlineContent("Enhance Your Calm") -> +// r: * -> status(420) -> inlineContent("Enhance Your Calm") -> ; // // Or: // -// * -> inlineContent("{\"foo\": 42}", "application/json") -> +// r: * -> inlineContent("{\"foo\": 42}", "application/json") -> ; // // It accepts two arguments: the content and the optional content type. // When the content type is not set, it tries to detect it using // http.DetectContentType. // // The filter shunts the request with status code 200. -// func NewInlineContent() filters.Spec { return &inlineContent{} } diff --git a/filters/builtin/inlinecontentifstatus.go b/filters/builtin/inlinecontentifstatus.go index 0103c4743e..26034257a3 100644 --- a/filters/builtin/inlinecontentifstatus.go +++ b/filters/builtin/inlinecontentifstatus.go @@ -19,13 +19,12 @@ type inlineContentIfStatus struct { // Creates a filter spec for the inlineContent() filter. // -// * -> inlineContentIfStatus(401, "{\"foo\": 42}", "application/json") -> "https://www.example.org" +// r: * -> inlineContentIfStatus(401, "{\"foo\": 42}", "application/json") -> "https://www.example.org"; // // It accepts three arguments: the statusCode code to match, the content and the optional content type. // When the content type is not set, it tries to detect it using http.DetectContentType. // // The filter replaces the response coming from the backend or the following filters. -// func NewInlineContentIfStatus() filters.Spec { return &inlineContentIfStatus{} } diff --git a/filters/builtin/query.go b/filters/builtin/query.go index 2b741429ee..9d6c6e7a92 100644 --- a/filters/builtin/query.go +++ b/filters/builtin/query.go @@ -21,7 +21,7 @@ type modQuery struct { // Returns a new dropQuery filter Spec, whose instances drop a corresponding // query parameter. // -// Instances expect the name string or template parameter, see eskip.Template.ApplyContext +// # Instances expect the name string or template parameter, see eskip.Template.ApplyContext // // Name: "dropQuery". func NewDropQuery() filters.Spec { return &modQuery{behavior: drop} } diff --git a/filters/builtin/querytoheader.go b/filters/builtin/querytoheader.go index 288786d861..81084d44fe 100644 --- a/filters/builtin/querytoheader.go +++ b/filters/builtin/querytoheader.go @@ -20,7 +20,7 @@ type ( // NewQueryToHeader creates a filter which converts query params // from the incoming Request to headers // -// queryToHeader("foo-query-param", "X-Foo-Header") +// queryToHeader("foo-query-param", "X-Foo-Header") // // The above filter will set the value of "X-Foo-Header" header to the // value of "foo-query-param" query param , to the request and will @@ -28,9 +28,8 @@ type ( // // The header value can be created by a formatstring with an optional third parameter // -// queryToHeader("foo-query-param", "X-Foo-Header", "prefix %s postfix") -// queryToHeader("access_token", "Authorization", "Bearer %s") -// +// queryToHeader("foo-query-param", "X-Foo-Header", "prefix %s postfix") +// queryToHeader("access_token", "Authorization", "Bearer %s") func NewQueryToHeader() filters.Spec { return &queryToHeaderSpec{} } diff --git a/filters/circuit/breaker.go b/filters/circuit/breaker.go index a167518874..a32459ca8c 100644 --- a/filters/circuit/breaker.go +++ b/filters/circuit/breaker.go @@ -57,7 +57,7 @@ func getDurationArg(a interface{}) (time.Duration, error) { // These filters set a breaker for the current route that open if the backend failures for the route reach a // value of N, where N is a mandatory argument of the filter: // -// consecutiveBreaker(15) +// consecutiveBreaker(15) // // The filter accepts the following optional arguments: timeout (milliseconds or duration string), // half-open-requests (integer), idle-ttl (milliseconds or duration string). @@ -70,7 +70,7 @@ func NewConsecutiveBreaker() filters.Spec { // These filters set a breaker for the current route that open if the backend failures for the route reach a // value of N within a window of the last M requests, where N and M are mandatory arguments of the filter: // -// rateBreaker(30, 300) +// rateBreaker(30, 300) // // The filter accepts the following optional arguments: timeout (milliseconds or duration string), // half-open-requests (integer), idle-ttl (milliseconds or duration string). diff --git a/filters/cookie/cookie.go b/filters/cookie/cookie.go index 72c6179111..c566b8029e 100644 --- a/filters/cookie/cookie.go +++ b/filters/cookie/cookie.go @@ -23,14 +23,14 @@ accessible from JS code running in web browsers. Examples: - requestCookie("test-session", "abc") + requestCookie("test-session", "abc") - responseCookie("test-session", "abc", 31536000) + responseCookie("test-session", "abc", 31536000) - responseCookie("test-session", "abc", 31536000, "change-only") + responseCookie("test-session", "abc", 31536000, "change-only") - // response cookie without HttpOnly: - jsCookie("test-session-info", "abc-debug", 31536000, "change-only") + // response cookie without HttpOnly: + jsCookie("test-session-info", "abc-debug", 31536000, "change-only") */ package cookie diff --git a/filters/cors/doc.go b/filters/cors/doc.go index b0db55e636..1f19493bba 100644 --- a/filters/cors/doc.go +++ b/filters/cors/doc.go @@ -1,7 +1,7 @@ /* Package cors implements the origin header for CORS. -How It Works +# How It Works The filter accepts an optional variadic list of acceptable origin parameters. If the input argument list is empty, the header will always be set to '*' which means any origin is acceptable. Otherwise the header is only set if the request contains diff --git a/filters/diag/diag.go b/filters/diag/diag.go index a6a81e88d8..7d8862759b 100644 --- a/filters/diag/diag.go +++ b/filters/diag/diag.go @@ -99,8 +99,7 @@ func kbps2bpms(kbps float64) float64 { // the byte length of the random response to be generated as an argument. // Eskip example: // -// * -> randomContent(2048) -> ; -// +// r: * -> randomContent(2048) -> ; func NewRandom() filters.Spec { return &random{} } // NewRepeat creates a filter specification whose filter instances can be used @@ -108,8 +107,7 @@ func NewRandom() filters.Spec { return &random{} } // the byte length of the response body to be generated as arguments. // Eskip example: // -// * -> repeatContent("x", 100) -> ; -// +// r: * -> repeatContent("x", 100) -> ; func NewRepeat() filters.Spec { return &repeat{} } // NewLatency creates a filter specification whose filter instances can be used @@ -117,73 +115,63 @@ func NewRepeat() filters.Spec { return &repeat{} } // as an argument. It always adds this value in addition to the natural latency, // and does not do any adjustments. Eskip example: // -// * -> latency(120) -> "https://www.example.org"; -// +// r: * -> latency(120) -> "https://www.example.org"; func NewLatency() filters.Spec { return &throttle{typ: latency} } // NewBandwidth creates a filter specification whose filter instances can be used // to maximize the bandwidth of the responses. It expects the bandwidth in // kbyte/sec as an argument. // -// * -> bandwidth(30) -> "https://www.example.org"; -// +// r: * -> bandwidth(30) -> "https://www.example.org"; func NewBandwidth() filters.Spec { return &throttle{typ: bandwidth} } // NewChunks creates a filter specification whose filter instances can be used // set artificial delays in between response chunks. It expects the byte length // of the chunks and the delay milliseconds. // -// * -> chunks(1024, "120ms") -> "https://www.example.org"; -// +// r: * -> chunks(1024, "120ms") -> "https://www.example.org"; func NewChunks() filters.Spec { return &throttle{typ: chunks} } // NewBackendLatency is the equivalent of NewLatency but for outgoing backend // requests. Eskip example: // -// * -> backendLatency(120) -> "https://www.example.org"; -// +// r: * -> backendLatency(120) -> "https://www.example.org"; func NewBackendLatency() filters.Spec { return &throttle{typ: backendLatency} } // NewBackendBandwidth is the equivalent of NewBandwidth but for outgoing backend // requests. Eskip example: // -// * -> backendBandwidth(30) -> "https://www.example.org"; -// +// r: * -> backendBandwidth(30) -> "https://www.example.org"; func NewBackendBandwidth() filters.Spec { return &throttle{typ: backendBandwidth} } // NewBackendChunks is the equivalent of NewChunks but for outgoing backend // requests. Eskip example: // -// * -> backendChunks(1024, 120) -> "https://www.example.org"; -// +// r: * -> backendChunks(1024, 120) -> "https://www.example.org"; func NewBackendChunks() filters.Spec { return &throttle{typ: backendChunks} } // NewUniformRequestLatency creates a latency for requests with uniform // distribution. Example delay around 1s with +/-120ms. // -// * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewUniformRequestLatency() filters.Spec { return &jitter{typ: uniformRequestDistribution} } // NewNormalRequestLatency creates a latency for requests with normal // distribution. Example delay around 1s with +/-120ms. // -// * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewNormalRequestLatency() filters.Spec { return &jitter{typ: normalRequestDistribution} } // NewUniformResponseLatency creates a latency for responses with uniform // distribution. Example delay around 1s with +/-120ms. // -// * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewUniformResponseLatency() filters.Spec { return &jitter{typ: uniformResponseDistribution} } // NewNormalResponseLatency creates a latency for responses with normal // distribution. Example delay around 1s with +/-120ms. // -// * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewNormalResponseLatency() filters.Spec { return &jitter{typ: normalResponseDistribution} } func (r *random) Name() string { return filters.RandomContentName } diff --git a/filters/doc.go b/filters/doc.go index 7ddba95ac0..47f3c54f73 100644 --- a/filters/doc.go +++ b/filters/doc.go @@ -20,8 +20,7 @@ Filters are used to augment both the inbound request's attributes before forwarding it to the route endpoint, and the outbound response's attributes before returning it to the original client. - -Filter Specification and Filter Instances +# Filter Specification and Filter Instances Filter implementations are based on filter specifications that provide a filter name and a 'factory' method to create filter instances. The filter name @@ -31,8 +30,7 @@ route. Filter instances are created while the route definitions are parsed and initialized, based on the specifications stored in the filter registry. Different filter instances can be created with different parameters. - -Filtering and FilterContext +# Filtering and FilterContext Once a route is identified during request processing, a context object is created that is unique to the request, holding the current request, the @@ -43,8 +41,7 @@ Each filter in a route is called twice, once for the request in the order of their position in the route definition, and once for the response in reverse order. - -Handling Requests with Filters +# Handling Requests with Filters Filters can handle the requests themselves, meaning that they can set the response status, headers and send any particular response body. In this case, diff --git a/filters/flowid/doc.go b/filters/flowid/doc.go index 238c93200e..5d8cdd94a3 100644 --- a/filters/flowid/doc.go +++ b/filters/flowid/doc.go @@ -6,8 +6,7 @@ Flow Ids let you correlate router logs for a given request against the upstream If your upstream application makes other requests to other services it can provide the same Flow ID value so that all of those logs can be correlated. - -How It Works +# How It Works Skipper generates a unique Flow Id for every HTTP request that it receives. The Flow ID is then passed to your upstream application as an HTTP header called X-Flow-Id. @@ -18,24 +17,24 @@ not a valid flow id it is ignored and a new flow id is generated, overwriting th Any other string used for this parameter is ignored and trigger the same, default, behavior - to ignore any existing X-Flow-Id header. -Generators +# Generators The Flow ID generation can follow any format. Skipper provides two Generator implementations - Standard and ULID. They offer different performance and options and you can choose which one you prefer. -Standard Flow IDs +# Standard Flow IDs The Standard Generator uses a base 64 alphabet and can be configured to generate flow IDs with length between 8 and 64 chars. It is very fast for small length FlowIDs and uses a system shared entropy source. It is safe for concurrent use. -ULID Flow IDs +# ULID Flow IDs The ULID Generator relies on the great work from https://github.com/alizain/ulid and https://github.com/oklog/ulid. It generates 26 char long Universally Unique Lexicographically Sortable IDs. It is very fast and it's also safe for concurrent use. -Programmatic Usage +# Programmatic Usage To create a specification of the FlowID filter you can either create the default specification, which uses the Standard Generator with a length of 16 chars as default or provide your own Generator instance. @@ -54,7 +53,7 @@ Custom spec with your own Generator implementation myCustomGenerator := newCustomGenerator(arg1, arg2) NewWithGenerator(myCustomGenerator) -Routing Usage +# Routing Usage The filter can be used with many different combinations of parameters. It can also be used without any parameter, using defaults @@ -72,9 +71,9 @@ Reuse existing flow id With a single string parameter with the value "reuse", the filter will accept an existing X-Flow-Id header, if it's present in the request. If it's invalid, a new one is generated and the header is overwritten. -Some Benchmarks +# Some Benchmarks -Built-In Flow ID Generator +# Built-In Flow ID Generator To decide upon which hashing mechanism to use we tested some versions of UUID v1 - v4 and some other implementations. The results are as follow: @@ -134,11 +133,9 @@ following results: BenchmarkFlowIdStandardGeneratorInParallel/32-8 2000000 918 ns/op 64 B/op 2 allocs/op BenchmarkFlowIdStandardGeneratorInParallel/64-8 1000000 1576 ns/op 128 B/op 2 allocs/op - ULID Flow ID Generator BenchmarkFlowIdULIDGenerator/Std-8 10000000 194 ns/op 48 B/op 2 allocs/op BenchmarkFlowIdULIDGeneratorInParallel-8 5000000 380 ns/op 48 B/op 2 allocs/op - */ package flowid diff --git a/filters/log/log.go b/filters/log/log.go index 063ea8fb43..59995b5613 100644 --- a/filters/log/log.go +++ b/filters/log/log.go @@ -111,7 +111,7 @@ func (tb *teeBody) Write(b []byte) (int, error) { // maxAuditBody attribute to limit the size of the log. It will use // os.Stderr as writer for the output of the log entries. // -// spec := NewAuditLog(1024) +// spec := NewAuditLog(1024) func NewAuditLog(maxAuditBody int) filters.Spec { return &auditLog{ writer: os.Stderr, diff --git a/filters/ratelimit/leakybucket.go b/filters/ratelimit/leakybucket.go index e674899aeb..fe1278d4e3 100644 --- a/filters/ratelimit/leakybucket.go +++ b/filters/ratelimit/leakybucket.go @@ -35,8 +35,7 @@ type leakyBucketFilter struct { // // Example to allow each unique Authorization header once in five seconds: // -// clusterLeakyBucketRatelimit("auth-${request.header.Authorization}", 1, "5s", 2, 1) -// +// clusterLeakyBucketRatelimit("auth-${request.header.Authorization}", 1, "5s", 2, 1) func NewClusterLeakyBucketRatelimit(registry *ratelimit.Registry) filters.Spec { return &leakyBucketSpec{ create: func(capacity int, emission time.Duration) leakyBucket { diff --git a/filters/ratelimit/ratelimit.go b/filters/ratelimit/ratelimit.go index e5c9bcb979..e47549972b 100644 --- a/filters/ratelimit/ratelimit.go +++ b/filters/ratelimit/ratelimit.go @@ -77,15 +77,15 @@ func NewLocalRatelimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> clientRatelimit(20, "1m") -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> clientRatelimit(20, "1m") +// -> "https://foo.backend.net"; // // Example rate limit per Authorization Header: // -// login: Path("/login") -// -> clientRatelimit(3, "1m", "Authorization") -// -> "https://login.backend.net"; +// login: Path("/login") +// -> clientRatelimit(3, "1m", "Authorization") +// -> "https://login.backend.net"; func NewClientRatelimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.ClientRatelimit, provider: provider, filterName: filters.ClientRatelimitName} } @@ -96,18 +96,17 @@ func NewClientRatelimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> ratelimit(20, "1s") -// -> "https://foo.backend.net"; -// +// backendHealthcheck: Path("/healthcheck") +// -> ratelimit(20, "1s") +// -> "https://foo.backend.net"; // // Optionally a custom response status code can be provided as an argument (default is 429). // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> ratelimit(20, "1s", 503) -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> ratelimit(20, "1s", 503) +// -> "https://foo.backend.net"; func NewRatelimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.ServiceRatelimit, provider: provider, filterName: filters.RatelimitName} } @@ -119,18 +118,17 @@ func NewRatelimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> clusterRatelimit("groupA", 200, "1m") -// -> "https://foo.backend.net"; -// +// backendHealthcheck: Path("/healthcheck") +// -> clusterRatelimit("groupA", 200, "1m") +// -> "https://foo.backend.net"; // // Optionally a custom response status code can be provided as an argument (default is 429). // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> clusterRatelimit("groupA", 200, "1m", 503) -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> clusterRatelimit("groupA", 200, "1m", 503) +// -> "https://foo.backend.net"; func NewClusterRateLimit(provider RatelimitProvider) filters.Spec { return NewShardedClusterRateLimit(provider, 1) } @@ -152,9 +150,9 @@ func NewShardedClusterRateLimit(provider RatelimitProvider, maxGroupShards int) // // Example: // -// backendHealthcheck: Path("/login") -// -> clusterClientRatelimit("groupB", 20, "1h") -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/login") +// -> clusterClientRatelimit("groupB", 20, "1h") +// -> "https://foo.backend.net"; // // The above example would limit access to "/login" if, the client did // more than 20 requests within the last hour to this route across all @@ -166,10 +164,9 @@ func NewShardedClusterRateLimit(provider RatelimitProvider, maxGroupShards int) // // Example: // -// backendHealthcheck: Path("/login") -// -> clusterClientRatelimit("groupC", 20, "1h", "Authorization") -// -> "https://foo.backend.net"; -// +// backendHealthcheck: Path("/login") +// -> clusterClientRatelimit("groupC", 20, "1h", "Authorization") +// -> "https://foo.backend.net"; func NewClusterClientRateLimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.ClusterClientRatelimit, provider: provider, filterName: filters.ClusterClientRatelimitName} } @@ -178,9 +175,9 @@ func NewClusterClientRateLimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> disableRatelimit() -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> disableRatelimit() +// -> "https://foo.backend.net"; func NewDisableRatelimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.DisableRatelimit, provider: provider, filterName: filters.DisableRatelimitName} } diff --git a/filters/rfc/rfc.go b/filters/rfc/rfc.go index 501c4a6e99..3450c0b2ea 100644 --- a/filters/rfc/rfc.go +++ b/filters/rfc/rfc.go @@ -21,7 +21,6 @@ type path struct{} // that they are encoded in the raw path. // // See also the PatchPath documentation in the rfc package. -// func NewPath() filters.Spec { return path{} } func (p path) Name() string { return filters.RfcPathName } @@ -39,7 +38,6 @@ type host struct{} // removes a trailing dot in the host header. // // See also the PatchHost documentation in the rfc package. -// func NewHost() filters.Spec { return host{} } func (host) Name() string { return filters.RfcHostName } diff --git a/filters/scheduler/doc.go b/filters/scheduler/doc.go index 4ce4967b07..18c92e51ea 100644 --- a/filters/scheduler/doc.go +++ b/filters/scheduler/doc.go @@ -33,5 +33,4 @@ // // - 502, if it can not get a request from data structure fast enough // - 503, if the data structure is full and reached its boundary -// package scheduler diff --git a/filters/scheduler/lifo.go b/filters/scheduler/lifo.go index 59de5290e3..dadca42c5c 100644 --- a/filters/scheduler/lifo.go +++ b/filters/scheduler/lifo.go @@ -163,7 +163,6 @@ func (*lifoGroupSpec) Name() string { return filters.LifoGroupName } // one of them will be used as the source for the applied settings, if there // is accidentally a difference between the settings in the same group, a // warning will be logged. -// func (*lifoGroupSpec) CreateFilter(args []interface{}) (filters.Filter, error) { if len(args) < 1 || len(args) > 4 { return nil, filters.ErrInvalidFilterParameters diff --git a/filters/sed/doc.go b/filters/sed/doc.go index a1323407aa..7d20ca135b 100644 --- a/filters/sed/doc.go +++ b/filters/sed/doc.go @@ -1,7 +1,7 @@ /* Package sed provides stream editor filters for request and response payload. -Filter sed +# Filter sed Example: @@ -31,7 +31,7 @@ The filter uses the go regular expression implementation: https://github.com/google/re2/wiki/Syntax . Due to the streaming nature, matches with zero length are ignored. -Memory handling and limitations +# Memory handling and limitations In order to avoid unbound buffering of unprocessed data, the sed* filters need to apply some limitations. Some patterns, e.g. `.*` would allow to match the complete @@ -55,7 +55,7 @@ use the delimited variant of the filters, e.g. for line based editing. If the max buffer handling is set to "abort", then the stream editing is stopped and the rest of the payload is dropped. -Filter sedDelim +# Filter sedDelim Like sed(), but it expects an additional argument, before the optional max buffer size argument, that is used to delimit chunks to be processed at once. The pattern @@ -64,13 +64,13 @@ delimiter, and matches across the chunk boundaries are not considered. editorRoute: * -> sedDelim("foo", "bar", "\n") -> "https://www.example.org"; -Filter sedRequest +# Filter sedRequest Like sed(), but for the request content. editorRoute: * -> sedRequest("foo", "bar") -> "https://www.example.org"; -Filter sedRequestDelim +# Filter sedRequestDelim Like sedDelim(), but for the request content. diff --git a/filters/sed/editor.go b/filters/sed/editor.go index bde6f04b31..bd5c525216 100644 --- a/filters/sed/editor.go +++ b/filters/sed/editor.go @@ -50,7 +50,6 @@ const ( // When the editor is closed, it doesn't read anymore from the input or return any // buffered data. If the input implements io.Closer, closing the editor closes the // input, too. -// type editor struct { // init: input io.Reader diff --git a/filters/serve/serve.go b/filters/serve/serve.go index 0d3830e7e6..cac6ce75b4 100644 --- a/filters/serve/serve.go +++ b/filters/serve/serve.go @@ -28,12 +28,11 @@ type pipedResponse struct { // // Example, a simple file server: // -// var handler = http.StripPrefix(webRoot, http.FileServer(http.Dir(root))) -// -// func (f *myFilter) Request(ctx filters.FilterContext) { -// serve.ServeHTTP(ctx, handler) -// } +// var handler = http.StripPrefix(webRoot, http.FileServer(http.Dir(root))) // +// func (f *myFilter) Request(ctx filters.FilterContext) { +// serve.ServeHTTP(ctx, handler) +// } func ServeHTTP(ctx filters.FilterContext, h http.Handler) { rsp := &http.Response{Header: make(http.Header)} r, w := io.Pipe() diff --git a/filters/shedder/admission.go b/filters/shedder/admission.go index af14d48ea5..37e328df9e 100644 --- a/filters/shedder/admission.go +++ b/filters/shedder/admission.go @@ -214,14 +214,16 @@ func (*AdmissionControlSpec) Name() string { return filters.AdmissionControlName // CreateFilter creates a new admissionControl filter with passed configuration: // -// admissionControl(metricSuffix, mode, d, windowSize, minRps, successThreshold, maxRejectProbability, exponent) -// admissionControl("$app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) +// admissionControl(metricSuffix, mode, d, windowSize, minRps, successThreshold, maxRejectProbability, exponent) +// admissionControl("$app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) // // metricSuffix is the suffix key to expose reject counter, should be unique by filter instance // mode is one of "active", "inactive", "logInactive" -// active will reject traffic -// inactive will never reject traffic -// logInactive will not reject traffic, but log to debug filter settings +// +// active will reject traffic +// inactive will never reject traffic +// logInactive will not reject traffic, but log to debug filter settings +// // windowSize is within [minWindowSize, maxWindowSize] // minRps threshold that needs to be reached such that the filter will apply // successThreshold is within (0,1] and sets the lowest request success rate at which the filter will not reject requests. diff --git a/filters/tee/doc.go b/filters/tee/doc.go index ccfb66fe22..8ff198f8fd 100644 --- a/filters/tee/doc.go +++ b/filters/tee/doc.go @@ -6,7 +6,7 @@ backend of the route. Example: - * -> tee("https://audit-logging.example.org") -> "https://foo.example.org" + r: * -> tee("https://audit-logging.example.org") -> "https://foo.example.org"; This will send an identical request for foo.example.org to audit-logging.example.org. Another use case could be using it for benchmarking a new backend with some real traffic diff --git a/filters/tee/tee.go b/filters/tee/tee.go index 53fac8c18b..9d98a13557 100644 --- a/filters/tee/tee.go +++ b/filters/tee/tee.go @@ -113,7 +113,6 @@ func NewTeeNoFollow() filters.Spec { // Returns a new tee filter Spec, whose instances execute the exact same Request against a shadow backend with given // options. Available options are nofollow and Timeout for http client. // parameters: shadow backend url, optional - the path(as a regexp) to match and the replacement string. -// func WithOptions(o Options) filters.Spec { return &teeSpec{options: o} } @@ -141,7 +140,7 @@ func (tt *teeTie) Read(b []byte) (int, error) { func (tt *teeTie) Close() error { return nil } -//We do not touch response at all +// We do not touch response at all func (r *tee) Response(filters.FilterContext) {} // Request is copied and then modified to adopt changes in new backend diff --git a/filters/tracing/spanname.go b/filters/tracing/spanname.go index 5c56fbd5a3..5c7db0f217 100644 --- a/filters/tracing/spanname.go +++ b/filters/tracing/spanname.go @@ -23,8 +23,7 @@ type filter struct { // NewSpanName creates a filter spec for setting the name of the outgoing span. (By default "proxy".) // -// tracingSpanName("example-operation") -// +// tracingSpanName("example-operation") func NewSpanName() filters.Spec { return &spec{} } diff --git a/loadbalancer/doc.go b/loadbalancer/doc.go index cd63e2bc32..4c3387afd3 100644 --- a/loadbalancer/doc.go +++ b/loadbalancer/doc.go @@ -3,21 +3,21 @@ Package loadbalancer implements load balancer algorithms that are applied by the roundRobin Algorithm - The roundRobin algorithm does proxy requests round robin to - backend endpoints. It has a mutex to update the index and will - start at a random index + The roundRobin algorithm does proxy requests round robin to + backend endpoints. It has a mutex to update the index and will + start at a random index random Algorithm - The random algorithm does proxy requests to random backend - endpoints. + The random algorithm does proxy requests to random backend + endpoints. consistentHash Algorithm - The consistentHash algorithm choose backend endpoints by hashing - client data with hash function fnv.New32. The client data is the - client IP, which will be looked up from X-Forwarded-For header - with remote IP as the fallback. + The consistentHash algorithm choose backend endpoints by hashing + client data with hash function fnv.New32. The client data is the + client IP, which will be looked up from X-Forwarded-For header + with remote IP as the fallback. powerOfRandomNChoices Algorithm @@ -31,12 +31,10 @@ applications that require a certain amount of warm-up time. Eskip example: - - r1: * -> ; - r2: * -> ; - r3: * -> ; - r4: * -> ; - + r1: * -> ; + r2: * -> ; + r3: * -> ; + r4: * -> ; Package loadbalancer also implements health checking of pool members for a group of routes, if backend calls are reported to the loadbalancer. @@ -45,19 +43,19 @@ Based on https://landing.google.com/sre/book/chapters/load-balancing-datacenter. Healthy (healthy) - The backend task has initialized correctly and is processing - requests. + The backend task has initialized correctly and is processing + requests. Refusing connections (dead) - The backend task is unresponsive. This can happen because the - task is starting up or shutting down, or because the backend is - in an abnormal state (though it would be rare for a backend to - stop listening on its port if it is not shutting down). + The backend task is unresponsive. This can happen because the + task is starting up or shutting down, or because the backend is + in an abnormal state (though it would be rare for a backend to + stop listening on its port if it is not shutting down). Lame duck (unhealthy) - The backend task is listening on its port and can serve, but is - explicitly asking clients to stop sending requests. + The backend task is listening on its port and can serve, but is + explicitly asking clients to stop sending requests. */ package loadbalancer diff --git a/logging/doc.go b/logging/doc.go index 76f5cbae85..0c88a83eb3 100644 --- a/logging/doc.go +++ b/logging/doc.go @@ -2,7 +2,7 @@ Package logging implements application log instrumentation and Apache combined access log. -Application Log +# Application Log The application log uses the logrus package: @@ -11,11 +11,11 @@ https://github.com/sirupsen/logrus To send messages to the application log, import this package and use its methods. Example: - import log "github.com/sirupsen/logrus" + import log "github.com/sirupsen/logrus" - func doSomething() { - log.Errorf("nothing to do") - } + func doSomething() { + log.Errorf("nothing to do") + } During startup initialization, it is possible to redirect the log output from the default /dev/stderr to another file, and to set a common @@ -23,7 +23,7 @@ prefix for each log entry. Setting the prefix may be a good idea when the access log is enabled and its output is the same as the one of the application log, to make it easier to split the output for diagnostics. -Access Log +# Access Log The access log prints HTTP access information in the Apache combined access log format. To output entries, use the logging.Access method. @@ -39,7 +39,7 @@ so filters can add more data to the access log files. When using the feature, an contained in a map[string]interface{} in the StateBag's key will be passed to the logger. This is specially useful when more request/response information is needed when logging. -Output Files +# Output Files To set a custom file output for the application log or the access log is currently not recommended in production environment, because neither the diff --git a/metrics/doc.go b/metrics/doc.go index 6d9ee4045b..f814cb2cd8 100644 --- a/metrics/doc.go +++ b/metrics/doc.go @@ -19,7 +19,7 @@ The collected metrics include detailed information about Skipper's relevant proc looking up routes, filters (aggregate and individual), backend communication and forwarding the response to the client. -Options +# Options To enable metrics, it needs to be initialized with a Listener address. In this case, Skipper will start an additional http listener, where the current metrics values can be downloaded. @@ -30,7 +30,7 @@ metrics and other systems if you aggregate them later in some monitoring system. You can also enable some Go garbage collector and runtime metrics using EnableDebugGcMetrics and EnableRuntimeMetrics, respectively. -REST API +# REST API This listener accepts GET requests on the /metrics endpoint like any other REST api. A request to "/metrics" should return a JSON response including all the collected metrics if CodaHale format is used, or in Plain text if Prometheus diff --git a/net/net.go b/net/net.go index 7af882a810..d6af53b04c 100644 --- a/net/net.go +++ b/net/net.go @@ -30,7 +30,7 @@ func parse(addr string) net.IP { // // Example: // -// X-Forwarded-For: client, proxy1, proxy2 +// X-Forwarded-For: client, proxy1, proxy2 func RemoteHost(r *http.Request) net.IP { ffs := r.Header.Get("X-Forwarded-For") ff, _, _ := strings.Cut(ffs, ",") @@ -48,7 +48,7 @@ func RemoteHost(r *http.Request) net.IP { // // Example: // -// X-Forwarded-For: ip-address-1, ip-address-2, client-ip-address +// X-Forwarded-For: ip-address-1, ip-address-2, client-ip-address func RemoteHostFromLast(r *http.Request) net.IP { ffs := r.Header.Get("X-Forwarded-For") ffa := strings.Split(ffs, ",") diff --git a/oauth/oauth.go b/oauth/oauth.go index 536c8aeb82..a190b236a8 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -22,11 +22,11 @@ file system under the directory passed in with the credentialsDir argument. The structure of the client credentials document: - {"client_id": "testclientid", "client_secret": "testsecret"} + {"client_id": "testclientid", "client_secret": "testsecret"} The structure of the user credentials document: - {"application_username": "testusername", "application_password": "testpassword"} + {"application_username": "testusername", "application_password": "testpassword"} The GetToken method ignores the expiration date and makes a new request to the OAuth2 service on every call, so storing the token, if necessary, is the diff --git a/predicates/auth/jwt.go b/predicates/auth/jwt.go index 06e85c0c8a..8ede69dd98 100644 --- a/predicates/auth/jwt.go +++ b/predicates/auth/jwt.go @@ -7,12 +7,12 @@ part of a JWT token, for example based on the issuer. Examples: - // one key value pair has to match - example1: JWTPayloadAnyKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") - -> "http://example.org/"; - // all key value pairs have to match - example2: * && JWTPayloadAllKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") - -> "http://example.org/"; + // one key value pair has to match + example1: JWTPayloadAnyKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") + -> "http://example.org/"; + // all key value pairs have to match + example2: * && JWTPayloadAllKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") + -> "http://example.org/"; */ package auth diff --git a/predicates/cookie/cookie.go b/predicates/cookie/cookie.go index 734077d43e..281e4739ce 100644 --- a/predicates/cookie/cookie.go +++ b/predicates/cookie/cookie.go @@ -31,8 +31,7 @@ type ( // // Eskip example: // -// Cookie("tcial", /^enabled$/) -> "https://www.example.org"; -// +// Cookie("tcial", /^enabled$/) -> "https://www.example.org"; func New() routing.PredicateSpec { return &spec{} } func (s *spec) Name() string { return predicates.CookieName } diff --git a/predicates/forwarded/forwarded.go b/predicates/forwarded/forwarded.go index 796e3c0f8e..6c1489c340 100644 --- a/predicates/forwarded/forwarded.go +++ b/predicates/forwarded/forwarded.go @@ -7,14 +7,14 @@ https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded Examples: - // only match requests to "example.com" - example1: ForwardedHost("example.com") -> "http://example.org"; + // only match requests to "example.com" + example1: ForwardedHost("example.com") -> "http://example.org"; - // only match requests to http - example2: ForwardedProtocol("http") -> "http://example.org"; + // only match requests to http + example2: ForwardedProtocol("http") -> "http://example.org"; - // only match requests to https - example3: ForwardedProtocol("https") -> "http://example.org"; + // only match requests to https + example3: ForwardedProtocol("https") -> "http://example.org"; */ package forwarded diff --git a/predicates/interval/interval.go b/predicates/interval/interval.go index 96b3abc53d..a4371d60a4 100644 --- a/predicates/interval/interval.go +++ b/predicates/interval/interval.go @@ -4,10 +4,10 @@ only during some period of time. Package includes three predicates: Between, Before and After. All predicates can be created using the date represented as: -- a string in RFC3339 format (see https://golang.org/pkg/time/#pkg-constants) -- a string in RFC3339 format without numeric timezone offset and a location name (see https://golang.org/pkg/time/#LoadLocation) -- an int64 or float64 number corresponding to the given Unix time in seconds since January 1, 1970 UTC. - float64 number will be converted into int64 number. + - a string in RFC3339 format (see https://golang.org/pkg/time/#pkg-constants) + - a string in RFC3339 format without numeric timezone offset and a location name (see https://golang.org/pkg/time/#LoadLocation) + - an int64 or float64 number corresponding to the given Unix time in seconds since January 1, 1970 UTC. + float64 number will be converted into int64 number. Between predicate matches only if current date is inside the specified range of dates. Between predicate requires two dates to be constructed. @@ -34,7 +34,6 @@ Examples: example7: Path("/zalando") && Between("2021-02-18T00:00:00", "2021-02-18T01:00:00", "Europe/Berlin") -> "https://www.zalando.de"; example8: Path("/zalando") && Before("2021-02-18T00:00:00", "Europe/Berlin") -> "https://www.zalando.de"; example9: Path("/zalando") && After("2021-02-18T00:00:00", "Europe/Berlin") -> "https://www.zalando.de"; - */ package interval diff --git a/predicates/methods/methods.go b/predicates/methods/methods.go index 18a4914742..13df4d4797 100644 --- a/predicates/methods/methods.go +++ b/predicates/methods/methods.go @@ -1,18 +1,16 @@ /* - Package methods implements a custom predicate to match routes based on the http method in request -It supports multiple http methods, with case insensitive input +# It supports multiple http methods, with case insensitive input Examples: - // matches GET request - example1: Methods("GET") -> "http://example.org"; - - // matches GET or POST request - example1: Methods("GET", "post") -> "http://example.org"; + // matches GET request + example1: Methods("GET") -> "http://example.org"; + // matches GET or POST request + example1: Methods("GET", "post") -> "http://example.org"; */ package methods diff --git a/predicates/query/query.go b/predicates/query/query.go index ec047f56ad..c02c528e2b 100644 --- a/predicates/query/query.go +++ b/predicates/query/query.go @@ -7,22 +7,21 @@ query params value match to a given regular exp Examples: - // Checking existence of a query param - // matches http://example.org?bb=a&query=withvalue - example1: QueryParam("query") -> "http://example.org"; + // Checking existence of a query param + // matches http://example.org?bb=a&query=withvalue + example1: QueryParam("query") -> "http://example.org"; - // Even a query param without a value - // matches http://example.org?bb=a&query= - example1: QueryParam("query") -> "http://example.org"; + // Even a query param without a value + // matches http://example.org?bb=a&query= + example1: QueryParam("query") -> "http://example.org"; - // matches with regexp - // matches http://example.org?bb=a&query=example - example1: QueryParam("query", "^example$") -> "http://example.org"; - - // matches with regexp and multiple values of query param - // matches http://example.org?bb=a&query=testing&query=example - example1: QueryParam("query", "^example$") -> "http://example.org"; + // matches with regexp + // matches http://example.org?bb=a&query=example + example1: QueryParam("query", "^example$") -> "http://example.org"; + // matches with regexp and multiple values of query param + // matches http://example.org?bb=a&query=testing&query=example + example1: QueryParam("query", "^example$") -> "http://example.org"; */ package query diff --git a/predicates/source/source.go b/predicates/source/source.go index cff90e49df..736fe06208 100644 --- a/predicates/source/source.go +++ b/predicates/source/source.go @@ -25,17 +25,17 @@ the X-Forwarded-For header and SourceFromLast() as last entry. Examples: - // only match requests from 1.2.3.4 - example1: Source("1.2.3.4") -> "http://example.org"; + // only match requests from 1.2.3.4 + example1: Source("1.2.3.4") -> "http://example.org"; - // only match requests from 1.2.3.0 - 1.2.3.255 - example2: Source("1.2.3.0/24") -> "http://example.org"; + // only match requests from 1.2.3.0 - 1.2.3.255 + example2: Source("1.2.3.0/24") -> "http://example.org"; - // only match requests from 1.2.3.4 and the 2.2.2.0/24 network - example3: Source("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; + // only match requests from 1.2.3.4 and the 2.2.2.0/24 network + example3: Source("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; - // same as example3, only match requests from 1.2.3.4 and the 2.2.2.0/24 network - example4: SourceFromLast("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; + // same as example3, only match requests from 1.2.3.4 and the 2.2.2.0/24 network + example4: SourceFromLast("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; */ package source diff --git a/predicates/traffic/traffic.go b/predicates/traffic/traffic.go index 8bfb1a66ab..16f90ef826 100644 --- a/predicates/traffic/traffic.go +++ b/predicates/traffic/traffic.go @@ -24,48 +24,47 @@ backend service. The below example, shows a possible eskip document used for green-blue deployments of APIS, which usually don't require stickiness: - // hit by 10% percent chance - v2: - Traffic(.1) -> - "https://api-test-green"; + // hit by 10% percent chance + v2: + Traffic(.1) -> + "https://api-test-green"; - // hit by remaining chance - v1: - "https://api-test-blue"; + // hit by remaining chance + v1: + "https://api-test-blue"; The below example, shows a possible eskip document with two, independent traffic controlled route sets, which uses session stickiness: - // hit by 5% percent chance - cartTest: - Traffic(.05, "cart-test", "test") && Path("/cart") -> - responseCookie("cart-test", "test") -> - "https://cart-test"; - - // hit by remaining chance - cart: - Path("/cart") -> - responseCookie("cart-test", "default") -> - "https://cart"; - - // hit by 15% percent chance - catalogTestA: - Traffic(.15, "catalog-test", "A") -> - responseCookie("catalog-test", "A") -> - "https://catalog-test-a"; - - // hit by 30% percent chance - catalogTestB: - Traffic(.3, "catalog-test", "B") -> - responseCookie("catalog-test", "B") -> - "https://catalog-test-b"; - - // hit by remaining chance - catalog: - * -> - responseCookie("catalog-test", "default") -> - "https://catalog"; - + // hit by 5% percent chance + cartTest: + Traffic(.05, "cart-test", "test") && Path("/cart") -> + responseCookie("cart-test", "test") -> + "https://cart-test"; + + // hit by remaining chance + cart: + Path("/cart") -> + responseCookie("cart-test", "default") -> + "https://cart"; + + // hit by 15% percent chance + catalogTestA: + Traffic(.15, "catalog-test", "A") -> + responseCookie("catalog-test", "A") -> + "https://catalog-test-a"; + + // hit by 30% percent chance + catalogTestB: + Traffic(.3, "catalog-test", "B") -> + responseCookie("catalog-test", "B") -> + "https://catalog-test-b"; + + // hit by remaining chance + catalog: + * -> + responseCookie("catalog-test", "default") -> + "https://catalog"; */ package traffic diff --git a/proxy/doc.go b/proxy/doc.go index 604fde2ab9..9832eb53e2 100644 --- a/proxy/doc.go +++ b/proxy/doc.go @@ -23,8 +23,7 @@ forwarding it to the route endpoint. It may also mean to handle the request internally if it is a 'shunt' route or to continue routing further in case of 'loopback'. - -Proxy Mechanism +# Proxy Mechanism 1. route matching: @@ -33,7 +32,6 @@ in skipper/routing. The result may be a route, which will be used for forwarding or handling the request, or nil, in which case the proxy responds with a configured http status code (defaults to 404). - 2. upstream request augmentation: In case of a matched route, the request handling method of all filters @@ -49,7 +47,6 @@ will prevent the request from reaching the route endpoint. The filters that are defined in the route after the one that broke the chain will never handle the request. - 3.a upstream request: The incoming and augmented request is mapped to an outgoing request and @@ -57,7 +54,6 @@ executed, addressing the endpoint defined by the current route. If a filter chain was broken by some filter this step is skipped. - 3.b shunt: In case the route is a 'shunt', an empty response is created with @@ -74,7 +70,6 @@ limit is reached. In case of a `dynamic` route, the final target must be defined in a filter. - 4. downstream response augmentation: The response handling method of all the filters processed in step 2 @@ -87,7 +82,6 @@ request latest in this phase. It should set the status and response headers and write the response body, if any, to the writer in the filter context. - 5. response: In case none of the filters handled the request, the response @@ -95,8 +89,7 @@ properties, including the status and the headers, are mapped to the outgoing response writer, and the response body is streamed to it, with continuous flushing. - -Routing Rules +# Routing Rules The route matching is implemented in the skipper/routing package. The routing rules are not static, but they can be continuously updated by @@ -106,8 +99,7 @@ The only exceptions are the priority routes, that have not originated from the external data sources, and are tested against the requests before the general routing tree. - -Handling the Host header +# Handling the Host header The default behavior regarding the 'Host' header of the proxy requests is that the proxy ignores the value set in the incoming request. This @@ -126,8 +118,7 @@ To control the value of the outgoing 'Host' header, the `OutgoingHost()` and `SetOutgoingHost()` methods of the `FilterContext` need to be used instead of the `Request.Header` map. - -Circuit Breakers +# Circuit Breakers When configured, skipper can use circuit breakers for the backend requests. It asks the registry for a matching circuit breaker for @@ -138,8 +129,7 @@ current breaker, otherwise it reports a success. For details, see: https://godoc.org/github.com/zalando/skipper/circuit. - -Proxy Example +# Proxy Example The below example demonstrates creating a routing proxy as a standard http.Handler interface: diff --git a/ratelimit/doc.go b/ratelimit/doc.go index 96934b78dd..4e9e8f6fcb 100644 --- a/ratelimit/doc.go +++ b/ratelimit/doc.go @@ -5,24 +5,24 @@ It provides per process rate limiting. It can be configured globally, or based on routes. Rate limiting can be lookuped based on HTTP headers for example X-Forwarded-For or Authorization. -Lookuper Type - SameBucketLookuper +# Lookuper Type - SameBucketLookuper This lookuper will use a static string to point always to the same bucket. This means all requests are counted the same. -Lookuper Type - HeaderLookuper +# Lookuper Type - HeaderLookuper This lookuper will use the content of the the specified header to calculate rate limiting. -Lookuper Type - XForwardedForLookuper +# Lookuper Type - XForwardedForLookuper This lookuper will use the remote IP of the origin request to calculate rate limiting. If there is no such header it will use the remote IP of the request. This is the default Lookuper and may be the one most users want to use. -Usage +# Usage When imported as a package, the Registry can be used to hold the rate limiters and their settings. On a higher level, rate limiter settings @@ -33,37 +33,36 @@ The following command starts skipper with default X-Forwarded-For Lookuper, that will start to rate limit after 5 requests within 60s from the same client - % skipper -ratelimits type=client,max-hits=5,time-window=60s + % skipper -ratelimits type=client,max-hits=5,time-window=60s The following configuration will rate limit /foo after 2 requests within 90s from the same requester and all other requests after 20 requests within 60s from the same client - % cat ratelimit.eskip - foo: Path("/foo") -> clientRatelimit(2,"1m30s") -> "http://www.example.org/foo" - rest: * -> clientRatelimit(20,"1m") -> "http://www.example.net/" - % skipper -enable-ratelimits -routes-file=ratelimit.eskip + % cat ratelimit.eskip + foo: Path("/foo") -> clientRatelimit(2,"1m30s") -> "http://www.example.org/foo" + rest: * -> clientRatelimit(20,"1m") -> "http://www.example.net/" + % skipper -enable-ratelimits -routes-file=ratelimit.eskip The following configuration will rate limit requests after 100 requests within 1 minute with the same Authorization Header - % cat ratelimit-auth.eskip - all: * -> clientRatelimit(100,"1m","Authorization") -> "http://www.example.org/" - % skipper -enable-ratelimits -routes-file=ratelimit-auth.eskip + % cat ratelimit-auth.eskip + all: * -> clientRatelimit(100,"1m","Authorization") -> "http://www.example.org/" + % skipper -enable-ratelimits -routes-file=ratelimit-auth.eskip The following configuration will rate limit requests to /login after 10 requests summed across all skipper peers within one hour from the same requester. - % cat ratelimit.eskip - foo: Path("/login") -> clientRatelimit(10,"1h") -> "http://www.example.org/login" - rest: * -> "http://www.example.net/" - % skipper -enable-ratelimits -routes-file=ratelimit.eskip -enable-swarm - + % cat ratelimit.eskip + foo: Path("/login") -> clientRatelimit(10,"1h") -> "http://www.example.org/login" + rest: * -> "http://www.example.net/" + % skipper -enable-ratelimits -routes-file=ratelimit.eskip -enable-swarm Rate limiter settings can be applied globally via command line flags or within routing settings. -Settings - Type +# Settings - Type Defines the type of the rate limiter. There are types that only use local state information and others that use cluster information using @@ -82,23 +81,23 @@ tested, because of redis. Redis ring based cluster ratelimits should not create a significant memory footprint for skipper instances, but might create load to redis. -Settings - MaxHits +# Settings - MaxHits Defines the maximum number of requests per user within a TimeWindow. -Settings - TimeWindow +# Settings - TimeWindow Defines the time window until rate limits will be enforced, if maximum number of requests are exceeded. This is defined as a string representation of Go's time.Duration, e.g. 1m30s. -Settings - Lookuper +# Settings - Lookuper Defines an optional configuration to choose which Header should be used to group client requests. It accepts any header, for example "Authorization". -Settings - Group +# Settings - Group Defines the ratelimit group, which can be the same for different routes, if you want to have one ratelimiter spanning more than one @@ -106,7 +105,7 @@ route. Make sure your settings are the same for the whole group. In case of different settings for the same group the behavior is undefined and could toggle between different configurations. -HTTP Response +# HTTP Response In case of rate limiting, the HTTP response status will be 429 Too Many Requests and two headers will be set. @@ -122,13 +121,12 @@ request: Both are based on RFC 6585. -Registry +# Registry The active rate limiters are stored in a registry. They are created based on routes or command line flags. The registry synchronizes access to the shared rate limiters. A registry has default settings that it will apply and that it will use the disable rate limiter in case it's not defined in the configuration or not global enabled. - */ package ratelimit diff --git a/rfc/patchpath.go b/rfc/patchpath.go index 581238aadf..58d978f573 100644 --- a/rfc/patchpath.go +++ b/rfc/patchpath.go @@ -58,12 +58,11 @@ func unescape(seq []byte) (byte, bool) { // the stdlib url package, and there is no difference between the parsed and the raw path. // This basically means that the following code is correct: // -// req.URL.Path = rfc.PatchPath(req.URL.Path, req.URL.RawPath) +// req.URL.Path = rfc.PatchPath(req.URL.Path, req.URL.RawPath) // // Links: // - https://tools.ietf.org/html/rfc2616#section-3.2.3 and // - https://tools.ietf.org/html/rfc3986#section-2.2 -// func PatchPath(parsed, raw string) string { p, r := []byte(parsed), []byte(raw) patched := make([]byte, 0, len(r)) diff --git a/routing/doc.go b/routing/doc.go index 6f15aa8f8c..63e6e96c9e 100644 --- a/routing/doc.go +++ b/routing/doc.go @@ -2,8 +2,7 @@ Package routing implements matching of http requests to a continuously updatable set of skipper routes. - -Request Evaluation +# Request Evaluation 1. The path in the http request is used to find one or more matching route definitions in a lookup tree. @@ -21,8 +20,7 @@ expressions, use the go stdlib regexp, which uses re2: https://github.com/google/re2/wiki/Syntax - -Matching Conditions +# Matching Conditions The following types of conditions are supported in the route definitions. @@ -47,8 +45,7 @@ HeaderRegexp("Key", "^Value$"). must be present in the request and one of the associated values must match the expression. - -Wildcards +# Wildcards Path matching supports two kinds of wildcards: @@ -65,8 +62,7 @@ predicate, the name of this parameter will be "*". This makes the PathSubtree("/foo") predicate equivalent to having routes with Path("/foo"), Path("/foo/") and Path("/foo/**") predicates. - -Custom Predicates +# Custom Predicates It is possible to define custom route matching rules in the form of custom predicates. Custom predicates need to implement the PredicateSpec @@ -76,8 +72,7 @@ predicate is matched based on the path tree, the predicate receives the request object, and it returns true or false meaning that the request is a match or not. - -Data Clients +# Data Clients Routing definitions are not directly passed to the routing instance, but they are loaded from clients that implement the DataClient interface. diff --git a/routing/matcher.go b/routing/matcher.go index fb4fac2ed4..686fb8c40a 100644 --- a/routing/matcher.go +++ b/routing/matcher.go @@ -211,7 +211,6 @@ func normalizePath(r *Route) (string, error) { // Using a set of regular expressions shared in // the current generation to preserve the // compiled instances. -// func newLeaf(r *Route, rxs map[string]*regexp.Regexp) (*leafMatcher, error) { hostRxs, err := getCompiledRxs(rxs, r.HostRegexps) if err != nil { diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 1be2a03634..dfd59a2693 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -201,7 +201,6 @@ type Options struct { // metrics. This goroutine is started when the first lifo filter is detected and returns // when the registry is closed. Individual metrics objects (keys) are used for each // lifo filter, and one for each lifo group defined by the lifoGroup filter. -// type Registry struct { options Options measuring bool diff --git a/secrets/readers.go b/secrets/readers.go index eee55d1594..72729f6aed 100644 --- a/secrets/readers.go +++ b/secrets/readers.go @@ -15,10 +15,10 @@ type SecretsReader interface { // StaticSecret implements SecretsReader interface. Example: // -// sec := []byte("mysecret") -// sss := StaticSecret(sec) -// b,_ := sss.GetSecret("") -// string(b) == sec // true +// sec := []byte("mysecret") +// sss := StaticSecret(sec) +// b,_ := sss.GetSecret("") +// string(b) == sec // true type StaticSecret []byte // GetSecret returns the static secret diff --git a/swarm/doc.go b/swarm/doc.go index 646e9481c6..c07c5057f1 100644 --- a/swarm/doc.go +++ b/swarm/doc.go @@ -22,9 +22,9 @@ http://www.cs.cornell.edu/~asdas/research/dsn02-SWIM.pdf. Quote from a nice overview https://prakhar.me/articles/swim/ - The SWIM or the Scalable Weakly-consistent Infection-style process - group Membership protocol is a protocol used for maintaining - membership amongst processes in a distributed system. + The SWIM or the Scalable Weakly-consistent Infection-style process + group Membership protocol is a protocol used for maintaining + membership amongst processes in a distributed system. While starting, Skipper will find its swarm peers through the Kubernetes API server. It will do that using a label selector query diff --git a/tracing/tracing.go b/tracing/tracing.go index 8d4831f63b..50b76c2c82 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -6,18 +6,18 @@ // The tracers, except for "noop", are built as Go Plugins. Note the warning from Go's // plugin.go: // -// // The plugin support is currently incomplete, only supports Linux, -// // and has known bugs. Please report any issues. +// // The plugin support is currently incomplete, only supports Linux, +// // and has known bugs. Please report any issues. // // All plugins must have a function named "InitTracer" with the following signature // -// func([]string) (opentracing.Tracer, error) +// func([]string) (opentracing.Tracer, error) // // The parameters passed are all arguments for the plugin, i.e. everything after the first // word from skipper's -opentracing parameter. E.g. when the -opentracing parameter is // "mytracer foo=bar token=xxx somename=bla:3" the "mytracer" plugin will receive // -// []string{"foo=bar", "token=xxx", "somename=bla:3"} +// []string{"foo=bar", "token=xxx", "somename=bla:3"} // // as arguments. // @@ -25,24 +25,24 @@ // // An example plugin looks like // -// package main +// package main // -// import ( -// basic "github.com/opentracing/basictracer-go" -// opentracing "github.com/opentracing/opentracing-go" -// ) +// import ( +// basic "github.com/opentracing/basictracer-go" +// opentracing "github.com/opentracing/opentracing-go" +// ) // -// func InitTracer(opts []string) (opentracing.Tracer, error) { -// return basic.NewTracerWithOptions(basic.Options{ -// Recorder: basic.NewInMemoryRecorder(), -// ShouldSample: func(traceID uint64) bool { return traceID%64 == 0 }, -// MaxLogsPerSpan: 25, -// }), nil -// } +// func InitTracer(opts []string) (opentracing.Tracer, error) { +// return basic.NewTracerWithOptions(basic.Options{ +// Recorder: basic.NewInMemoryRecorder(), +// ShouldSample: func(traceID uint64) bool { return traceID%64 == 0 }, +// MaxLogsPerSpan: 25, +// }), nil +// } // // This should be built with // -// go build -buildmode=plugin -o basic.so ./basic/basic.go +// go build -buildmode=plugin -o basic.so ./basic/basic.go // // and copied to the given as -plugindir (by default, "./plugins"). // From d4b2a6299ef304ef22d9bd1d19899b82f9cb6359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 13 Sep 2022 20:36:25 +0200 Subject: [PATCH 0076/1047] Tool/govulncheck should not block build (#2075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * tool: remove govulncheck from blocking builds but run concurrent in pipeline Signed-off-by: Sandor Szücs * cleanup pipeline step and only run on merge to master Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- delivery.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/delivery.yaml b/delivery.yaml index a2874659da..759041c789 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -1,6 +1,21 @@ version: "2017-09-20" +allow_concurrent_steps: true pipeline: +- id: vulncheck + depends_on: [] + vm_config: + type: linux + image: "cdp-runtime/go-1.18" + type: script + when: + branch: master + event: push + commands: + - desc: vuln-check + cmd: | + make deps govulncheck - id: build + depends_on: [] vm_config: type: linux image: "cdp-runtime/go-1.18" @@ -35,7 +50,7 @@ pipeline: fi export IMAGE ARM_IMAGE ARM64_IMAGE MULTIARCH_IMAGE - make deps cicheck staticcheck gosec govulncheck + make deps cicheck staticcheck gosec git status git diff cd packaging && make docker.build.amd64 && git status && git diff && make docker.push.amd64 && make docker.push.multiarch @@ -75,6 +90,7 @@ pipeline: echo "Not creating a release. No release version defined." fi - id: docs + depends_on: [build] type: script overlay: ci/python commands: From 5b831ff5cf6f4c51628fcf1a4ad7e55af3b6cc5a Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Thu, 15 Sep 2022 12:59:58 +0300 Subject: [PATCH 0077/1047] Added modResponseHeader filter (#2076) * Added modResponseHeader filter Signed-off-by: Roman Zavodskikh * :wAdded info about modResponseHeader into documentation Signed-off-by: Roman Zavodskikh Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- docs/reference/filters.md | 16 +++++++ filters/builtin/builtin.go | 1 + filters/builtin/mod_header.go | 57 ++++++++++++++++++++++++ filters/builtin/mod_header_test.go | 71 ++++++++++++++++++++++++++++++ filters/filters.go | 1 + 5 files changed, 146 insertions(+) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index e9f2767612..3c007df8a1 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -155,6 +155,22 @@ Example: foo: * -> dropRequestHeader("User-Agent") -> "https://backend.example.org"; ``` +## modResponseHeader + +Same as [modRequestHeader](#modrequestheader), only for responses + +Parameters: + +* header name (string) +* the expression to match (regex) +* the replacement (string) + +Example: + +``` +do_not_avoid_caching: * -> modResponseHeader("cache-control", "no-cache", "cache") -> "https://zalando.de"; +``` + ## setResponseHeader Same as [setRequestHeader](#setrequestheader), only for responses diff --git a/filters/builtin/builtin.go b/filters/builtin/builtin.go index 53e1ad800c..660b7162fd 100644 --- a/filters/builtin/builtin.go +++ b/filters/builtin/builtin.go @@ -138,6 +138,7 @@ func MakeRegistry() filters.Registry { NewModPath(), NewSetPath(), NewModRequestHeader(), + NewModResponseHeader(), NewDropQuery(), NewSetQuery(), NewHealthCheck(), diff --git a/filters/builtin/mod_header.go b/filters/builtin/mod_header.go index 242530afcf..a3ef00e64a 100644 --- a/filters/builtin/mod_header.go +++ b/filters/builtin/mod_header.go @@ -73,3 +73,60 @@ func (f *modRequestHeader) Request(ctx filters.FilterContext) { } func (*modRequestHeader) Response(filters.FilterContext) {} + +type modResponseHeader struct { + headerName string + rx *regexp.Regexp + replacement string +} + +// NewModResponseHeader returns a new filter Spec, whose instances execute +// regexp.ReplaceAllString on the request host. Instances expect three +// parameters: the header name, the expression to match and the replacement string. +// Name: "modResponseHeader". +func NewModResponseHeader() filters.Spec { return &modResponseHeader{} } + +func (spec *modResponseHeader) Name() string { + return filters.ModResponseHeaderName +} + +//lint:ignore ST1016 "spec" makes sense here and we reuse the type for the filter +func (spec *modResponseHeader) CreateFilter(config []interface{}) (filters.Filter, error) { + if len(config) != 3 { + return nil, filters.ErrInvalidFilterParameters + } + + headerName, ok := config[0].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + expr, ok := config[1].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + replacement, ok := config[2].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + rx, err := regexp.Compile(expr) + if err != nil { + return nil, err + } + + return &modResponseHeader{headerName: headerName, rx: rx, replacement: replacement}, nil +} + +func (*modResponseHeader) Request(filters.FilterContext) {} + +func (f *modResponseHeader) Response(ctx filters.FilterContext) { + resp := ctx.Response() + + if _, ok := resp.Header[http.CanonicalHeaderKey(f.headerName)]; !ok { + return + } + + resp.Header.Set(f.headerName, f.rx.ReplaceAllString(resp.Header.Get(f.headerName), f.replacement)) +} diff --git a/filters/builtin/mod_header_test.go b/filters/builtin/mod_header_test.go index 4813821557..af716cc3c8 100644 --- a/filters/builtin/mod_header_test.go +++ b/filters/builtin/mod_header_test.go @@ -156,6 +156,77 @@ func TestModRequestHeader(t *testing.T) { } } +func TestModResponseHeader(t *testing.T) { + for _, tt := range []struct { + msg string + headerName string + expression string + replacement string + responseHeader http.Header + expectedHeader string + expectHeaderToNotExist bool + }{{ + "replace when header is provided and pattern matches", + "Accept-Language", + `^nl\-NL$`, + `en`, + http.Header{"Accept-Language": []string{"nl-NL"}}, + "en", + false, + }, { + "replace when header is provided and pattern matches anything", + "Accept-Language", + `^.*`, + `en`, + http.Header{"Accept-Language": []string{"nl-NL"}}, + "en", + false, + }, { + "replace when header is not provided and pattern matches anything", + "Accept-Language", + `^.*`, + `en`, + http.Header{}, + "", + true, + }, { + "do not replace when header is not provided and pattern does not match", + "Accept-Language", + `fr`, + `en`, + http.Header{}, + "", + true, + }} { + t.Run(tt.msg, func(t *testing.T) { + spec := NewModResponseHeader() + f, err := spec.CreateFilter([]interface{}{tt.headerName, tt.expression, tt.replacement}) + if err != nil { + t.Error(err) + } + + resp := http.Response{} + resp.Header = http.Header{} + + for n, vs := range tt.responseHeader { + resp.Header[n] = vs + } + + ctx := &filtertest.Context{FResponse: &resp} + f.Response(ctx) + + hv := resp.Header.Get(tt.headerName) + if hv != tt.expectedHeader { + t.Errorf(`failed to modify request header %s to "%s". Got: "%s"`, tt.headerName, tt.expectedHeader, hv) + } + + if tt.expectHeaderToNotExist && len(resp.Header.Values(tt.headerName)) != 0 { + t.Errorf(`expected header %s to not exist, but it does`, tt.headerName) + } + }) + } +} + func TestModifyHostWithInvalidExpression(t *testing.T) { spec := NewModRequestHeader() if f, err := spec.CreateFilter([]interface{}{"Host", "(?=;)", "foo"}); err == nil || f != nil { diff --git a/filters/filters.go b/filters/filters.go index 2fc998dd1d..31a06d538a 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -185,6 +185,7 @@ const ( SetRequestHeaderName = "setRequestHeader" AppendRequestHeaderName = "appendRequestHeader" DropRequestHeaderName = "dropRequestHeader" + ModResponseHeaderName = "modResponseHeader" SetResponseHeaderName = "setResponseHeader" AppendResponseHeaderName = "appendResponseHeader" DropResponseHeaderName = "dropResponseHeader" From 4c797c4d8924afc55ed1c1084740e44f7a80e5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 15 Sep 2022 15:23:46 +0200 Subject: [PATCH 0078/1047] fix: fifo error metric counted "other" errors in case there is no error (#2073) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: error metric counted "other" errors in case there is no error fix: counter should reflect semaphore state, right now metrics show only 0 or 1 sometimes 2 as "active" Signed-off-by: Sandor Szücs * fix: tests and counter decrement on error fix: all review things that we found during a call fix: reconfiguration and test fix: capture atomic counter to local scope such that we do not have race condition while reconfigure test: add proxy test test: update test according to how reconfigrations of fifo works test: add test with constant routing table scheduler changes while running a stream of requests Signed-off-by: Sandor Szücs * fix my own review Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- filters/scheduler/fifo.go | 3 +- filters/scheduler/fifo_test.go | 174 +++++++++++++++++++++++++++- scheduler/export_test.go | 10 ++ scheduler/fifo_test.go | 17 ++- scheduler/proxy_test.go | 130 +++++++++++++++++++++ scheduler/scheduler.go | 45 +++++--- scheduler/scheduler_test.go | 202 +++++++++++++++++++++++++++++++-- 7 files changed, 545 insertions(+), 36 deletions(-) create mode 100644 scheduler/proxy_test.go diff --git a/filters/scheduler/fifo.go b/filters/scheduler/fifo.go index f8d20fef09..65fe1ce683 100644 --- a/filters/scheduler/fifo.go +++ b/filters/scheduler/fifo.go @@ -94,8 +94,9 @@ func (f *fifoFilter) SetQueue(fq *scheduler.FifoQueue) { // - 502 if queue timeout // - 500 if error unknown func (f *fifoFilter) Request(ctx filters.FilterContext) { + q := f.GetQueue() c := ctx.Request().Context() - done, err := f.queue.Wait(c) + done, err := q.Wait(c) if err != nil { if span := opentracing.SpanFromContext(c); span != nil { ext.Error.Set(span, true) diff --git a/filters/scheduler/fifo_test.go b/filters/scheduler/fifo_test.go index ee22cc4a6c..3565b77b46 100644 --- a/filters/scheduler/fifo_test.go +++ b/filters/scheduler/fifo_test.go @@ -298,10 +298,178 @@ func TestFifo(t *testing.T) { } va := newVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) - // buf := bytes.NewBuffer(make([]byte, 0, 1024)) - // va.Attack(buf, 3*time.Second, tt.name) va.Attack(io.Discard, 1*time.Second, tt.name) - //t.Logf("buf: %v", buf.String()) + + t.Logf("Success [0..1]: %0.2f", va.metrics.Success) + t.Logf("requests: %d", va.metrics.Requests) + got := va.metrics.Success * float64(va.metrics.Requests) + want := tt.wantOkRate * float64(va.metrics.Requests) + if got < want { + t.Fatalf("OK rate too low got 0 { + t.Fatal("no OK") + } + if !ok && tt.wantOkRate == 0 { + count499, ok := va.metrics.StatusCodes["0"] + if !ok || va.metrics.Requests != uint64(count499) { + t.Fatalf("want all 499 client cancel but %d != %d", va.metrics.Requests, count499) + } + } + if float64(countOK) < want { + t.Fatalf("OK too low got fifo(%v, %v, "%v") -> "%s"`, args...) + + dc, err := testdataclient.NewDoc(doc) + if err != nil { + t.Fatalf("Failed to create testdataclient: %v", err) + } + ro := routing.Options{ + SignalFirstLoad: true, + FilterRegistry: fr, + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{reg}, + } + rt := routing.New(ro) + defer rt.Close() + <-rt.FirstLoad() + + tracer := &testTracer{MockTracer: mocktracer.New()} + pr := proxy.WithParams(proxy.Params{ + Routing: rt, + OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + }) + defer pr.Close() + + ts := httptest.NewServer(pr) + defer ts.Close() + + reqURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("Failed to parse url %s: %v", ts.URL, err) + } + + rsp, err := http.DefaultClient.Get(reqURL.String()) + if err != nil { + t.Fatalf("Failed to get response from %s: %v", reqURL.String(), err) + } + if rsp.StatusCode != http.StatusOK { + t.Fatalf("Failed to get valid response from endpoint: %d", rsp.StatusCode) + } + + // run dataclient updates + quit := make(chan struct{}) + newDoc := fmt.Sprintf(`aroute: * -> fifo(100, 200, "250ms") -> "%s"`, backend.URL) + go func(q chan<- struct{}, updateRate time.Duration, doc1, doc2 string) { + i := 0 + for { + select { + case <-quit: + println("number of route updates:", i) + return + case <-time.After(updateRate): + } + i++ + if i%2 == 0 { + dc.UpdateDoc(doc2, nil) + } else { + dc.UpdateDoc(doc1, nil) + } + } + + }(quit, tt.updateRate, doc, newDoc) + + va := newVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) + va.Attack(io.Discard, 1*time.Second, tt.name) + quit <- struct{}{} t.Logf("Success [0..1]: %0.2f", va.metrics.Success) t.Logf("requests: %d", va.metrics.Requests) diff --git a/scheduler/export_test.go b/scheduler/export_test.go index 2e50cd3b5c..c335b163d2 100644 --- a/scheduler/export_test.go +++ b/scheduler/export_test.go @@ -3,3 +3,13 @@ package scheduler var ( ExportQueueCloseDelay = &queueCloseDelay ) + +// GetFifoForTest is only compiled and used in tests +func (r *Registry) GetFifoForTest(s string) (*FifoQueue, bool) { + id := queueId{ + name: s, + grouped: false, + } + q, ok := r.fifoQueues[id] + return q, ok +} diff --git a/scheduler/fifo_test.go b/scheduler/fifo_test.go index 29fd7bd137..640ec00dc3 100644 --- a/scheduler/fifo_test.go +++ b/scheduler/fifo_test.go @@ -11,16 +11,20 @@ import ( func TestFifo(t *testing.T) { waitForStatus := func(t *testing.T, fq *FifoQueue, s QueueStatus) { t.Helper() + if fq == nil { + t.Fatal("fq nil") + } timeout := time.After(120 * time.Millisecond) for { time.Sleep(time.Millisecond) - if fq != nil && fq.Status() == s { + cur := fq.Status() + if cur == s { return } select { case <-timeout: - t.Fatal("failed to reach status") + t.Fatalf("failed to reach status, want %v, got: %v", s, cur) default: } } @@ -49,14 +53,7 @@ func TestFifo(t *testing.T) { go fq.Wait(ctx) go fq.Wait(ctx) go fq.Wait(ctx) - waitForStatus(t, fq, QueueStatus{ActiveRequests: 1, QueuedRequests: 1}) - - ch := make(chan struct{}) - go func() { - ch <- struct{}{} - fq.Wait(ctx) - }() - <-ch + waitForStatus(t, fq, QueueStatus{ActiveRequests: 1, QueuedRequests: 2}) f, err = fq.Wait(ctx) if err != ErrQueueFull { diff --git a/scheduler/proxy_test.go b/scheduler/proxy_test.go new file mode 100644 index 0000000000..25fa93ce42 --- /dev/null +++ b/scheduler/proxy_test.go @@ -0,0 +1,130 @@ +package scheduler_test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "sync" + "testing" + "time" + + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters/builtin" + filterScheduler "github.com/zalando/skipper/filters/scheduler" + "github.com/zalando/skipper/metrics" + "github.com/zalando/skipper/net" + "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/routing/testdataclient" + "github.com/zalando/skipper/scheduler" +) + +func TestFifoChanges(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(200 * time.Millisecond) + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + })) + defer backend.Close() + + schedulerRegistry := scheduler.RegistryWith(scheduler.Options{ + Metrics: metrics.Default, + EnableRouteFIFOMetrics: true, + }) + defer schedulerRegistry.Close() + + spec := filterScheduler.NewFifo() + fr := builtin.MakeRegistry() + fr.Register(spec) + args := []interface{}{ + 2, + 2, + "2s", + } + + _, err := spec.CreateFilter(args) + if err != nil { + t.Fatalf("Failed to create filter: %v", err) + } + + r := &eskip.Route{Id: "r_test", Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, Backend: backend.URL} + + doc := fmt.Sprintf(`r_test: * -> fifo(%d, %d, "%s") -> "%s"`, append(args, backend.URL)...) + cli, err := testdataclient.NewDoc(doc) + if err != nil { + t.Fatalf("Failed to create testdataclient: %v", err) + } + + proxy := proxytest.WithRoutingOptions(fr, routing.Options{ + SignalFirstLoad: true, + DataClients: []routing.DataClient{cli}, + PreProcessors: []routing.PreProcessor{ + schedulerRegistry.PreProcessor(), + }, + PostProcessors: []routing.PostProcessor{ + schedulerRegistry, + }, + }, r) + + reqURL, err := url.Parse(proxy.URL) + if err != nil { + t.Errorf("Failed to parse url %s: %v", proxy.URL, err) + } + + req, err := http.NewRequest("GET", reqURL.String(), nil) + if err != nil { + t.Error(err) + return + } + + errCH := make(chan error) + f := func(t *testing.T, errCH chan<- error, r *http.Request, wantCode int) { + t.Helper() + + client := net.NewClient(net.Options{ + ResponseHeaderTimeout: 2 * time.Second, + }) + rsp, err := client.Do(r) + if err != nil { + errCH <- fmt.Errorf("Failed to do http call: %w", err) + return + } + defer rsp.Body.Close() + if rsp.StatusCode != wantCode { + errCH <- fmt.Errorf("fifo filter failed got=%d, want %d", rsp.StatusCode, wantCode) + return + } + } + time.Sleep(100 * time.Millisecond) + N := 4 + var wg sync.WaitGroup + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + f(t, errCH, req, http.StatusOK) + wg.Done() + + }() + } + time.Sleep(100 * time.Millisecond) + go f(t, errCH, req, http.StatusServiceUnavailable) + + wg.Wait() + close(errCH) + for err := range errCH { + if err != nil { + t.Fatal(err.Error()) + } + } + + errCH = make(chan error) + go func() { + f(t, errCH, req, http.StatusOK) + close(errCH) + }() + err = <-errCH + if err != nil { + t.Fatalf("Failed to get ok: %v", err) + } +} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index dfd59a2693..67b22fad42 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -109,6 +109,7 @@ type fifoQueue struct { func (fq *fifoQueue) status() QueueStatus { fq.mu.RLock() maxConcurrency := fq.maxConcurrency + closed := fq.closed fq.mu.RUnlock() all := fq.counter.Load() @@ -125,18 +126,24 @@ func (fq *fifoQueue) status() QueueStatus { return QueueStatus{ ActiveRequests: int(active), QueuedRequests: int(queued), - Closed: fq.closed, + Closed: closed, } } -// Reconfigure does nothing, because it creates a data race that would -// require more mutex than we want to have +func (fq *fifoQueue) close() { + fq.mu.Lock() + fq.closed = true + fq.mu.Unlock() +} + func (fq *fifoQueue) reconfigure(c Config) { fq.mu.Lock() defer fq.mu.Unlock() fq.maxConcurrency = uint64(c.MaxConcurrency) fq.maxQueueSize = uint64(c.MaxQueueSize) fq.timeout = c.Timeout + fq.sem = semaphore.NewWeighted(int64(c.MaxConcurrency)) + fq.counter = atomic.NewUint64(0) } func (fq *fifoQueue) wait(ctx context.Context) (func(), error) { @@ -144,20 +151,25 @@ func (fq *fifoQueue) wait(ctx context.Context) (func(), error) { maxConcurrency := fq.maxConcurrency maxQueueSize := fq.maxQueueSize timeout := fq.timeout + sem := fq.sem + cnt := fq.counter fq.mu.RUnlock() // handle queue - all := fq.counter.Inc() - defer fq.counter.Dec() + all := cnt.Inc() // queue full? if all > maxConcurrency+maxQueueSize { + cnt.Dec() return nil, ErrQueueFull } - // limit concurrency + // set timeout c, done := context.WithTimeout(ctx, timeout) defer done() - if err := fq.sem.Acquire(c, 1); err != nil { + + // limit concurrency + if err := sem.Acquire(c, 1); err != nil { + cnt.Dec() switch err { case context.DeadlineExceeded: return nil, ErrQueueTimeout @@ -168,8 +180,11 @@ func (fq *fifoQueue) wait(ctx context.Context) (func(), error) { return nil, err } } + return func() { - fq.sem.Release(1) + // postpone release to Response() filter + cnt.Dec() + sem.Release(1) }, nil } @@ -274,7 +289,7 @@ type GroupedLIFOFilter interface { // will be nil. func (fq *FifoQueue) Wait(ctx context.Context) (func(), error) { f, err := fq.queue.wait(ctx) - if fq.metrics != nil { + if err != nil && fq.metrics != nil { switch err { case ErrQueueFull: fq.metrics.IncCounter(fq.errorFullMetricsKey) @@ -299,12 +314,15 @@ func (fq *FifoQueue) Config() Config { return fq.config } -func (fq *FifoQueue) reconfigure() { - fq.queue.reconfigure(fq.config) +// Reconfigure updates the connfiguration of the FifoQueue. It will +// reset the current state. +func (fq *FifoQueue) Reconfigure(c Config) { + fq.config = c + fq.queue.reconfigure(c) } func (fq *FifoQueue) close() { - fq.queue.closed = true + fq.queue.close() } // Wait blocks until a request can be processed or needs to be rejected. @@ -382,8 +400,7 @@ func (r *Registry) getFifoQueue(id queueId, c Config) *FifoQueue { fq, ok := r.fifoQueues[id] if ok { if fq.config != c { - fq.config = c - fq.reconfigure() + fq.Reconfigure(c) } } else { fq = r.newFifoQueue(id.name, c) diff --git a/scheduler/scheduler_test.go b/scheduler/scheduler_test.go index 74598fc49e..23d952791f 100644 --- a/scheduler/scheduler_test.go +++ b/scheduler/scheduler_test.go @@ -245,19 +245,26 @@ func TestScheduler(t *testing.T) { func TestConfig(t *testing.T) { waitForStatus := func(t *testing.T, fq *scheduler.FifoQueue, q *scheduler.Queue, s scheduler.QueueStatus) { t.Helper() + var st scheduler.QueueStatus timeout := time.After(120 * time.Millisecond) for { - if q != nil && q.Status() == s { - return + if q != nil { + st = q.Status() + if st == s { + return + } } - if fq != nil && fq.Status() == s { - return + if fq != nil { + st = fq.Status() + if st == s { + return + } } select { case <-timeout: - t.Fatal("failed to reach status") + t.Fatalf("failed to reach status got %v, want %v", st, s) default: } } @@ -346,7 +353,7 @@ func TestConfig(t *testing.T) { waitForStatus(t, nil, q1, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) }) - t.Run("update fifo config", func(t *testing.T) { + t.Run("update fifo config increase concurrency", func(t *testing.T) { const doc = `route: * -> fifo(2, 2, "3s") -> ` rt, dc, close := initTest(doc) defer close() @@ -357,22 +364,201 @@ func TestConfig(t *testing.T) { // fill up the queue: go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + q := f.Filter.(scheduler.FIFOFilter).GetQueue() + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + + // change the configuration + updateDoc(t, dc, `route: * -> fifo(3, 2, "3s") -> `, nil) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 0, QueuedRequests: 0}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 3, QueuedRequests: 2}) + }) + + t.Run("update fifo config increase max queue size", func(t *testing.T) { + const doc = `route: * -> fifo(2, 2, "3s") -> ` + rt, dc, close := initTest(doc) + defer close() + + req := &http.Request{URL: &url.URL{}} + r, _ := rt.Route(req) + f := r.Filters[0] + + // fill up the queue: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) q := f.Filter.(scheduler.FIFOFilter).GetQueue() waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) - // change the configuration, should decrease the queue size: + // change the configuration updateDoc(t, dc, `route: * -> fifo(2, 3, "3s") -> `, nil) - go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 0, QueuedRequests: 0}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 3}) }) + t.Run("update fifo config decrease max queue size", func(t *testing.T) { + const doc = `route: * -> fifo(2, 2, "3s") -> ` + rt, dc, close := initTest(doc) + defer close() + + req := &http.Request{URL: &url.URL{}} + r, _ := rt.Route(req) + f := r.Filters[0] + + // fill up the queue: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + q := f.Filter.(scheduler.FIFOFilter).GetQueue() + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + + // change the configuration + updateDoc(t, dc, `route: * -> fifo(2, 1, "3s") -> `, nil) + + // update resets + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 0, QueuedRequests: 0}) + //filling again + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + + // adding requests won't change the state if we have already too many in queue + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + }) + + t.Run("update fifo config decrease max concurrency", func(t *testing.T) { + const doc = `route: * -> fifo(2, 2, "3s") -> ` + rt, dc, close := initTest(doc) + defer close() + + req := &http.Request{URL: &url.URL{}} + r, _ := rt.Route(req) + f := r.Filters[0] + + // fill up the queue minus one: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + q := f.Filter.(scheduler.FIFOFilter).GetQueue() + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + + // change the configuration + updateDoc(t, dc, `route: * -> fifo(1, 2, "3s") -> `, nil) + + // update resets + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 0, QueuedRequests: 0}) + // filling again + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 1, QueuedRequests: 2}) + + // adding requests won't change the state if we have already too many in queue + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 1, QueuedRequests: 2}) + }) + + t.Run("update fifo config decrease max queue size overflow test", func(t *testing.T) { + const doc = `route: * -> fifo(2, 2, "3s") -> ` + rt, dc, close := initTest(doc) + defer close() + + req := &http.Request{URL: &url.URL{}} + r, _ := rt.Route(req) + f := r.Filters[0] + + // fill up the queue: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + q := f.Filter.(scheduler.FIFOFilter).GetQueue() + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + + // change the configuration + updateDoc(t, dc, `route: * -> fifo(2, 1, "3s") -> `, nil) + + // update resets + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 0, QueuedRequests: 0}) + + // fill up the queue again + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + + // adding requests won't change the state if we have already too many in queue + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 1}) + }) + + t.Run("update fifo config decrease max concurrency overflow test", func(t *testing.T) { + const doc = `route: * -> fifo(2, 2, "3s") -> ` + rt, dc, close := initTest(doc) + defer close() + + req := &http.Request{URL: &url.URL{}} + r, _ := rt.Route(req) + f := r.Filters[0] + + // fill up the queue minus one: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + + q := f.Filter.(scheduler.FIFOFilter).GetQueue() + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 2, QueuedRequests: 2}) + + // change the configuration + updateDoc(t, dc, `route: * -> fifo(1, 2, "3s") -> `, nil) + + // update resets + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 0, QueuedRequests: 0}) + + // fill up the queue again: + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 1, QueuedRequests: 2}) + + // adding requests won't change the state if we have already too many in queue + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + go f.Request(&filtertest.Context{FRequest: req, FStateBag: make(map[string]interface{})}) + waitForStatus(t, q, nil, scheduler.QueueStatus{ActiveRequests: 1, QueuedRequests: 2}) + }) + t.Run("update lifo config", func(t *testing.T) { const doc = `route: * -> lifo(2, 2) -> ` rt, dc, close := initTest(doc) From 495ecd7781cc676db1495dd870b9e0df9e841e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 15 Sep 2022 19:37:42 +0200 Subject: [PATCH 0079/1047] fix govulncheck reported issue (#2077) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index db2bdebc32..2c27d4cad1 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 go.uber.org/atomic v1.9.0 golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 diff --git a/go.sum b/go.sum index bf7c3f5361..6a8c97efb0 100644 --- a/go.sum +++ b/go.sum @@ -1087,8 +1087,8 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From 869c25b814d2be8d44b762f6ced82d98a6e8f040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Mon, 19 Sep 2022 18:26:58 +0200 Subject: [PATCH 0080/1047] fix: redis autoscaling introduced a concurrent map access issue with kubernetes clusterstate, which was only used by one goroutine (#2078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- dataclients/kubernetes/clusterstate.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dataclients/kubernetes/clusterstate.go b/dataclients/kubernetes/clusterstate.go index cfe5f74948..6a4c6645fc 100644 --- a/dataclients/kubernetes/clusterstate.go +++ b/dataclients/kubernetes/clusterstate.go @@ -3,6 +3,7 @@ package kubernetes import ( "fmt" "sort" + "sync" log "github.com/sirupsen/logrus" @@ -10,6 +11,7 @@ import ( ) type clusterState struct { + mu sync.Mutex ingresses []*definitions.IngressItem ingressesV1 []*definitions.IngressV1Item routeGroups []*definitions.RouteGroupItem @@ -20,6 +22,8 @@ type clusterState struct { } func (state *clusterState) getService(namespace, name string) (*service, error) { + state.mu.Lock() + defer state.mu.Unlock() s, ok := state.services[newResourceID(namespace, name)] if !ok { return nil, errServiceNotFound @@ -34,6 +38,8 @@ func (state *clusterState) getService(namespace, name string) (*service, error) } func (state *clusterState) getServiceRG(namespace, name string) (*service, error) { + state.mu.Lock() + defer state.mu.Unlock() s, ok := state.services[newResourceID(namespace, name)] if !ok { return nil, fmt.Errorf("service not found: %s/%s", namespace, name) @@ -49,6 +55,8 @@ func (state *clusterState) GetEndpointsByService(namespace, name, protocol strin TargetPort: servicePort.TargetPort.String(), } + state.mu.Lock() + defer state.mu.Unlock() if cached, ok := state.cachedEndpoints[epID]; ok { return cached } @@ -71,6 +79,8 @@ func (state *clusterState) GetEndpointsByTarget(namespace, name, protocol string TargetPort: target.String(), } + state.mu.Lock() + defer state.mu.Unlock() if cached, ok := state.cachedEndpoints[epID]; ok { return cached } From af67775fb49b52d045bed11d36e9411d5d93c6da Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 23 Sep 2022 10:57:35 +0200 Subject: [PATCH 0081/1047] proxy: fix FIFO/LIFO cleanup (#2082) `*Proxy.do()` maybe called for the `` backend which resulted in double call of pending `done` handlers. Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- filters/scheduler/loopback_test.go | 57 ++++++++++++++++++++++++++++++ proxy/proxy.go | 11 ++++-- 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 filters/scheduler/loopback_test.go diff --git a/filters/scheduler/loopback_test.go b/filters/scheduler/loopback_test.go new file mode 100644 index 0000000000..56780ca88a --- /dev/null +++ b/filters/scheduler/loopback_test.go @@ -0,0 +1,57 @@ +package scheduler_test + +import ( + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/builtin" + fifo "github.com/zalando/skipper/filters/scheduler" + "github.com/zalando/skipper/proxy" + "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/routing/testdataclient" + "github.com/zalando/skipper/scheduler" +) + +func TestFifoLoopback(t *testing.T) { + dc, err := testdataclient.NewDoc(` + main: Path("/") -> fifo(100, 100, "1s") -> setPath("/loop") -> ; + loop: Path("/loop") -> fifo(100, 100, "1s") -> inlineContent("ok") -> ; + `) + require.NoError(t, err) + + filterRegistry := make(filters.Registry) + filterRegistry.Register(fifo.NewFifo()) + filterRegistry.Register(builtin.NewSetPath()) + filterRegistry.Register(builtin.NewInlineContent()) + + schedulerRegistry := scheduler.RegistryWith(scheduler.Options{}) + defer schedulerRegistry.Close() + + rt := routing.New(routing.Options{ + DataClients: []routing.DataClient{dc}, + FilterRegistry: filterRegistry, + PreProcessors: []routing.PreProcessor{schedulerRegistry.PreProcessor()}, + PostProcessors: []routing.PostProcessor{schedulerRegistry}, + SignalFirstLoad: true, + }) + <-rt.FirstLoad() + + pr := proxy.WithParams(proxy.Params{Routing: rt}) + tsp := httptest.NewServer(pr) + defer tsp.Close() + + resp, err := http.Get(tsp.URL + "/") + require.NoError(t, err) + defer resp.Body.Close() + + content, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + assert.Equal(t, "ok", string(content)) +} diff --git a/proxy/proxy.go b/proxy/proxy.go index e23fd23fe3..1e08e3c682 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1059,15 +1059,22 @@ func (p *Proxy) do(ctx *context) error { // happens if we get proxy errors, for example connect errors, // which would block responses until fifo() timeouts. defer func() { - pendingFIFO, _ := ctx.StateBag()[scheduler.FIFOKey].([]func()) + stateBag := ctx.StateBag() + + pendingFIFO, _ := stateBag[scheduler.FIFOKey].([]func()) for _, done := range pendingFIFO { done() } - pendingLIFO, _ := ctx.StateBag()[scheduler.LIFOKey].([]func()) + pendingLIFO, _ := stateBag[scheduler.LIFOKey].([]func()) for _, done := range pendingLIFO { done() } + + // Cleanup state bag to avoid double call of done() + // because do() could be called for loopback backend + delete(stateBag, scheduler.FIFOKey) + delete(stateBag, scheduler.LIFOKey) }() // proxy global setting From 8d420b78b2623e42c912eaad14e62aeb57c9e973 Mon Sep 17 00:00:00 2001 From: herojan Date: Tue, 11 Oct 2022 09:58:24 +0100 Subject: [PATCH 0082/1047] Implement fadeIn for consistent hash algorithm (#2087) * Make consistentHash a struct Signed-off-by: Francis Moloney * Configure fadein Signed-off-by: Francis Moloney * Add test for consistenthash fadein Signed-off-by: Francis Moloney * Modify fadeIn implementation for consistent hash algorithm Signed-off-by: Francis Moloney * Use fadingIndexes array with correct length Signed-off-by: Francis Moloney * PR Feedback: use pointers for consistentHash algorithm Signed-off-by: Francis Moloney * PR Feedback: Rename skip function Signed-off-by: Francis Moloney Signed-off-by: Francis Moloney --- docs/reference/filters.md | 2 +- loadbalancer/algorithm.go | 116 ++++++++++++++++++++++++--------- loadbalancer/algorithm_test.go | 26 ++++---- loadbalancer/fadein_test.go | 19 +++++- 4 files changed, 116 insertions(+), 47 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 3c007df8a1..73ec49ed50 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -2667,7 +2667,7 @@ the traffic in a gradually increasing way, starting from their detection for the they receive equal amount traffic as the previously existing routes. The detection time of an load balanced backend endpoint is preserved over multiple generations of the route configuration (over route changes). This filter can be used to saturate the load of autoscaling applications that require a warm-up time and therefore a -smooth ramp-up. The fade-in feature can be used together with the round-robin and random LB algorithms. +smooth ramp-up. The fade-in feature can be used together with the roundRobin, random or consistentHash LB algorithms. While the default fade-in curve is linear, the optional exponent parameter can be used to adjust the shape of the fade-in curve, based on the following equation: diff --git a/loadbalancer/algorithm.go b/loadbalancer/algorithm.go index d5037eaa4d..91ac6de97a 100644 --- a/loadbalancer/algorithm.go +++ b/loadbalancer/algorithm.go @@ -10,7 +10,7 @@ import ( "sync" "time" - xxhash "github.com/cespare/xxhash/v2" + "github.com/cespare/xxhash/v2" log "github.com/sirupsen/logrus" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/net" @@ -93,11 +93,6 @@ func shiftWeighted(rnd *rand.Rand, ctx *routing.LBContext, w []float64, now time func shiftToRemaining(rnd *rand.Rand, ctx *routing.LBContext, wi []int, wf []float64, now time.Time) routing.LBEndpoint { notFadingIndexes := wi ep := ctx.Route.LBEndpoints - for i := 0; i < len(ep); i++ { - if _, fadingIn := fadeInState(now, ctx.Route.LBFadeInDuration, ep[i].Detected); !fadingIn { - notFadingIndexes = append(notFadingIndexes, i) - } - } // if all endpoints are fading, the simplest approach is to use the oldest, // this departs from the desired curve, but guarantees monotonic fade-in. From @@ -111,7 +106,8 @@ func shiftToRemaining(rnd *rand.Rand, ctx *routing.LBContext, wi []int, wf []flo return ep[notFadingIndexes[rnd.Intn(len(notFadingIndexes))]] } -func withFadeIn(rnd *rand.Rand, ctx *routing.LBContext, wi []int, wf []float64, choice int) routing.LBEndpoint { +func withFadeIn(rnd *rand.Rand, ctx *routing.LBContext, notFadingIndexes []int, choice int, algo routing.LBAlgorithm) routing.LBEndpoint { + ep := ctx.Route.LBEndpoints now := time.Now() f := fadeIn( now, @@ -121,10 +117,29 @@ func withFadeIn(rnd *rand.Rand, ctx *routing.LBContext, wi []int, wf []float64, ) if rnd.Float64() < f { - return ctx.Route.LBEndpoints[choice] + return ep[choice] + } + for i := 0; i < len(ep); i++ { + if _, fadingIn := fadeInState(now, ctx.Route.LBFadeInDuration, ep[i].Detected); !fadingIn { + notFadingIndexes = append(notFadingIndexes, i) + } } - return shiftToRemaining(rnd, ctx, wi, wf, now) + switch a := algo.(type) { + case *roundRobin: + return shiftToRemaining(a.rnd, ctx, notFadingIndexes, a.fadingWeights, now) + case *random: + return shiftToRemaining(a.rand, ctx, notFadingIndexes, a.fadingWeights, now) + case *consistentHash: + // If all endpoints are fading, normal consistent hash result + if len(notFadingIndexes) == 0 { + return ep[choice] + } + // otherwise calculate consistent hash again using endpoints which are not fading + return ep[a.chooseConsistentHashEndpoint(ctx, skipFadingEndpoints(notFadingIndexes))] + default: + return ep[choice] + } } type roundRobin struct { @@ -161,7 +176,7 @@ func (r *roundRobin) Apply(ctx *routing.LBContext) routing.LBEndpoint { return ctx.Route.LBEndpoints[r.index] } - return withFadeIn(r.rnd, ctx, r.notFadingIndexes, r.fadingWeights, r.index) + return withFadeIn(r.rnd, ctx, r.notFadingIndexes, r.index, r) } type random struct { @@ -193,7 +208,7 @@ func (r *random) Apply(ctx *routing.LBContext) routing.LBEndpoint { return ctx.Route.LBEndpoints[i] } - return withFadeIn(r.rand, ctx, r.notFadingIndexes, r.fadingWeights, i) + return withFadeIn(r.rand, ctx, r.notFadingIndexes, i, r) } type ( @@ -201,19 +216,29 @@ type ( index int // index of endpoint in endpoint list hash uint64 // hash of endpoint } - consistentHash []endpointHash // list of endpoints sorted by hash value + consistentHash struct { + hashRing []endpointHash // list of endpoints sorted by hash value + rand *rand.Rand + notFadingIndexes []int + } ) -func (ch consistentHash) Len() int { return len(ch) } -func (ch consistentHash) Less(i, j int) bool { return ch[i].hash < ch[j].hash } -func (ch consistentHash) Swap(i, j int) { ch[i], ch[j] = ch[j], ch[i] } +func (ch *consistentHash) Len() int { return len(ch.hashRing) } +func (ch *consistentHash) Less(i, j int) bool { return ch.hashRing[i].hash < ch.hashRing[j].hash } +func (ch *consistentHash) Swap(i, j int) { + ch.hashRing[i], ch.hashRing[j] = ch.hashRing[j], ch.hashRing[i] +} func newConsistentHashInternal(endpoints []string, hashesPerEndpoint int) routing.LBAlgorithm { - ch := consistentHash(make([]endpointHash, hashesPerEndpoint*len(endpoints))) + ch := &consistentHash{ + hashRing: make([]endpointHash, hashesPerEndpoint*len(endpoints)), + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + notFadingIndexes: make([]int, 0, len(endpoints)), + } for i, ep := range endpoints { endpointStartIndex := hashesPerEndpoint * i for j := 0; j < hashesPerEndpoint; j++ { - ch[endpointStartIndex+j] = endpointHash{i, hash(fmt.Sprintf("%s-%d", ep, j))} + ch.hashRing[endpointStartIndex+j] = endpointHash{i, hash(fmt.Sprintf("%s-%d", ep, j))} } } sort.Sort(ch) @@ -229,9 +254,9 @@ func hash(s string) uint64 { } // Returns index in hash ring with the closest hash to key's hash -func (ch consistentHash) searchRing(key string) int { +func (ch *consistentHash) searchRing(key string, skipEndpoint func(int) bool) int { h := hash(key) - i := sort.Search(ch.Len(), func(i int) bool { return ch[i].hash >= h }) + i := sort.Search(ch.Len(), func(i int) bool { return ch.hashRing[i].hash >= h && !skipEndpoint(ch.hashRing[i].index) }) if i == ch.Len() { // rollover i = 0 } @@ -239,9 +264,9 @@ func (ch consistentHash) searchRing(key string) int { } // Returns index of endpoint with closest hash to key's hash -func (ch consistentHash) search(key string) int { - ringIndex := ch.searchRing(key) - return ch[ringIndex].index +func (ch *consistentHash) search(key string, skipEndpoint func(int) bool) int { + ringIndex := ch.searchRing(key, skipEndpoint) + return ch.hashRing[ringIndex].index } func computeLoadAverage(ctx *routing.LBContext) float64 { @@ -254,13 +279,17 @@ func computeLoadAverage(ctx *routing.LBContext) float64 { } // Returns index of endpoint with closest hash to key's hash, which is also below the target load -func (ch consistentHash) boundedLoadSearch(key string, balanceFactor float64, ctx *routing.LBContext) int { - ringIndex := ch.searchRing(key) +// skipEndpoint function is used to skip endpoints we don't want, such as fading endpoints +func (ch *consistentHash) boundedLoadSearch(key string, balanceFactor float64, ctx *routing.LBContext, skipEndpoint func(int) bool) int { + ringIndex := ch.searchRing(key, skipEndpoint) averageLoad := computeLoadAverage(ctx) targetLoad := averageLoad * balanceFactor // Loop round ring, starting at endpoint with closest hash. Stop when we find one whose load is less than targetLoad. for i := 0; i < ch.Len(); i++ { - endpointIndex := ch[ringIndex].index + endpointIndex := ch.hashRing[ringIndex].index + if skipEndpoint(endpointIndex) { + continue + } load := ctx.Route.LBEndpoints[endpointIndex].Metrics.GetInflightRequests() // We know there must be an endpoint whose load <= average load. // Since targetLoad >= average load (balancerFactor >= 1), there must also be an endpoint with load <= targetLoad. @@ -270,15 +299,25 @@ func (ch consistentHash) boundedLoadSearch(key string, balanceFactor float64, ct ringIndex = (ringIndex + 1) % ch.Len() } - return ch[ringIndex].index + return ch.hashRing[ringIndex].index } // Apply implements routing.LBAlgorithm with a consistent hash algorithm. -func (ch consistentHash) Apply(ctx *routing.LBContext) routing.LBEndpoint { +func (ch *consistentHash) Apply(ctx *routing.LBContext) routing.LBEndpoint { if len(ctx.Route.LBEndpoints) == 1 { return ctx.Route.LBEndpoints[0] } + choice := ch.chooseConsistentHashEndpoint(ctx, noSkippedEndpoints) + + if ctx.Route.LBFadeInDuration <= 0 { + return ctx.Route.LBEndpoints[choice] + } + + return withFadeIn(ch.rand, ctx, ch.notFadingIndexes, choice, ch) +} + +func (ch *consistentHash) chooseConsistentHashEndpoint(ctx *routing.LBContext, skipEndpoint func(int) bool) int { key, ok := ctx.Params[ConsistentHashKey].(string) if !ok { key = net.RemoteHost(ctx.Request).String() @@ -286,12 +325,27 @@ func (ch consistentHash) Apply(ctx *routing.LBContext) routing.LBEndpoint { balanceFactor, ok := ctx.Params[ConsistentHashBalanceFactor].(float64) var choice int if !ok { - choice = ch.search(key) + choice = ch.search(key, skipEndpoint) } else { - choice = ch.boundedLoadSearch(key, balanceFactor, ctx) + choice = ch.boundedLoadSearch(key, balanceFactor, ctx, skipEndpoint) + } + + return choice +} + +func skipFadingEndpoints(notFadingEndpoints []int) func(int) bool { + return func(i int) bool { + for _, notFadingEndpoint := range notFadingEndpoints { + if i == notFadingEndpoint { + return false + } + } + return true } +} - return ctx.Route.LBEndpoints[choice] +func noSkippedEndpoints(_ int) bool { + return false } type powerOfRandomNChoices struct { @@ -301,7 +355,7 @@ type powerOfRandomNChoices struct { } // newPowerOfRandomNChoices selects N random backends and picks the one with less outstanding requests. -func newPowerOfRandomNChoices(endpoints []string) routing.LBAlgorithm { +func newPowerOfRandomNChoices([]string) routing.LBAlgorithm { rnd := rand.New(rand.NewSource(time.Now().UnixNano())) // #nosec return &powerOfRandomNChoices{ rand: rnd, diff --git a/loadbalancer/algorithm_test.go b/loadbalancer/algorithm_test.go index 4eae7ed659..6e8dc85d91 100644 --- a/loadbalancer/algorithm_test.go +++ b/loadbalancer/algorithm_test.go @@ -102,7 +102,7 @@ func TestSelectAlgorithm(t *testing.T) { t.Fatal("failed to set the endpoints") } - if _, ok := rr[0].LBAlgorithm.(consistentHash); !ok { + if _, ok := rr[0].LBAlgorithm.(*consistentHash); !ok { t.Fatal("failed to set the right algorithm") } }) @@ -277,8 +277,8 @@ func TestApply(t *testing.T) { func TestConsistentHashSearch(t *testing.T) { apply := func(key string, endpoints []string) string { - ch := newConsistentHash(endpoints).(consistentHash) - return endpoints[ch.search(key)] + ch := newConsistentHash(endpoints).(*consistentHash) + return endpoints[ch.search(key, noSkippedEndpoints)] } endpoints := []string{"http://127.0.0.1:8080", "http://127.0.0.2:8080", "http://127.0.0.3:8080"} @@ -313,7 +313,7 @@ func TestConsistentHashBoundedLoadSearch(t *testing.T) { LBEndpoints: endpoints, }, }})[0] - ch := route.LBAlgorithm.(consistentHash) + ch := route.LBAlgorithm.(*consistentHash) ctx := &routing.LBContext{Request: r, Route: route, Params: map[string]interface{}{ConsistentHashBalanceFactor: 1.25}} noLoad := ch.Apply(ctx) nonBounded := ch.Apply(&routing.LBContext{Request: r, Route: route, Params: map[string]interface{}{}}) @@ -384,7 +384,7 @@ func TestConsistentHashBoundedLoadDistribution(t *testing.T) { LBEndpoints: endpoints, }, }})[0] - ch := route.LBAlgorithm.(consistentHash) + ch := route.LBAlgorithm.(*consistentHash) balanceFactor := 1.25 ctx := &routing.LBContext{Request: r, Route: route, Params: map[string]interface{}{ConsistentHashBalanceFactor: balanceFactor}} @@ -405,8 +405,8 @@ func TestConsistentHashBoundedLoadDistribution(t *testing.T) { func TestConsistentHashKeyDistribution(t *testing.T) { endpoints := []string{"http://10.2.0.1:8080", "http://10.2.0.2:8080", "http://10.2.0.3:8080", "http://10.2.0.4:8080", "http://10.2.0.5:8080", "http://10.2.0.6:8080", "http://10.2.0.7:8080", "http://10.2.0.8:8080", "http://10.2.0.9:8080", "http://10.2.0.10:8080"} - stdDev1hashPerEndpoint := measureStdDev(t, endpoints, 1) - stdDev100HashesPerEndpoint := measureStdDev(t, endpoints, 100) + stdDev1hashPerEndpoint := measureStdDev(endpoints, 1) + stdDev100HashesPerEndpoint := measureStdDev(endpoints, 100) if stdDev100HashesPerEndpoint >= stdDev1hashPerEndpoint { t.Errorf("Standard deviation with 100 hashes per endpoint should be lower than with 1 hash per endpoint. 100 hashes: %f, 1 hash: %f", stdDev100HashesPerEndpoint, stdDev1hashPerEndpoint) @@ -425,17 +425,17 @@ func addInflightRequests(endpoint routing.LBEndpoint, count int) { // Measures how fair the hash ring is to each endpoint. // i.e. Of the possible hashes, how many will go to each endpoint. The lower the standard deviation the better. -func measureStdDev(t *testing.T, endpoints []string, hashesPerEndpoint int) float64 { - ch := newConsistentHashInternal(endpoints, hashesPerEndpoint).(consistentHash) +func measureStdDev(endpoints []string, hashesPerEndpoint int) float64 { + ch := newConsistentHashInternal(endpoints, hashesPerEndpoint).(*consistentHash) ringOwnership := map[int]uint64{} prevPartitionEndHash := uint64(0) - for i := 0; i < len(ch); i++ { - endpointIndex := ch[i].index - partitionEndHash := uint64(ch[i].hash) + for i := 0; i < len(ch.hashRing); i++ { + endpointIndex := ch.hashRing[i].index + partitionEndHash := ch.hashRing[i].hash ringOwnership[endpointIndex] += partitionEndHash - prevPartitionEndHash prevPartitionEndHash = partitionEndHash } - ringOwnership[ch[0].index] += math.MaxUint64 - prevPartitionEndHash + ringOwnership[ch.hashRing[0].index] += math.MaxUint64 - prevPartitionEndHash return stdDeviation(ringOwnership) } diff --git a/loadbalancer/fadein_test.go b/loadbalancer/fadein_test.go index 41f9618816..fcd86bac46 100644 --- a/loadbalancer/fadein_test.go +++ b/loadbalancer/fadein_test.go @@ -2,11 +2,12 @@ package loadbalancer import ( "fmt" + "github.com/zalando/skipper/routing" + "math/rand" + "strconv" "strings" "testing" "time" - - "github.com/zalando/skipper/routing" ) const ( @@ -60,6 +61,7 @@ func testFadeIn( a := algorithm(ep) ctx := &routing.LBContext{ + Params: map[string]interface{}{}, Route: &routing.Route{ LBFadeInDuration: fadeInDuration, LBFadeInExponent: 1, @@ -74,10 +76,12 @@ func testFadeIn( } t.Log("test start", time.Now()) + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) var stats []string stop := time.After(fadeInDuration) func() { for { + ctx.Params[ConsistentHashKey] = strconv.Itoa(rnd.Intn(1000)) ep := a.Apply(ctx) stats = append(stats, ep.Host) select { @@ -165,4 +169,15 @@ func TestFadeIn(t *testing.T) { testFadeIn(t, "random, 7", newRandom, old, 0, 0, 0, 0, 0, 0) testFadeIn(t, "random, 8", newRandom, 0, 0, 0, 0, 0, 0) testFadeIn(t, "random, 9", newRandom, fadeInDuration/2, fadeInDuration/3, fadeInDuration/4) + + testFadeIn(t, "consistent-hash, 0", newConsistentHash, old, old) + testFadeIn(t, "consistent-hash, 1", newConsistentHash, 0, old) + testFadeIn(t, "consistent-hash, 2", newConsistentHash, 0, 0) + testFadeIn(t, "consistent-hash, 3", newConsistentHash, old, 0) + testFadeIn(t, "consistent-hash, 4", newConsistentHash, old, old, old, 0) + testFadeIn(t, "consistent-hash, 5", newConsistentHash, old, old, old, 0, 0, 0) + testFadeIn(t, "consistent-hash, 6", newConsistentHash, old, 0, 0, 0) + testFadeIn(t, "consistent-hash, 7", newConsistentHash, old, 0, 0, 0, 0, 0, 0) + testFadeIn(t, "consistent-hash, 8", newConsistentHash, 0, 0, 0, 0, 0, 0) + testFadeIn(t, "consistent-hash, 9", newConsistentHash, fadeInDuration/2, fadeInDuration/3, fadeInDuration/4) } From 73f898d971659906800d0408ffc50a2decd12e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 11 Oct 2022 10:59:31 +0200 Subject: [PATCH 0083/1047] fix: add test with skipper, kubernetes and redis autoscaling updates (#2086) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: add test with skipper, kubernetes and redis autoscaling updates via kubernetes test: enable changing redis update interval for testing purposes refactor: expose Endpoint and EndpointList refactor: vegetaAttacker into httptest client feature: redis can listen on any port add test conditions Signed-off-by: Sandor Szücs * fix staticcheck unused Signed-off-by: Sandor Szücs * fix: test by waiting until proxy is ready via readinessprobe fix: race by locking client that access state test: cleanup logs Signed-off-by: Sandor Szücs * reduce locking in clusterstate Signed-off-by: Sandor Szücs * fix: deadlock Signed-off-by: Sandor Szücs * refactor: extract funcs for repitive tasks Signed-off-by: Sandor Szücs * refactor: unexpose Endpoint and EndpointList Signed-off-by: Sandor Szücs * revert remove lock as suggested Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- dataclients/kubernetes/clusterstate.go | 23 ++ dataclients/kubernetes/ingressdefinitions.go | 13 + dataclients/kubernetes/kube.go | 17 +- filters/scheduler/fifo_test.go | 118 ++------ net/httptest/client.go | 97 +++++++ skipper.go | 14 +- skipper_test.go | 276 +++++++++++++++++++ 7 files changed, 451 insertions(+), 107 deletions(-) create mode 100644 net/httptest/client.go diff --git a/dataclients/kubernetes/clusterstate.go b/dataclients/kubernetes/clusterstate.go index 6a4c6645fc..5f2a1973b0 100644 --- a/dataclients/kubernetes/clusterstate.go +++ b/dataclients/kubernetes/clusterstate.go @@ -72,6 +72,29 @@ func (state *clusterState) GetEndpointsByService(namespace, name, protocol strin return targets } +func (state *clusterState) GetEndpointsByName(namespace, name, protocol string) []string { + epID := endpointID{ + ResourceID: newResourceID(namespace, name), + Protocol: protocol, + } + state.mu.Lock() + defer state.mu.Unlock() + if cached, ok := state.cachedEndpoints[epID]; ok { + return cached + } + + ep, ok := state.endpoints[epID.ResourceID] + if !ok { + return nil + } + + targets := ep.targets(protocol) + sort.Strings(targets) + state.cachedEndpoints[epID] = targets + return targets + +} + func (state *clusterState) GetEndpointsByTarget(namespace, name, protocol string, target *definitions.BackendPort) []string { epID := endpointID{ ResourceID: newResourceID(namespace, name), diff --git a/dataclients/kubernetes/ingressdefinitions.go b/dataclients/kubernetes/ingressdefinitions.go index 76fb0631ba..a1349fe01b 100644 --- a/dataclients/kubernetes/ingressdefinitions.go +++ b/dataclients/kubernetes/ingressdefinitions.go @@ -138,6 +138,19 @@ func (ep endpoint) targetsByServiceTarget(protocol string, serviceTarget *defini return nil } +func (ep endpoint) targets(protocol string) []string { + result := make([]string, 0) + for _, s := range ep.Subsets { + for _, p := range s.Ports { + for _, a := range s.Addresses { + result = append(result, formatEndpoint(a, p, protocol)) + } + } + } + + return result +} + type subset struct { Addresses []*address `json:"addresses"` Ports []*port `json:"ports"` diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index a389a47430..904c39a46d 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -7,11 +7,11 @@ import ( "os" "regexp" "strings" + "sync" "text/template" "time" log "github.com/sirupsen/logrus" - "github.com/zalando/skipper/dataclients/kubernetes/definitions" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/secrets/certregistry" @@ -219,6 +219,7 @@ type Options struct { // Client is a Skipper DataClient implementation used to create routes based on Kubernetes Ingress settings. type Client struct { + mu sync.Mutex ClusterClient *clusterClient ingress *ingress routeGroups *routeGroups @@ -363,11 +364,14 @@ func mapRoutes(r []*eskip.Route) map[string]*eskip.Route { } func (c *Client) loadAndConvert() ([]*eskip.Route, error) { + c.mu.Lock() state, err := c.ClusterClient.fetchClusterState() if err != nil { + c.mu.Unlock() return nil, err } c.state = state + c.mu.Unlock() defaultFilters := c.fetchDefaultFilterConfigs() @@ -525,16 +529,13 @@ func (c *Client) fetchDefaultFilterConfigs() defaultFilters { return filters } -func (c *Client) GetEndpointAddresses(ns, name string, port int) []string { +func (c *Client) GetEndpointAddresses(ns, name string) []string { + c.mu.Lock() + defer c.mu.Unlock() if c.state == nil { return nil } - - addrs := c.state.GetEndpointsByTarget(ns, name, "TCP", &definitions.BackendPort{ - Value: port, - }) - - return addrs + return c.state.GetEndpointsByName(ns, name, "TCP") } func compareStringList(a, b []string) []string { diff --git a/filters/scheduler/fifo_test.go b/filters/scheduler/fifo_test.go index 3565b77b46..3aa3142261 100644 --- a/filters/scheduler/fifo_test.go +++ b/filters/scheduler/fifo_test.go @@ -3,9 +3,8 @@ package scheduler import ( "fmt" "io" - "log" "net/http" - "net/http/httptest" + stdlibhttptest "net/http/httptest" "net/url" "testing" "time" @@ -13,12 +12,11 @@ import ( "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/metrics/metricstest" + "github.com/zalando/skipper/net/httptest" "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/routing" "github.com/zalando/skipper/routing/testdataclient" "github.com/zalando/skipper/scheduler" - - vegeta "github.com/tsenart/vegeta/lib" ) func TestCreateFifoFilter(t *testing.T) { @@ -236,7 +234,7 @@ func TestFifo(t *testing.T) { fr := make(filters.Registry) fr.Register(fs) - backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + backend := stdlibhttptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(tt.backendTime) w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) @@ -281,7 +279,7 @@ func TestFifo(t *testing.T) { }) defer pr.Close() - ts := httptest.NewServer(pr) + ts := stdlibhttptest.NewServer(pr) defer ts.Close() reqURL, err := url.Parse(ts.URL) @@ -297,24 +295,24 @@ func TestFifo(t *testing.T) { t.Fatalf("Failed to get valid response from endpoint: %d", rsp.StatusCode) } - va := newVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) + va := httptest.NewVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) va.Attack(io.Discard, 1*time.Second, tt.name) - t.Logf("Success [0..1]: %0.2f", va.metrics.Success) - t.Logf("requests: %d", va.metrics.Requests) - got := va.metrics.Success * float64(va.metrics.Requests) - want := tt.wantOkRate * float64(va.metrics.Requests) + t.Logf("Success [0..1]: %0.2f", va.Success()) + t.Logf("requests: %d", va.TotalRequests()) + got := va.TotalSuccess() + want := tt.wantOkRate * float64(va.TotalRequests()) if got < want { t.Fatalf("OK rate too low got 0 { t.Fatal("no OK") } if !ok && tt.wantOkRate == 0 { - count499, ok := va.metrics.StatusCodes["0"] - if !ok || va.metrics.Requests != uint64(count499) { - t.Fatalf("want all 499 client cancel but %d != %d", va.metrics.Requests, count499) + count499, ok := va.CountStatus(0) + if !ok || va.TotalRequests() != uint64(count499) { + t.Fatalf("want all 499 client cancel but %d != %d", va.TotalRequests(), count499) } } if float64(countOK) < want { @@ -399,7 +397,7 @@ func TestConstantRouteUpdatesFifo(t *testing.T) { fr := make(filters.Registry) fr.Register(fs) - backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + backend := stdlibhttptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(tt.backendTime) w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) @@ -429,7 +427,7 @@ func TestConstantRouteUpdatesFifo(t *testing.T) { }) defer pr.Close() - ts := httptest.NewServer(pr) + ts := stdlibhttptest.NewServer(pr) defer ts.Close() reqURL, err := url.Parse(ts.URL) @@ -467,25 +465,25 @@ func TestConstantRouteUpdatesFifo(t *testing.T) { }(quit, tt.updateRate, doc, newDoc) - va := newVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) + va := httptest.NewVegetaAttacker(reqURL.String(), tt.freq, tt.per, tt.clientTimeout) va.Attack(io.Discard, 1*time.Second, tt.name) quit <- struct{}{} - t.Logf("Success [0..1]: %0.2f", va.metrics.Success) - t.Logf("requests: %d", va.metrics.Requests) - got := va.metrics.Success * float64(va.metrics.Requests) - want := tt.wantOkRate * float64(va.metrics.Requests) + t.Logf("Success [0..1]: %0.2f", va.Success()) + t.Logf("requests: %d", va.TotalRequests()) + got := va.TotalSuccess() + want := tt.wantOkRate * float64(va.TotalRequests()) if got < want { t.Fatalf("OK rate too low got 0 { - t.Fatal("no OK") + t.Fatalf("no OK") } if !ok && tt.wantOkRate == 0 { - count499, ok := va.metrics.StatusCodes["0"] - if !ok || va.metrics.Requests != uint64(count499) { - t.Fatalf("want all 499 client cancel but %d != %d", va.metrics.Requests, count499) + count499, ok := va.CountStatus(0) + if !ok || va.TotalRequests() != uint64(count499) { + t.Fatalf("want all 499 client cancel but %d != %d", va.TotalRequests(), count499) } } if float64(countOK) < want { @@ -494,69 +492,3 @@ func TestConstantRouteUpdatesFifo(t *testing.T) { }) } } - -type vegetaAttacker struct { - attacker *vegeta.Attacker - metrics *vegeta.Metrics - rate *vegeta.Rate - targeter vegeta.Targeter -} - -func newVegetaAttacker(url string, freq int, per time.Duration, timeout time.Duration) *vegetaAttacker { - atk := vegeta.NewAttacker( - vegeta.Connections(10), - vegeta.H2C(false), - vegeta.HTTP2(false), - vegeta.KeepAlive(true), - vegeta.MaxWorkers(10), - vegeta.Redirects(0), - vegeta.Timeout(timeout), - vegeta.Workers(5), - ) - - tr := vegeta.NewStaticTargeter(vegeta.Target{Method: "GET", URL: url}) - rate := vegeta.Rate{Freq: freq, Per: per} - - m := vegeta.Metrics{ - Histogram: &vegeta.Histogram{ - Buckets: []time.Duration{ - 0, - 10 * time.Microsecond, - 50 * time.Microsecond, - 100 * time.Microsecond, - 500 * time.Microsecond, - 1 * time.Millisecond, - 5 * time.Millisecond, - 10 * time.Millisecond, - 25 * time.Millisecond, - 50 * time.Millisecond, - 100 * time.Millisecond, - 1000 * time.Millisecond, - }, - }, - } - - return &vegetaAttacker{ - attacker: atk, - metrics: &m, - rate: &rate, - targeter: tr, - } -} - -func (atk *vegetaAttacker) Attack(w io.Writer, d time.Duration, name string) { - for res := range atk.attacker.Attack(atk.targeter, atk.rate, d, name) { - if res == nil { - continue - } - atk.metrics.Add(res) - //metrics.Latencies.Add(res.Latency) - } - atk.metrics.Close() - // logrus.Info("histogram reporter:") - // histReporter := vegeta.NewHistogramReporter(atk.metrics.Histogram) - // histReporter.Report(os.Stdout) - log.Print("text reporter:") - reporter := vegeta.NewTextReporter(atk.metrics) - reporter.Report(w) -} diff --git a/net/httptest/client.go b/net/httptest/client.go new file mode 100644 index 0000000000..9b6b44f06b --- /dev/null +++ b/net/httptest/client.go @@ -0,0 +1,97 @@ +package httptest + +import ( + "io" + "log" + "strconv" + "time" + + vegeta "github.com/tsenart/vegeta/lib" +) + +type VegetaAttacker struct { + attacker *vegeta.Attacker + metrics *vegeta.Metrics + rate *vegeta.Rate + targeter vegeta.Targeter +} + +func NewVegetaAttacker(url string, freq int, per time.Duration, timeout time.Duration) *VegetaAttacker { + atk := vegeta.NewAttacker( + vegeta.Connections(10), + vegeta.H2C(false), + vegeta.HTTP2(false), + vegeta.KeepAlive(true), + vegeta.MaxWorkers(10), + vegeta.Redirects(0), + vegeta.Timeout(timeout), + vegeta.Workers(5), + ) + + tr := vegeta.NewStaticTargeter(vegeta.Target{Method: "GET", URL: url}) + rate := vegeta.Rate{Freq: freq, Per: per} + + m := vegeta.Metrics{ + Histogram: &vegeta.Histogram{ + Buckets: []time.Duration{ + 0, + 10 * time.Microsecond, + 50 * time.Microsecond, + 100 * time.Microsecond, + 500 * time.Microsecond, + 1 * time.Millisecond, + 5 * time.Millisecond, + 10 * time.Millisecond, + 25 * time.Millisecond, + 50 * time.Millisecond, + 100 * time.Millisecond, + 1000 * time.Millisecond, + }, + }, + } + + return &VegetaAttacker{ + attacker: atk, + metrics: &m, + rate: &rate, + targeter: tr, + } +} + +func (atk *VegetaAttacker) Attack(w io.Writer, d time.Duration, name string) { + for res := range atk.attacker.Attack(atk.targeter, atk.rate, d, name) { + if res == nil { + continue + } + atk.metrics.Add(res) + //metrics.Latencies.Add(res.Latency) + } + atk.metrics.Close() + // logrus.Info("histogram reporter:") + // histReporter := vegeta.NewHistogramReporter(atk.metrics.Histogram) + // histReporter.Report(os.Stdout) + log.Print("text reporter:") + reporter := vegeta.NewTextReporter(atk.metrics) + reporter.Report(w) +} + +func (atk *VegetaAttacker) Metrics() *vegeta.Metrics { + return atk.metrics +} + +func (atk *VegetaAttacker) Success() float64 { + return atk.metrics.Success +} + +func (atk *VegetaAttacker) TotalRequests() uint64 { + return atk.metrics.Requests +} + +func (atk *VegetaAttacker) TotalSuccess() float64 { + return atk.metrics.Success * float64(atk.metrics.Requests) +} + +func (atk *VegetaAttacker) CountStatus(code int) (int, bool) { + cnt, ok := atk.metrics.StatusCodes[strconv.Itoa(code)] + return cnt, ok +} diff --git a/skipper.go b/skipper.go index 9254d7e6ef..50086cad79 100644 --- a/skipper.go +++ b/skipper.go @@ -74,6 +74,7 @@ const DefaultPluginDir = "./plugins" type testOptions struct { redisConnMetricsInterval time.Duration + redisUpdateInterval time.Duration } // Options to start skipper. @@ -268,7 +269,7 @@ type Options struct { // KubernetesRedisServiceName to be used to lookup ring shards dynamically KubernetesRedisServiceName string - // KubernetesRedisServicePort to be used to lookup ring shards dynamically + // *DEPRECATED* KubernetesRedisServicePort is not used anymore KubernetesRedisServicePort int // *DEPRECATED* API endpoint of the Innkeeper service, storing route definitions. @@ -1273,12 +1274,12 @@ func findKubernetesDataclient(dataClients []routing.DataClient) *kubernetes.Clie return kdc } -func getRedisUpdaterFunc(namespace, name string, port int, kdc *kubernetes.Client) func() []string { +func getRedisUpdaterFunc(namespace, name string, kdc *kubernetes.Client) func() []string { return func() []string { // TODO(sszuecs): make sure kubernetes dataclient is already initialized and // has polled the data once or kdc.GetEndpointAdresses should be blocking // call to kubernetes API - a := kdc.GetEndpointAddresses(namespace, name, port) + a := kdc.GetEndpointAddresses(namespace, name) log.Debugf("Redis updater called and found %d redis endpoints", len(a)) for i := 0; i < len(a); i++ { a[i] = strings.TrimPrefix(a[i], "TCP://") @@ -1501,6 +1502,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { MinIdleConns: o.SwarmRedisMinIdleConns, MaxIdleConns: o.SwarmRedisMaxIdleConns, ConnMetricsInterval: o.redisConnMetricsInterval, + UpdateInterval: o.redisUpdateInterval, Tracer: tracer, Log: log.New(), } @@ -1550,12 +1552,12 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { } // in case we have kubernetes dataclient and we can detect redis instances, we patch redisOptions - if redisOptions != nil && o.KubernetesRedisServiceNamespace != "" && o.KubernetesRedisServiceName != "" && o.KubernetesRedisServicePort > 0 { - log.Infof("Use endpoints %s/%s :%d to fetch updated redis shards", o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName, o.KubernetesRedisServicePort) + if redisOptions != nil && o.KubernetesRedisServiceNamespace != "" && o.KubernetesRedisServiceName != "" { + log.Infof("Use endpoints %s/%s to fetch updated redis shards", o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName) kdc := findKubernetesDataclient(dataClients) if kdc != nil { - redisOptions.AddrUpdater = getRedisUpdaterFunc(o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName, o.KubernetesRedisServicePort, kdc) + redisOptions.AddrUpdater = getRedisUpdaterFunc(o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName, kdc) } else { log.Errorf("Failed to find kubernetes dataclient, but redis shards should be get by kubernetes svc %s/%s", o.KubernetesRedisServiceNamespace, o.KubernetesRedisServiceName) } diff --git a/skipper_test.go b/skipper_test.go index 0f7ad653cd..e4b4228a9a 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -1,10 +1,14 @@ package skipper import ( + "bytes" "crypto/tls" + "fmt" "io" "net" "net/http" + stdlibhttptest "net/http/httptest" + "net/url" "os" "syscall" "testing" @@ -12,13 +16,23 @@ import ( log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/dataclients/kubernetes/kubernetestest" "github.com/zalando/skipper/dataclients/routestring" "github.com/zalando/skipper/filters" + flog "github.com/zalando/skipper/filters/accesslog" "github.com/zalando/skipper/filters/builtin" + fscheduler "github.com/zalando/skipper/filters/scheduler" + "github.com/zalando/skipper/loadbalancer" + "github.com/zalando/skipper/metrics/metricstest" + "github.com/zalando/skipper/net/httptest" + "github.com/zalando/skipper/net/redistest" "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/ratelimit" "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/routing/testdataclient" + "github.com/zalando/skipper/scheduler" "github.com/zalando/skipper/secrets/certregistry" + "github.com/zalando/skipper/tracing/tracingtest" "github.com/stretchr/testify/require" ) @@ -342,3 +356,265 @@ func Example_ratelimitRegistryBinding() { log.Fatal(Run(o)) // Example functions without output comments are compiled but not executed } + +func TestConcurrentKubernetesClusterStateAccess(t *testing.T) { + redisPortFmt := ` - port: %s + protocol: TCP +` + redisEpSpecFmt := ` +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: skipper-ingress-redis + name: redis + namespace: skipper +subsets: +- addresses: + - hostname: redis-%d.skipper.cluster.local + ip: %s + ports: +` + + kubeSpec := ` +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: target +spec: + backends: + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: /test + filters: + - disableAccessLog() + - clusterRatelimit("foo", 1, "1s") + - status(200) + - inlineContent("OK") +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp +spec: + hosts: + - example.org + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + defaultBackends: + - backendName: myapp +--- +apiVersion: v1 +kind: Service +metadata: + name: myapp +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: myapp +subsets: +- addresses: + - ip: 10.2.4.8 + - ip: 10.2.4.16 + ports: + - port: 80 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: skipper-ingress-redis + name: redis + namespace: skipper +spec: + clusterIP: None + ports: + - port: 6379 + protocol: TCP + targetPort: 6379 + selector: + application: skipper-ingress-redis + type: ClusterIP +` + + redis1, done1 := redistest.NewTestRedis(t) + redis2, done2 := redistest.NewTestRedis(t) + redis3, done3 := redistest.NewTestRedis(t) + defer done1() + defer done2() + defer done3() + host1, port1, err := net.SplitHostPort(redis1) + if err != nil { + t.Fatalf("Failed to SplitHostPort: %v", err) + } + _, port2, err := net.SplitHostPort(redis2) + if err != nil { + t.Fatalf("Failed to SplitHostPort: %v", err) + } + _, port3, err := net.SplitHostPort(redis3) + if err != nil { + t.Fatalf("Failed to SplitHostPort: %v", err) + } + + // apiserver1 + specFmt := redisEpSpecFmt + redisPortFmt + redisSpec1 := fmt.Sprintf(specFmt, 0, host1, port1) + apiServer1, u1, err := createApiserver(kubeSpec + redisSpec1) + if err != nil { + t.Fatalf("Failed to start apiserver1: %v", err) + } + defer apiServer1.Close() + + // apiserver2 + specFmt += redisPortFmt + redisSpec2 := fmt.Sprintf(specFmt, 0, host1, port1, port2) + apiServer2, u2, err := createApiserver(kubeSpec + redisSpec2) + if err != nil { + t.Fatalf("Failed to start apiserver2: %v", err) + } + defer apiServer2.Close() + + // apiserver3 + specFmt += redisPortFmt + redisSpec3 := fmt.Sprintf(specFmt, 0, host1, port1, port2, port3) + apiServer3, u3, err := createApiserver(kubeSpec + redisSpec3) + if err != nil { + t.Fatalf("Failed to start apiserver3: %v", err) + } + defer apiServer3.Close() + + // create skipper as LB to kube-apiservers + fr := createFilterRegistry(fscheduler.NewFifo(), flog.NewEnableAccessLog()) + metrics := &metricstest.MockMetrics{} + reg := scheduler.RegistryWith(scheduler.Options{ + Metrics: metrics, + EnableRouteFIFOMetrics: true, + }) + defer reg.Close() + + docFmt := ` +r1: * -> enableAccessLog(4,5) -> fifo(100,100,"3s") -> ; +r2: PathRegexp("/endpoints") -> enableAccessLog(2,4,5) -> fifo(100,100,"3s") -> ; +` + docApiserver := fmt.Sprintf(docFmt, u1.String(), u2.String(), u3.String(), u1.String(), u2.String(), u3.String()) + dc, err := testdataclient.NewDoc(docApiserver) + if err != nil { + t.Fatalf("Failed to create testdataclient: %v", err) + } + + // create LB in front of apiservers to be able to switch the data served by apiserver + ro := routing.Options{ + SignalFirstLoad: true, + FilterRegistry: fr, + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{ + loadbalancer.NewAlgorithmProvider(), + reg, + }, + SuppressLogs: true, + } + rt := routing.New(ro) + defer rt.Close() + <-rt.FirstLoad() + tracer := &tracingtest.Tracer{} + pr := proxy.WithParams(proxy.Params{ + Routing: rt, + OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + }) + defer pr.Close() + lb := stdlibhttptest.NewServer(pr) + defer lb.Close() + + // run skipper proxy that we want to test + o := Options{ + Address: ":9090", + EnableRatelimiters: true, + EnableSwarm: true, + Kubernetes: true, + KubernetesURL: lb.URL, + KubernetesRedisServiceNamespace: "skipper", + KubernetesRedisServiceName: "redis", + KubernetesIngressV1: true, + KubernetesHealthcheck: true, + SourcePollTimeout: 1500 * time.Millisecond, + WaitFirstRouteLoad: true, + ClusterRatelimitMaxGroupShards: 2, + SwarmRedisDialTimeout: 100 * time.Millisecond, + SuppressRouteUpdateLogs: false, + SupportListener: ":9091", + testOptions: testOptions{ + redisUpdateInterval: time.Second, + }, + } + + sigs := make(chan os.Signal, 1) + go run(o, sigs, nil) + + // wait for proxy being ready + ready := false + for !ready { + rsp, _ := http.DefaultClient.Get("http://localhost:9090/kube-system/healthz") + if rsp != nil && rsp.StatusCode == 200 { + ready = true + } + time.Sleep(100 * time.Millisecond) + } + + rate := 10 + sec := 5 + va := httptest.NewVegetaAttacker("http://localhost:9090/test", rate, time.Second, time.Second) + va.Attack(io.Discard, time.Duration(sec)*time.Second, "mytest") + t.Logf("Success [0..1]: %0.2f", va.Success()) + + if successRate := va.Success(); successRate < 0.1 && successRate > 0.5 { + t.Fatalf("Test should have a success rate between %0.2f < %0.2f < %0.2f", 0.1, successRate, 0.5) + } + if reqCount := va.TotalRequests(); reqCount < uint64(rate*sec) { + t.Fatalf("Test should run %d requests got: %d", uint64(rate*sec), reqCount) + } + countOK, ok := va.CountStatus(http.StatusOK) + if countOK == 0 { + t.Fatalf("Some requests should have passed: %d %v", countOK, ok) + } + + countLimited, ok := va.CountStatus(http.StatusTooManyRequests) + if !ok || countLimited < countOK { + t.Fatalf("count TooMany should be higher than OKs: %d < %d: %v", countLimited, countOK, ok) + } + + sigs <- syscall.SIGTERM +} + +func createApiserver(spec string) (*stdlibhttptest.Server, *url.URL, error) { + api, err := kubernetestest.NewAPI(kubernetestest.TestAPIOptions{}, bytes.NewBufferString(spec)) + if err != nil { + return nil, nil, err + } + apiServer := stdlibhttptest.NewServer(api) + u, err := url.Parse(apiServer.URL) + return apiServer, u, err +} + +func createFilterRegistry(specs ...filters.Spec) filters.Registry { + fr := make(filters.Registry) + for _, spec := range specs { + fr.Register(spec) + } + return fr +} From 75ade3b8e319cf5bff88e30a8f01bc95b472023f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 11 Oct 2022 11:02:33 +0200 Subject: [PATCH 0084/1047] Test/secrets (#2090) * add more encrype/decrypt tests Signed-off-by: Sandor Szuecs * better error and log Signed-off-by: Sandor Szuecs * add failing test cases for encrypt/decrypt Signed-off-by: Sandor Szuecs Signed-off-by: Sandor Szuecs --- secrets/encrypter.go | 4 +- secrets/encrypter_test.go | 120 ++++++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 20 deletions(-) diff --git a/secrets/encrypter.go b/secrets/encrypter.go index 654bf73129..e00aed12bb 100644 --- a/secrets/encrypter.go +++ b/secrets/encrypter.go @@ -156,7 +156,7 @@ func (e *Encrypter) RefreshCiphers() error { func (e *Encrypter) runCipherRefresher(refreshInterval time.Duration) error { err := e.RefreshCiphers() if err != nil { - return err + return fmt.Errorf("failed to refresh ciphers: %w", err) } go func() { ticker := time.NewTicker(refreshInterval) @@ -173,7 +173,7 @@ func (e *Encrypter) runCipherRefresher(refreshInterval time.Duration) error { log.Debug("started refresh of ciphers") err := e.RefreshCiphers() if err != nil { - log.Error("failed to refresh the ciphers") + log.Errorf("failed to refresh the ciphers: %v", err) } log.Debug("finished refresh of ciphers") } diff --git a/secrets/encrypter_test.go b/secrets/encrypter_test.go index 6be161f98b..cc1e66ba0b 100644 --- a/secrets/encrypter_test.go +++ b/secrets/encrypter_test.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "reflect" + "strconv" + "strings" "sync" "testing" "time" @@ -12,35 +14,117 @@ import ( ) type testingSecretSource struct { - getCount int - secretKey string + getCount int + secretKey string + failingGetSecret bool + changingGetSecret bool } func (s *testingSecretSource) GetSecret() ([][]byte, error) { + if s.failingGetSecret { + return nil, fmt.Errorf("failed to get secret") + } + s.getCount++ + + if s.changingGetSecret { + return [][]byte{[]byte(s.secretKey + strconv.Itoa(s.getCount))}, nil + } return [][]byte{[]byte(s.secretKey)}, nil } +func (s *testingSecretSource) SetSecret(key string) { + s.secretKey = key +} + func TestEncryptDecrypt(t *testing.T) { - enc := &Encrypter{ - secretSource: &testingSecretSource{secretKey: "abc"}, - } - enc.RefreshCiphers() + for _, tt := range []struct { + name string + secretKey string + plaintext string + secSrc *testingSecretSource + wantErr bool + wantErr2 bool + }{ + { + name: "shorter secret than plaintext", + secretKey: "abc", + plaintext: "helloworld", + secSrc: &testingSecretSource{}, + wantErr: false, + }, + { + name: "long plaintext", + secretKey: "mykey", + plaintext: strings.Repeat("hello", 2000), + secSrc: &testingSecretSource{}, + wantErr: false, + }, + { + name: "long secret", + secretKey: strings.Repeat("abcdefghijklmn", 2000), + plaintext: "hello", + secSrc: &testingSecretSource{}, + wantErr: false, + }, + { + name: "long plaintext and secret", + secretKey: strings.Repeat("abcdefghijklmn", 2000), + plaintext: strings.Repeat("helloworld", 5000), + secSrc: &testingSecretSource{}, + wantErr: false, + }, + { + name: "failing refresh", + secretKey: "abcdefghijklmn", + plaintext: "hello", + secSrc: &testingSecretSource{ + failingGetSecret: true, + }, + wantErr: true, + wantErr2: true, + }, + { + name: "changing secret", + secretKey: "abcdefghijklmn", + plaintext: "hello", + secSrc: &testingSecretSource{ + changingGetSecret: true, + }, + wantErr2: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + tt.secSrc.SetSecret(tt.secretKey) + enc := &Encrypter{ + secretSource: tt.secSrc, + } + enc.RefreshCiphers() + + plain := []byte(tt.plaintext) + b, err := enc.Encrypt(plain) + if err != nil && !tt.wantErr { + t.Errorf("failed to encrypt data block: %v", err) + } else if tt.wantErr && err == nil { + t.Fatal("wantErr while encrypting, but got no error") + } - plaintext := "helloworld" - plain := []byte(plaintext) - b, err := enc.Encrypt(plain) - if err != nil { - t.Errorf("failed to encrypt data block: %v", err) - } - decenc, err := enc.Decrypt(b) - if err != nil { - t.Errorf("failed to decrypt data block: %v", err) - } - if string(decenc) != plaintext { - t.Errorf("decrypted plaintext is not the same as plaintext: %s", string(decenc)) + enc.RefreshCiphers() + + decenc, err := enc.Decrypt(b) + if err != nil && !tt.wantErr2 { + t.Errorf("failed to decrypt data block: %v", err) + } else if tt.wantErr2 && err == nil { + t.Fatal("wantErr while decrypting, but got no error") + } + + if string(decenc) != tt.plaintext && !tt.wantErr2 { + t.Errorf("decrypted plaintext is not the same as plaintext: %s", string(decenc)) + } + }) } } + func TestCipherRefreshing(t *testing.T) { d := 1 * time.Second sleepD := 4 * d From caa30a919296b9712402b51d886165cfb8a948ca Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Tue, 11 Oct 2022 12:04:41 +0300 Subject: [PATCH 0085/1047] Added some performance tests for cachedEndpoints (#2092) Signed-off-by: Roman Zavodskikh Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- dataclients/kubernetes/clusterstate_test.go | 87 +++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 dataclients/kubernetes/clusterstate_test.go diff --git a/dataclients/kubernetes/clusterstate_test.go b/dataclients/kubernetes/clusterstate_test.go new file mode 100644 index 0000000000..3fbf4078ac --- /dev/null +++ b/dataclients/kubernetes/clusterstate_test.go @@ -0,0 +1,87 @@ +package kubernetes + +import ( + "strconv" + "testing" + + "github.com/zalando/skipper/dataclients/kubernetes/definitions" +) + +var dummy2 []string + +func benchmarkCachedEndpoints(b *testing.B, n int) { + endpoints := make(map[definitions.ResourceID]*endpoint) + for i := 0; i < n; i++ { + name := "foo-" + strconv.Itoa(i) + rid := newResourceID("default", name) + ep := &endpoint{ + Meta: &definitions.Metadata{ + Namespace: "default", + Name: name, + Labels: map[string]string{}, + }, + Subsets: []*subset{ + { + Addresses: []*address{ + {"192.168.0.1", "node1"}, + {"192.168.0.2", "node2"}, + {"192.168.0.3", "node3"}, + {"192.168.0.4", "node4"}, + {"192.168.0.5", "node5"}, + {"192.168.0.6", "node6"}, + {"192.168.0.7", "node7"}, + {"192.168.0.8", "node8"}, + {"192.168.0.9", "node9"}, + {"192.168.0.10", "node10"}, + {"192.168.0.11", "node11"}, + }, + Ports: []*port{ + {"ssh", 22, "TCP"}, + {"http", 80, "TCP"}, + }, + }, + }, + } + endpoints[rid] = ep + } + + cs := &clusterState{ + ingresses: nil, + ingressesV1: nil, + routeGroups: nil, + services: nil, + endpoints: endpoints, + secrets: nil, + cachedEndpoints: make(map[endpointID][]string), + } + + b.ResetTimer() + dummy := []string{} + for i := 0; i < b.N; i++ { + dummy = cs.GetEndpointsByTarget("default", "foo-0", "TCP", &definitions.BackendPort{}) + } + dummy2 = dummy +} + +func BenchmarkCachedEndpoint(b *testing.B) { + for _, tt := range []struct { + name string + endpointsNumber int + }{ + { + name: "1M Endpoints", + endpointsNumber: 1_000_000, + }, + { + name: "10K Endpoints", + endpointsNumber: 10_000, + }, + { + name: "3 Endpoints", + endpointsNumber: 3, + }} { + b.Run(tt.name, func(b *testing.B) { + benchmarkCachedEndpoints(b, tt.endpointsNumber) + }) + } +} From 7ac768a3370f46bc417437f5a9f5f64160a039ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 11 Oct 2022 11:05:03 +0200 Subject: [PATCH 0086/1047] test: query predicate add more error cases (#2091) Signed-off-by: Sandor Szuecs Signed-off-by: Sandor Szuecs --- predicates/query/query_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/predicates/query/query_test.go b/predicates/query/query_test.go index f7bec3e796..0cc77974f4 100644 --- a/predicates/query/query_test.go +++ b/predicates/query/query_test.go @@ -41,6 +41,21 @@ func TestQueryArgs(t *testing.T) { []interface{}{"key", "value", `\`}, 0, true, + }, { + "invalid type key", + []interface{}{5, "value"}, + 0, + true, + }, { + "invalid type value", + []interface{}{"key", 5}, + 0, + true, + }, { + "invalid regexp string", + []interface{}{"key", `\`}, + 0, + true, }} { func() { p, err := New().Create(ti.args) From 044a0539ed6c14b69506d7881770cd175d56bf38 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 12 Oct 2022 12:18:08 +0200 Subject: [PATCH 0087/1047] gosec: ignore use of weak random number generator (#2095) Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- loadbalancer/algorithm.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/loadbalancer/algorithm.go b/loadbalancer/algorithm.go index 91ac6de97a..4e49d5faf8 100644 --- a/loadbalancer/algorithm.go +++ b/loadbalancer/algorithm.go @@ -230,9 +230,10 @@ func (ch *consistentHash) Swap(i, j int) { } func newConsistentHashInternal(endpoints []string, hashesPerEndpoint int) routing.LBAlgorithm { + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) // #nosec ch := &consistentHash{ hashRing: make([]endpointHash, hashesPerEndpoint*len(endpoints)), - rand: rand.New(rand.NewSource(time.Now().UnixNano())), + rand: rnd, notFadingIndexes: make([]int, 0, len(endpoints)), } for i, ep := range endpoints { From 29ae54f26f24c7b0afab503cbb613101dbe1f0ff Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 12 Oct 2022 23:15:34 +0200 Subject: [PATCH 0088/1047] Allow to selectively disable filters (#2096) Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- config/config.go | 4 ++++ config/config_test.go | 1 + filters/builtin/builtin.go | 19 +++++++++++-------- skipper.go | 37 +++++++++++++++++++++++++++++-------- skipper_test.go | 23 +++++++++++++++++++++++ 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/config/config.go b/config/config.go index ee0e7b821e..34de61c23c 100644 --- a/config/config.go +++ b/config/config.go @@ -130,6 +130,7 @@ type Config struct { InlineRoutes string `yaml:"inline-routes"` AppendFilters *defaultFiltersFlags `yaml:"default-filters-append"` PrependFilters *defaultFiltersFlags `yaml:"default-filters-prepend"` + DisabledFilters *listFlag `yaml:"disabled-filters"` EditRoute *routeChangerConfig `yaml:"edit-route"` CloneRoute *routeChangerConfig `yaml:"clone-route"` SourcePollTimeout int64 `yaml:"source-poll-timeout"` @@ -289,6 +290,7 @@ func NewConfig() *Config { cfg.SwarmRedisURLs = commaListFlag() cfg.AppendFilters = &defaultFiltersFlags{} cfg.PrependFilters = &defaultFiltersFlags{} + cfg.DisabledFilters = commaListFlag() cfg.CloneRoute = &routeChangerConfig{} cfg.EditRoute = &routeChangerConfig{} cfg.KubernetesEastWestRangeDomains = commaListFlag() @@ -400,6 +402,7 @@ func NewConfig() *Config { flag.Int64Var(&cfg.SourcePollTimeout, "source-poll-timeout", int64(3000), "polling timeout of the routing data sources, in milliseconds") flag.Var(cfg.AppendFilters, "default-filters-append", "set of default filters to apply to append to all filters of all routes") flag.Var(cfg.PrependFilters, "default-filters-prepend", "set of default filters to apply to prepend to all filters of all routes") + flag.Var(cfg.DisabledFilters, "disabled-filters", "comma separated list of filters unavailable for use") flag.Var(cfg.EditRoute, "edit-route", "match and edit filters and predicates of all routes") flag.Var(cfg.CloneRoute, "clone-route", "clone all matching routes and replace filters and predicates of all matched routes") flag.BoolVar(&cfg.WaitFirstRouteLoad, "wait-first-route-load", false, "prevent starting the listener before the first batch of routes were loaded") @@ -766,6 +769,7 @@ func (c *Config) ToOptions() skipper.Options { Prepend: c.PrependFilters.filters, Append: c.AppendFilters.filters, }, + DisabledFilters: c.DisabledFilters.values, CloneRoute: eskip.NewClone(c.CloneRoute.Reg, c.CloneRoute.Repl), EditRoute: eskip.NewEditor(c.EditRoute.Reg, c.EditRoute.Repl), SourcePollTimeout: time.Duration(c.SourcePollTimeout) * time.Millisecond, diff --git a/config/config_test.go b/config/config_test.go index f7b41e2b8e..2dd6a34d17 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -114,6 +114,7 @@ func Test_NewConfig(t *testing.T) { EtcdTimeout: 2 * time.Second, AppendFilters: &defaultFiltersFlags{}, PrependFilters: &defaultFiltersFlags{}, + DisabledFilters: commaListFlag(), CloneRoute: &routeChangerConfig{}, EditRoute: &routeChangerConfig{}, SourcePollTimeout: 3000, diff --git a/filters/builtin/builtin.go b/filters/builtin/builtin.go index 660b7162fd..b97ce03d93 100644 --- a/filters/builtin/builtin.go +++ b/filters/builtin/builtin.go @@ -112,12 +112,8 @@ const ( BackendTimeoutName = filters.BackendTimeoutName ) -// Returns a Registry object initialized with the default set of filter -// specifications found in the filters package. (including the builtin -// and the flowid subdirectories.) -func MakeRegistry() filters.Registry { - r := make(filters.Registry) - for _, s := range []filters.Spec{ +func Filters() []filters.Spec { + return []filters.Spec{ NewBackendIsProxy(), NewRequestHeader(), NewSetRequestHeader(), @@ -219,9 +215,16 @@ func MakeRegistry() filters.Registry { fadein.NewEndpointCreated(), consistenthash.NewConsistentHashKey(), consistenthash.NewConsistentHashBalanceFactor(), - } { - r.Register(s) } +} +// Returns a Registry object initialized with the default set of filter +// specifications found in the filters package. (including the builtin +// and the flowid subdirectories.) +func MakeRegistry() filters.Registry { + r := make(filters.Registry) + for _, s := range Filters() { + r.Register(s) + } return r } diff --git a/skipper.go b/skipper.go index 50086cad79..85b9c985ef 100644 --- a/skipper.go +++ b/skipper.go @@ -318,6 +318,9 @@ type Options struct { // DefaultFilters will be applied to all routes automatically. DefaultFilters *eskip.DefaultFilters + // DisabledFilters is a list of filters unavailable for use + DisabledFilters []string + // CloneRoute is a PreProcessor, that will be applied to all routes automatically. It // will clone all matching routes and apply changes to the // cloned routes. @@ -1061,6 +1064,31 @@ func initLog(o Options) error { return nil } +// filterRegistry creates a filter registry with the builtin and +// custom filter specs registered excluding disabled filters +func (o *Options) filterRegistry() filters.Registry { + registry := make(filters.Registry) + + disabledFilters := make(map[string]struct{}) + for _, name := range o.DisabledFilters { + disabledFilters[name] = struct{}{} + } + + for _, f := range builtin.Filters() { + if _, ok := disabledFilters[f.Name()]; !ok { + registry.Register(f) + } + } + + for _, f := range o.CustomFilters { + if _, ok := disabledFilters[f.Name()]; !ok { + registry.Register(f) + } + } + + return registry +} + func (o *Options) tlsConfig(cr *certregistry.CertRegistry) (*tls.Config, error) { if o.ProxyTLS != nil { @@ -1647,13 +1675,6 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { o.CustomFilters = append(o.CustomFilters, compress) } - // create a filter registry with the available filter specs registered, - // and register the custom filters - registry := builtin.MakeRegistry() - for _, f := range o.CustomFilters { - registry.Register(f) - } - // create routing // create the proxy instance var mo routing.MatchingOptions @@ -1714,7 +1735,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { // create a routing engine ro := routing.Options{ - FilterRegistry: registry, + FilterRegistry: o.filterRegistry(), MatchingOptions: mo, PollTimeout: o.SourcePollTimeout, DataClients: dataClients, diff --git a/skipper_test.go b/skipper_test.go index e4b4228a9a..2a74346685 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -20,6 +20,7 @@ import ( "github.com/zalando/skipper/dataclients/routestring" "github.com/zalando/skipper/filters" flog "github.com/zalando/skipper/filters/accesslog" + "github.com/zalando/skipper/filters/auth" "github.com/zalando/skipper/filters/builtin" fscheduler "github.com/zalando/skipper/filters/scheduler" "github.com/zalando/skipper/loadbalancer" @@ -34,6 +35,7 @@ import ( "github.com/zalando/skipper/secrets/certregistry" "github.com/zalando/skipper/tracing/tracingtest" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -88,6 +90,27 @@ func findAddress() (string, error) { return l.Addr().String(), nil } +func TestOptionsFilterRegistry(t *testing.T) { + o := &Options{ + CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, + } + fr := o.filterRegistry() + + assert.Contains(t, fr, filters.SetRequestHeaderName) + assert.Contains(t, fr, filters.LuaName) + assert.Contains(t, fr, filters.BearerInjectorName) + + o = &Options{ + CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, + DisabledFilters: []string{filters.LuaName, filters.BearerInjectorName}, + } + fr = o.filterRegistry() + + assert.Contains(t, fr, filters.SetRequestHeaderName) + assert.NotContains(t, fr, filters.LuaName) + assert.NotContains(t, fr, filters.BearerInjectorName) +} + func TestOptionsTLSConfig(t *testing.T) { cr := certregistry.NewCertRegistry() From d3e8dbd82527da74ac3fb4f4a9868a840478b81f Mon Sep 17 00:00:00 2001 From: Mustafa Saber Date: Wed, 12 Oct 2022 23:44:04 +0200 Subject: [PATCH 0089/1047] Make use of default filters in `RouteSRV` (#2083) * Add default filters to routeSRV Signed-off-by: Mustafa Abdelrahman * Add OAuth2 to routeSRV Use OAuth2 configurations to add OAuth2 routes before pulling routes from routeSRV Signed-off-by: Mustafa Abdelrahman * Only use required OAuth2 config values to add callback route Signed-off-by: Mustafa Abdelrahman * Add tests for OAuth2 callback route Signed-off-by: Mustafa Abdelrahman * test: fix nil exception stop calling a function from variable that could be nil Signed-off-by: Mustafa Abdelrahman * Add more testcases for routesrv Tests for EastWest and append flag Signed-off-by: Mustafa Abdelrahman Signed-off-by: Mustafa Abdelrahman --- config/config.go | 5 + routesrv/options.go | 14 +++ routesrv/polling.go | 13 +++ routesrv/routesrv.go | 20 +++- routesrv/routesrv_test.go | 104 ++++++++++++++++++ ...ternal-host-explicit-route-predicate.eskip | 7 ++ ...nternal-host-explicit-route-predicate.yaml | 41 +++++++ ...lb-target-multi-with-default-filters.eskip | 21 ++++ ...lb-target-multi-with-oauth2-callback.eskip | 20 ++++ 9 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 routesrv/testdata/internal-host-explicit-route-predicate.eskip create mode 100644 routesrv/testdata/internal-host-explicit-route-predicate.yaml create mode 100644 routesrv/testdata/lb-target-multi-with-default-filters.eskip create mode 100644 routesrv/testdata/lb-target-multi-with-oauth2-callback.eskip diff --git a/config/config.go b/config/config.go index 34de61c23c..6781b69039 100644 --- a/config/config.go +++ b/config/config.go @@ -637,6 +637,7 @@ func (c *Config) ToRouteSrvOptions() routesrv.Options { return routesrv.Options{ Address: c.Address, DefaultFiltersDir: c.DefaultFiltersDir, + DefaultFilters: &eskip.DefaultFilters{Prepend: c.PrependFilters.filters, Append: c.AppendFilters.filters}, KubernetesAllowedExternalNames: c.KubernetesAllowedExternalNames, KubernetesInCluster: c.KubernetesInCluster, KubernetesURL: c.KubernetesURL, @@ -660,6 +661,10 @@ func (c *Config) ToRouteSrvOptions() routesrv.Options { SourcePollTimeout: time.Duration(c.SourcePollTimeout) * time.Millisecond, WaitForHealthcheckInterval: c.WaitForHealthcheckInterval, WhitelistedHealthCheckCIDR: whitelistCIDRS, + + // Auth: + EnableOAuth2GrantFlow: c.EnableOAuth2GrantFlow, + OAuth2CallbackPath: c.Oauth2CallbackPath, } } diff --git a/routesrv/options.go b/routesrv/options.go index a94e3b7ddd..c5767009b0 100644 --- a/routesrv/options.go +++ b/routesrv/options.go @@ -6,6 +6,7 @@ import ( "github.com/zalando/skipper/dataclients/kubernetes" "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" ) // Options for initializing/running RouteServer @@ -114,10 +115,23 @@ type Options struct { // Default filters directory enables default filters mechanism and sets the directory where the filters are located DefaultFiltersDir string + // DefaultFilters enables appending/prepending filters to all routes + DefaultFilters *eskip.DefaultFilters + // OriginMarker is *deprecated* and not used anymore. It will be deleted in v1. OriginMarker bool + // List of custom filter specifications. + CustomFilters []filters.Spec + // OpenTracingBackendNameTag enables an additional tracing tag containing a backend name // for a route when it's available (e.g. for RouteGroups) OpenTracingBackendNameTag bool + + // EnableOAuth2GrantFlow, enables OAuth2 Grant Flow filter + EnableOAuth2GrantFlow bool + + // OAuth2CallbackPath contains the path where the OAuth2 callback requests with the + // authorization code should be redirected to. Defaults to /.well-known/oauth2-callback + OAuth2CallbackPath string } diff --git a/routesrv/polling.go b/routesrv/polling.go index 3968a77b29..240a5303f0 100644 --- a/routesrv/polling.go +++ b/routesrv/polling.go @@ -10,6 +10,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters/auth" "github.com/zalando/skipper/routing" "github.com/zalando/skipper/tracing" ) @@ -47,6 +49,11 @@ type poller struct { timeout time.Duration quit chan struct{} + // Preprocessors + defaultFilters *eskip.DefaultFilters + oauth2Config *auth.OAuthConfig + + // tracer tracer ot.Tracer } @@ -65,6 +72,12 @@ func (p *poller) poll(wg *sync.WaitGroup) { span := tracing.CreateSpan("poll_routes", context.TODO(), p.tracer) routes, err := p.client.LoadAll() + if p.defaultFilters != nil { + routes = p.defaultFilters.Do(routes) + } + if p.oauth2Config != nil { + routes = p.oauth2Config.NewGrantPreprocessor().Do(routes) + } routesCount = len(routes) switch { diff --git a/routesrv/routesrv.go b/routesrv/routesrv.go index bf0b0f9b61..e9ee4d4364 100644 --- a/routesrv/routesrv.go +++ b/routesrv/routesrv.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" "github.com/zalando/skipper/dataclients/kubernetes" + "github.com/zalando/skipper/filters/auth" "github.com/zalando/skipper/tracing" ) @@ -79,12 +80,21 @@ func New(opts Options) (*RouteServer, error) { if err != nil { return nil, err } + + var oauthConfig *auth.OAuthConfig + if opts.EnableOAuth2GrantFlow /* explicitly enable grant flow */ { + oauthConfig = &auth.OAuthConfig{} + oauthConfig.CallbackPath = opts.OAuth2CallbackPath + } + rs.poller = &poller{ - client: dataclient, - timeout: opts.SourcePollTimeout, - b: b, - quit: make(chan struct{}), - tracer: tracer, + client: dataclient, + timeout: opts.SourcePollTimeout, + b: b, + quit: make(chan struct{}), + defaultFilters: opts.DefaultFilters, + oauth2Config: oauthConfig, + tracer: tracer, } rs.wg = &sync.WaitGroup{} diff --git a/routesrv/routesrv_test.go b/routesrv/routesrv_test.go index c4dd849940..8456e4463f 100644 --- a/routesrv/routesrv_test.go +++ b/routesrv/routesrv_test.go @@ -256,3 +256,107 @@ func TestRoutesAreUpdated(t *testing.T) { t.Error("route contents were not updated") } } + +func TestRoutesWithDefaultFilters(t *testing.T) { + defer tl.Reset() + ks, _ := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServerWithOptions(t, routesrv.Options{ + SourcePollTimeout: pollInterval, + KubernetesURL: ks.URL, + DefaultFilters: &eskip.DefaultFilters{ + Prepend: []*eskip.Filter{ + { + Name: "enableAccessLog", + Args: []any{4, 5}, + }, + }, + Append: []*eskip.Filter{ + { + Name: "status", + Args: []any{200}, + }, + }, + }, + }) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Error("routes not initialized") + } + w := getRoutes(rs) + + want := parseEskipFixture(t, "testdata/lb-target-multi-with-default-filters.eskip") + got, err := eskip.Parse(w.Body.String()) + if err != nil { + t.Fatalf("served routes are not valid eskip: %s", w.Body) + } + if !eskip.EqLists(got, want) { + t.Errorf("served routes do not reflect kubernetes resources: %s", cmp.Diff(got, want)) + } + wantHTTPCode(t, w, http.StatusOK) +} + +func TestRoutesWithOAuth2Callback(t *testing.T) { + defer tl.Reset() + ks, _ := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServerWithOptions(t, routesrv.Options{ + SourcePollTimeout: pollInterval, + KubernetesURL: ks.URL, + EnableOAuth2GrantFlow: true, + OAuth2CallbackPath: "/.well-known/oauth2-callback", + }) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Error("routes not initialized") + } + w := getRoutes(rs) + + want := parseEskipFixture(t, "testdata/lb-target-multi-with-oauth2-callback.eskip") + got, err := eskip.Parse(w.Body.String()) + if err != nil { + t.Fatalf("served routes are not valid eskip: %s", w.Body) + } + if !eskip.EqLists(got, want) { + t.Errorf("served routes do not reflect kubernetes resources: %s", cmp.Diff(got, want)) + } + wantHTTPCode(t, w, http.StatusOK) +} + +func TestRoutesWithEastWest(t *testing.T) { + defer tl.Reset() + ks, _ := newKubeServer(t, loadKubeYAML(t, "testdata/internal-host-explicit-route-predicate.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServerWithOptions(t, routesrv.Options{ + SourcePollTimeout: pollInterval, + KubernetesURL: ks.URL, + KubernetesEastWestRangeDomains: []string{"ingress.cluster.local"}, + KubernetesEastWestRangePredicates: []*eskip.Predicate{ + { + Name: "ClientIP", + Args: []any{"10.2.0.0/15"}, + }, + }, + }) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Error("routes not initialized") + } + w := getRoutes(rs) + + want := parseEskipFixture(t, "testdata/internal-host-explicit-route-predicate.eskip") + got, err := eskip.Parse(w.Body.String()) + if err != nil { + t.Fatalf("served routes are not valid eskip: %s", w.Body) + } + if !eskip.EqLists(got, want) { + t.Errorf("served routes do not reflect kubernetes resources: %s", cmp.Diff(got, want)) + } + wantHTTPCode(t, w, http.StatusOK) +} diff --git a/routesrv/testdata/internal-host-explicit-route-predicate.eskip b/routesrv/testdata/internal-host-explicit-route-predicate.eskip new file mode 100644 index 0000000000..18377ef35c --- /dev/null +++ b/routesrv/testdata/internal-host-explicit-route-predicate.eskip @@ -0,0 +1,7 @@ +kube_rg__internal_default__myapp__all__0_0: + Host("^(ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") && + ClientIP("10.2.0.0/15") && Header("X-Foo", "bar") + -> ; + +kube_rg__internal___ingress_cluster_local__catchall__0_0: + Host("^(ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") && ClientIP("10.2.0.0/15") -> ; diff --git a/routesrv/testdata/internal-host-explicit-route-predicate.yaml b/routesrv/testdata/internal-host-explicit-route-predicate.yaml new file mode 100644 index 0000000000..5c05e4a1f3 --- /dev/null +++ b/routesrv/testdata/internal-host-explicit-route-predicate.yaml @@ -0,0 +1,41 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp +spec: + hosts: + - ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + defaultBackends: + - backendName: myapp + routes: + - predicates: + - Header("X-Foo", "bar") +--- +apiVersion: v1 +kind: Service +metadata: + name: myapp +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: myapp +subsets: +- addresses: + - ip: 10.2.4.8 + - ip: 10.2.4.16 + ports: + - port: 80 diff --git a/routesrv/testdata/lb-target-multi-with-default-filters.eskip b/routesrv/testdata/lb-target-multi-with-default-filters.eskip new file mode 100644 index 0000000000..16a5b2b77c --- /dev/null +++ b/routesrv/testdata/lb-target-multi-with-default-filters.eskip @@ -0,0 +1,21 @@ +// default backend, target 1: +kube_namespace1__ingress1______: + * + -> enableAccessLog(4, 5) + -> status(200) + -> ; + +// path rule, target 1: +kube_namespace1__ingress1__test_example_org___test1__service1: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) + && PathRegexp(/^(\/test1)/) + -> enableAccessLog(4, 5) + -> status(200) + -> ; + +// catch all: +kube___catchall__test_example_org____: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) + -> enableAccessLog(4, 5) + -> status(200) + -> ; diff --git a/routesrv/testdata/lb-target-multi-with-oauth2-callback.eskip b/routesrv/testdata/lb-target-multi-with-oauth2-callback.eskip new file mode 100644 index 0000000000..d281422d80 --- /dev/null +++ b/routesrv/testdata/lb-target-multi-with-oauth2-callback.eskip @@ -0,0 +1,20 @@ +// OAuth2 callback route +__oauth2_grant_callback: Path("/.well-known/oauth2-callback") + -> grantCallback() + -> ; + +// default backend, target 1: +kube_namespace1__ingress1______: + * + -> ; + +// path rule, target 1: +kube_namespace1__ingress1__test_example_org___test1__service1: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) + && PathRegexp(/^(\/test1)/) + -> ; + +// catch all: +kube___catchall__test_example_org____: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) + -> ; From 929cb0119fcaeaba7b55ec56f6524549affee6c9 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 13 Oct 2022 12:11:44 +0200 Subject: [PATCH 0090/1047] Empty commit to retrigger build (#2098) Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov From ea99ea82b5c2a3b3ac05ab4f8aa23a875c82a8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 13 Oct 2022 18:45:53 +0200 Subject: [PATCH 0091/1047] feature: ratelimit failure mode config filter (#2097) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: failure mode config filter ratelimitFailClosed() for all cluster ratelimits, leaky bucket and backendRatelimit filters Signed-off-by: Sandor Szücs --- docs/reference/filters.md | 32 ++++-- filters/filters.go | 1 + filters/ratelimit/fail_closed.go | 77 +++++++++++++ filters/ratelimit/fail_closed_test.go | 152 ++++++++++++++++++++++++++ filters/ratelimit/leakybucket.go | 27 ++++- filters/ratelimit/ratelimit_test.go | 1 + proxy/proxytest/proxytest.go | 4 + ratelimit/ratelimit.go | 7 ++ ratelimit/redis.go | 4 +- skipper.go | 7 ++ 10 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 filters/ratelimit/fail_closed.go create mode 100644 filters/ratelimit/fail_closed_test.go diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 73ec49ed50..128249ba34 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1607,12 +1607,14 @@ See also the [circuit breaker docs](https://godoc.org/github.com/zalando/skipper Can be used as [egress](egress.md) feature. -## ~~localRatelimit~~ +## rate limits + +### ~~localRatelimit~~ **DEPRECATED** use [clientRatelimit](#clientratelimit) with the same settings instead. -## clientRatelimit +### clientRatelimit Per skipper instance calculated ratelimit, that allows number of requests by client. The definition of the same client is based on data @@ -1650,7 +1652,7 @@ clientRatelimit(3, "1m", "X-Foo,Authorization,X-Bar") See also the [ratelimit docs](https://godoc.org/github.com/zalando/skipper/ratelimit). -## ratelimit +### ratelimit Per skipper instance calculated ratelimit, that allows forwarding a number of requests to the backend group. You need to run skipper with @@ -1670,7 +1672,7 @@ ratelimit(4000, "1m", 503) See also the [ratelimit docs](https://godoc.org/github.com/zalando/skipper/ratelimit). -## clusterClientRatelimit +### clusterClientRatelimit This ratelimit is calculated across all skipper peers and the same rate limit group. The first parameter is a string to select the same @@ -1698,7 +1700,7 @@ clusterClientRatelimit("groupA", 10, "1h", "X-Forwarded-For,Authorization,User-A See also the [ratelimit docs](https://godoc.org/github.com/zalando/skipper/ratelimit). -## clusterRatelimit +### clusterRatelimit This ratelimit is calculated across all skipper peers and the same rate limit group. The first parameter is a string to select the same @@ -1723,7 +1725,7 @@ clusterRatelimit("groupB", 4000, "1m", 503) See also the [ratelimit docs](https://godoc.org/github.com/zalando/skipper/ratelimit). -## backendRatelimit +### backendRatelimit The filter configures request rate limit for each backend endpoint within rate limit group across all Skipper peers. When limit is reached Skipper refuses to forward the request to the backend and @@ -1782,7 +1784,7 @@ foo: Path("/baz") Configures rate limit of 100 requests per second for each `backend1` and `backend2` and responds with `429 Too Many Requests` when limit is reached. -## clusterLeakyBucketRatelimit +### clusterLeakyBucketRatelimit Implements leaky bucket rate limit algorithm that uses Redis as a storage. Requires command line flags `-enable-ratelimits`, `-enable-swarm` and `-swarm-redis-urls` to be set. @@ -1882,6 +1884,22 @@ Path("/cheap") -> clusterLeakyBucketRatelimit("user-${request.cookie.Authori Path("/expensive") -> clusterLeakyBucketRatelimit("user-${request.cookie.Authorization}", 1, "1s", 5, 2) -> ... ``` +### ratelimitFailClosed + +This filter changes the failure mode for rate limit filters. If the +filter is present, infrastructure issues will lead to rate limit. + +Examples: +``` +fail_open: * -> clusterRatelimit("g",10, "1s") +fail_closed: * -> ratelimitFailClosed() -> clusterRatelimit("g", 10, "1s") +``` + +In case `clusterRatelimit` could not reach the swarm (f.e. redis): + +* Route `fail_open` will allow the request +* Route `fail_closed` will deny the request + ## shedder The basic idea of load shedding is to reduce errors by early stopping diff --git a/filters/filters.go b/filters/filters.go index 31a06d538a..699f49bdd4 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -274,6 +274,7 @@ const ( ClusterRatelimitName = "clusterRatelimit" ClusterLeakyBucketRatelimitName = "clusterLeakyBucketRatelimit" BackendRateLimitName = "backendRatelimit" + RatelimitFailClosedName = "ratelimitFailClosed" LuaName = "lua" CorsOriginName = "corsOrigin" HeaderToQueryName = "headerToQuery" diff --git a/filters/ratelimit/fail_closed.go b/filters/ratelimit/fail_closed.go new file mode 100644 index 0000000000..1bd675d7a5 --- /dev/null +++ b/filters/ratelimit/fail_closed.go @@ -0,0 +1,77 @@ +package ratelimit + +import ( + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/routing" +) + +type failClosedSpec struct{} +type failClosed struct{} +type FailClosedPostProcessor struct{} + +func NewFailClosedPostProcessor() *FailClosedPostProcessor { + return &FailClosedPostProcessor{} +} + +// Do is implementing a PostProcessor interface to change the filter +// configs at filter processing time. The fail open/closed decision +// needs to be done once and can be processed before we activate the +// new routes. +func (*FailClosedPostProcessor) Do(routes []*routing.Route) []*routing.Route { + for _, r := range routes { + var found bool + + for _, f := range r.Filters { + if f.Name == filters.RatelimitFailClosedName { + found = true + continue + } + // no config changes detected + if !found { + continue + } + + switch f.Name { + // leaky bucket has no Settings + case filters.ClusterLeakyBucketRatelimitName: + lf, ok := f.Filter.(*leakyBucketFilter) + if ok { + lf.failClosed = true + } + + case filters.BackendRateLimitName: + bf, ok := f.Filter.(*BackendRatelimit) + if ok { + bf.Settings.FailClosed = true + } + + case filters.ClientRatelimitName: + fallthrough + case filters.ClusterClientRatelimitName: + fallthrough + case filters.ClusterRatelimitName: + ff, ok := f.Filter.(*filter) + if ok { + ff.settings.FailClosed = true + } + } + } + } + return routes +} + +func NewFailClosed() filters.Spec { + return &failClosedSpec{} +} + +func (*failClosedSpec) Name() string { + return filters.RatelimitFailClosedName +} + +func (*failClosedSpec) CreateFilter([]interface{}) (filters.Filter, error) { + return &failClosed{}, nil +} + +func (*failClosed) Request(filters.FilterContext) {} + +func (*failClosed) Response(filters.FilterContext) {} diff --git a/filters/ratelimit/fail_closed_test.go b/filters/ratelimit/fail_closed_test.go new file mode 100644 index 0000000000..f4c7c6cc2f --- /dev/null +++ b/filters/ratelimit/fail_closed_test.go @@ -0,0 +1,152 @@ +package ratelimit_test + +import ( + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/builtin" + fratelimit "github.com/zalando/skipper/filters/ratelimit" + snet "github.com/zalando/skipper/net" + "github.com/zalando/skipper/proxy" + "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/ratelimit" + "github.com/zalando/skipper/routing" +) + +func TestFailureMode(t *testing.T) { + for _, tt := range []struct { + name string + ratelimitFilterName string + failClosed bool + wantLimit bool + limitStatusCode int + }{ + { + name: "test clusterRatelimit fail open", + ratelimitFilterName: "clusterRatelimit", + wantLimit: false, + limitStatusCode: http.StatusTooManyRequests, + }, + { + name: "test clusterRatelimit fail closed", + ratelimitFilterName: "clusterRatelimit", + failClosed: true, + wantLimit: true, + limitStatusCode: http.StatusTooManyRequests, + }, + { + name: "test clusterClientRatelimit fail open", + ratelimitFilterName: "clusterClientRatelimit", + wantLimit: false, + limitStatusCode: http.StatusTooManyRequests, + }, + { + name: "test clusterClientRatelimit fail closed", + ratelimitFilterName: "clusterClientRatelimit", + failClosed: true, + wantLimit: true, + limitStatusCode: http.StatusTooManyRequests, + }, + { + name: "test backendRatelimit fail open", + ratelimitFilterName: "backendRatelimit", + wantLimit: false, + limitStatusCode: http.StatusServiceUnavailable, + }, + { + name: "test backendRatelimit fail closed", + ratelimitFilterName: "backendRatelimit", + failClosed: true, + wantLimit: true, + limitStatusCode: http.StatusServiceUnavailable, + }, + { + name: "test clusterLeakyBucketRatelimit fail open", + ratelimitFilterName: "clusterLeakyBucketRatelimit", + wantLimit: false, + limitStatusCode: http.StatusTooManyRequests, + }, + { + name: "test clusterLeakyBucketRatelimit fail closed", + ratelimitFilterName: "clusterLeakyBucketRatelimit", + failClosed: true, + wantLimit: true, + limitStatusCode: http.StatusTooManyRequests, + }} { + t.Run(tt.name, func(t *testing.T) { + fr := builtin.MakeRegistry() + + reg := ratelimit.NewSwarmRegistry(nil, &snet.RedisOptions{Addrs: []string{"127.0.0.2:6379"}}, ratelimit.Settings{}) + defer reg.Close() + + provider := fratelimit.NewRatelimitProvider(reg) + fr.Register(fratelimit.NewClusterRateLimit(provider)) + fr.Register(fratelimit.NewClusterClientRateLimit(provider)) + fr.Register(fratelimit.NewClusterLeakyBucketRatelimit(reg)) + fr.Register(fratelimit.NewBackendRatelimit()) + fr.Register(fratelimit.NewFailClosed()) + + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + })) + defer backend.Close() + + args := []interface{}{"t", 1, "1s"} + switch tt.ratelimitFilterName { + case filters.ClusterLeakyBucketRatelimitName: + args = append(args, 10, 1) + case filters.ClusterClientRatelimitName: + args = append(args, "X-Test") + } + + r := &eskip.Route{Filters: []*eskip.Filter{ + {Name: tt.ratelimitFilterName, Args: args}}, Backend: backend.URL} + if tt.failClosed { + r.Filters = append([]*eskip.Filter{{Name: fratelimit.NewFailClosed().Name()}}, + r.Filters...) + } + + proxy := proxytest.WithParamsAndRoutingOptions( + fr, + proxy.Params{ + RateLimiters: reg, + }, + routing.Options{ + PostProcessors: []routing.PostProcessor{ + fratelimit.NewFailClosedPostProcessor(), + }, + }, + r) + reqURL, err := url.Parse(proxy.URL) + if err != nil { + t.Fatalf("Failed to parse url %s: %v", proxy.URL, err) + } + req, err := http.NewRequest("GET", reqURL.String(), nil) + if err != nil { + t.Fatal(err) + return + } + req.Header.Set("X-Test", "foo") + + rsp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + + defer rsp.Body.Close() + + limited := rsp.StatusCode == tt.limitStatusCode + if tt.wantLimit && !limited { + t.Errorf("Failed to get limited response, got: limited=%v status=%d", limited, rsp.StatusCode) + } else if !tt.wantLimit && limited { + t.Errorf("Failed to get allowed response, got: limited=%v", limited) + } + }) + } + +} diff --git a/filters/ratelimit/leakybucket.go b/filters/ratelimit/leakybucket.go index fe1278d4e3..09055912dd 100644 --- a/filters/ratelimit/leakybucket.go +++ b/filters/ratelimit/leakybucket.go @@ -21,9 +21,10 @@ type leakyBucketSpec struct { } type leakyBucketFilter struct { - label *eskip.Template - bucket leakyBucket - increment int + label *eskip.Template + bucket leakyBucket + increment int + failClosed bool } // NewClusterLeakyBucketRatelimit creates a filter Spec, whose instances implement rate limiting using leaky bucket algorithm. @@ -84,7 +85,15 @@ func (s *leakyBucketSpec) CreateFilter(args []interface{}) (filters.Filter, erro // emission is the reciprocal of the leak rate emission := leakPeriod / time.Duration(leakVolume) - return &leakyBucketFilter{eskip.NewTemplate(label), s.create(capacity, emission), increment}, nil + return &leakyBucketFilter{ + label: eskip.NewTemplate(label), + bucket: s.create(capacity, emission), + increment: increment, + }, nil +} + +func fail(ctx filters.FilterContext, header http.Header) { + ctx.Serve(&http.Response{StatusCode: http.StatusTooManyRequests, Header: header}) } func (f *leakyBucketFilter) Request(ctx filters.FilterContext) { @@ -94,7 +103,12 @@ func (f *leakyBucketFilter) Request(ctx filters.FilterContext) { } added, retry, err := f.bucket.Add(ctx.Request().Context(), label, f.increment) if err != nil { - return // allow on error + if f.failClosed { + header := http.Header{} + header.Set("Retry-After", "60") + fail(ctx, header) + } + return } if added { return // allow if successfully added @@ -104,7 +118,8 @@ func (f *leakyBucketFilter) Request(ctx filters.FilterContext) { if retry > 0 { header.Set("Retry-After", strconv.Itoa(int(retry/time.Second))) } - ctx.Serve(&http.Response{StatusCode: http.StatusTooManyRequests, Header: header}) + + fail(ctx, header) } func (*leakyBucketFilter) Response(filters.FilterContext) {} diff --git a/filters/ratelimit/ratelimit_test.go b/filters/ratelimit/ratelimit_test.go index 0922b0a33c..d7406df2ec 100644 --- a/filters/ratelimit/ratelimit_test.go +++ b/filters/ratelimit/ratelimit_test.go @@ -77,6 +77,7 @@ func (l *testLimit) get(s ratelimit.Settings) limit { } return l } + func (l *testLimit) AllowContext(context.Context, string) bool { return false } func (l *testLimit) RetryAfter(string) int { return 31415 } diff --git a/proxy/proxytest/proxytest.go b/proxy/proxytest/proxytest.go index 407281aac1..9991e80dff 100644 --- a/proxy/proxytest/proxytest.go +++ b/proxy/proxytest/proxytest.go @@ -21,6 +21,10 @@ type TestProxy struct { server *httptest.Server } +func WithParamsAndRoutingOptions(fr filters.Registry, proxyParams proxy.Params, o routing.Options, routes ...*eskip.Route) *TestProxy { + return newTestProxy(fr, o, proxyParams, routes...) +} + func WithRoutingOptions(fr filters.Registry, o routing.Options, routes ...*eskip.Route) *TestProxy { return newTestProxy(fr, o, proxy.Params{CloseIdleConnsPeriod: -time.Second}, routes...) } diff --git a/ratelimit/ratelimit.go b/ratelimit/ratelimit.go index 08042e53d8..50aa82781e 100644 --- a/ratelimit/ratelimit.go +++ b/ratelimit/ratelimit.go @@ -278,6 +278,13 @@ func (rrl *RoundRobinLookuper) String() string { // Settings configures the chosen rate limiter type Settings struct { + // FailClosed allows to to decide what happens on failures to + // query the ratelimit. For example redis is down, fail open + // or fail closed. FailClosed set to true will deny the + // request and set to true will allow the request. Default is + // to fail open. + FailClosed bool `yaml:"fail-closed"` + // Type of the chosen rate limiter Type RatelimitType `yaml:"type"` diff --git a/ratelimit/redis.go b/ratelimit/redis.go index 2c4c9ed9b1..67b4b02809 100644 --- a/ratelimit/redis.go +++ b/ratelimit/redis.go @@ -16,6 +16,7 @@ import ( // clusterLimitRedis stores all data required for the cluster ratelimit. type clusterLimitRedis struct { + failClosed bool typ string group string maxHits int64 @@ -44,6 +45,7 @@ func newClusterRateLimiterRedis(s Settings, r *net.RedisRingClient, group string } rl := &clusterLimitRedis{ + failClosed: s.FailClosed, typ: s.Type.String(), group: group, maxHits: int64(s.MaxHits), @@ -128,7 +130,7 @@ func (c *clusterLimitRedis) AllowContext(ctx context.Context, clearText string) c.measureQuery(allowMetricsFormat, allowMetricsFormatWithGroup, &failed, now) if failed { - allow = true // fail open + allow = !c.failClosed } if allow { c.metrics.IncCounter(redisMetricsPrefix + "allows") diff --git a/skipper.go b/skipper.go index 85b9c985ef..4745786218 100644 --- a/skipper.go +++ b/skipper.go @@ -1594,6 +1594,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { } var ratelimitRegistry *ratelimit.Registry + var failClosedRatelimitPostProcessor *ratelimitfilters.FailClosedPostProcessor if o.EnableRatelimiters || len(o.RatelimitSettings) > 0 { log.Infof("enabled ratelimiters %v: %v", o.EnableRatelimiters, o.RatelimitSettings) ratelimitRegistry = ratelimit.NewSwarmRegistry(swarmer, redisOptions, o.RatelimitSettings...) @@ -1608,8 +1609,11 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { o.ClusterRatelimitMaxGroupShards = 1 } + failClosedRatelimitPostProcessor = ratelimitfilters.NewFailClosedPostProcessor() + provider := ratelimitfilters.NewRatelimitProvider(ratelimitRegistry) o.CustomFilters = append(o.CustomFilters, + ratelimitfilters.NewFailClosed(), ratelimitfilters.NewClientRatelimit(provider), ratelimitfilters.NewLocalRatelimit(provider), ratelimitfilters.NewRatelimit(provider), @@ -1752,6 +1756,9 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { }, SignalFirstLoad: o.WaitFirstRouteLoad, } + if failClosedRatelimitPostProcessor != nil { + ro.PostProcessors = append(ro.PostProcessors, failClosedRatelimitPostProcessor) + } if o.DefaultFilters != nil { ro.PreProcessors = append(ro.PreProcessors, o.DefaultFilters) From 55b54b559900c28c8da53d3f87d4419d1f3733fd Mon Sep 17 00:00:00 2001 From: Mustafa Saber Date: Thu, 13 Oct 2022 23:24:23 +0200 Subject: [PATCH 0092/1047] Update docs for RouteSRV (#2099) Signed-off-by: Mustafa Abdelrahman Signed-off-by: Mustafa Abdelrahman --- docs/img/skipper_with_routesrv.svg | 4 ++++ docs/img/skipper_without_routesrv.svg | 4 ++++ docs/kubernetes/ingress-controller.md | 13 +++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 docs/img/skipper_with_routesrv.svg create mode 100644 docs/img/skipper_without_routesrv.svg diff --git a/docs/img/skipper_with_routesrv.svg b/docs/img/skipper_with_routesrv.svg new file mode 100644 index 0000000000..7e28703eea --- /dev/null +++ b/docs/img/skipper_with_routesrv.svg @@ -0,0 +1,4 @@ + + + +
frg
frg
routesrv
routesrv
pull
pull
skipper ingress
skipper ingress
pull
pull
skipper ingress
skipper ingress
frg = fetches ingress/routegroup routing table every 3 seconds
frg = fetches ingress/routegroup routing table every 3 seconds
pull
pull
skipper ingress
skipper ingress
k8s apiserver
k8s apiserver
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/img/skipper_without_routesrv.svg b/docs/img/skipper_without_routesrv.svg new file mode 100644 index 0000000000..c737eeab6f --- /dev/null +++ b/docs/img/skipper_without_routesrv.svg @@ -0,0 +1,4 @@ + + + +
k8s apiserver
k8s apiserver
frg
frg
skipper ingress
skipper ingress
frg
frg
skipper ingress
skipper ingress
frg = fetches ingress/routegroup routing table every 3 seconds
frg = fetches ingress/routegroup routing table every 3 seconds
frg
frg
skipper ingress
skipper ingress
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/kubernetes/ingress-controller.md b/docs/kubernetes/ingress-controller.md index fd7bb7c633..8c1c9a2cec 100644 --- a/docs/kubernetes/ingress-controller.md +++ b/docs/kubernetes/ingress-controller.md @@ -195,6 +195,19 @@ load balancer virtual IP. ![ingress-traffic-flow](../img/ingress-traffic-flow-baremetal.svg) +## RouteSRV + +In kubernetes skipper-ingress fetches ingress/routegroup configurations every **3s**, with high number of skipper pods *~100* we faced issues with kube-apiserver. At which we introduced RouteSRV, which will serve as a layer between kube-apiserver and skipper ingress, so it will give us more flexiability in scaling skipper-ingress without affecting k8s-apiserver + +### Before + +![skipper-without-routesrv](../img/skipper_without_routesrv.svg) + +### After + +![skipper-with-routesrv](../img/skipper_with_routesrv.svg) + + ## Requirements In general for one endpoint you need, a DNS A/AAAA record pointing to From f056dc3f695a83b40c57ff9f8ff2bd63a4e47fb3 Mon Sep 17 00:00:00 2001 From: Peter Klijn Date: Fri, 14 Oct 2022 10:05:55 +0200 Subject: [PATCH 0093/1047] Add an option to override the routing via services instead of endpoints (#2088) * Add an option to override the routing via services instead of endpoints Signed-off-by: Peter Klijn * Change variable name as suggested in GH issue Signed-off-by: Peter Klijn * Add feature boolean to main Options structs and pass along to kubernetes.Options Signed-off-by: Peter Klijn * Add some simple tests Signed-off-by: Peter Klijn * Add test for Traffic filter Signed-off-by: Peter Klijn * Add a small section explaining the ability to override the default behaviour of using endpoints Signed-off-by: Peter Klijn * Add an example to the docs for context Signed-off-by: Peter Klijn * Update documentation based on PR feedback Signed-off-by: Peter Klijn * Update eps variable to reduce duplication Signed-off-by: Peter Klijn Signed-off-by: Peter Klijn --- dataclients/kubernetes/ingress.go | 2 + dataclients/kubernetes/ingressv1.go | 17 ++- dataclients/kubernetes/kube.go | 4 + .../kubernetes/kubernetestest/fixtures.go | 2 + ...d-and-force-k8s-service-flag-enabled.eskip | 3 + ...nd-and-force-k8s-service-flag-enabled.kube | 2 + ...nd-and-force-k8s-service-flag-enabled.yaml | 43 ++++++++ ...or-filter-and-force-k8s-service-flag.eskip | 4 + ...tor-filter-and-force-k8s-service-flag.kube | 8 ++ ...tor-filter-and-force-k8s-service-flag.yaml | 103 ++++++++++++++++++ ...ic-switch-and-force-k8s-service-flag.eskip | 18 +++ ...fic-switch-and-force-k8s-service-flag.kube | 3 + ...fic-switch-and-force-k8s-service-flag.yaml | 85 +++++++++++++++ docs/kubernetes/ingress-controller.md | 21 +++- routesrv/options.go | 4 + routesrv/routesrv.go | 1 + skipper.go | 5 + 17 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.yaml diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index b44c7642c2..ce0d85f678 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -59,6 +59,7 @@ type ingress struct { kubernetesEnableEastWest bool ingressV1 bool provideHTTPSRedirect bool + forceKubernetesService bool } var nonWord = regexp.MustCompile(`\W`) @@ -80,6 +81,7 @@ func newIngress(o Options) *ingress { eastWestRangeDomains: o.KubernetesEastWestRangeDomains, eastWestRangePredicates: o.KubernetesEastWestRangePredicates, allowedExternalNames: o.AllowedExternalNames, + forceKubernetesService: o.ForceKubernetesService, } } diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index 8826bba5b6..68bf639b40 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -40,6 +40,7 @@ func convertPathRuleV1( prule *definitions.PathRuleV1, pathMode PathMode, allowedExternalNames []*regexp.Regexp, + forceKubernetesService bool, ) (*eskip.Route, error) { ns := metadata.Namespace @@ -78,6 +79,8 @@ func convertPathRuleV1( } } else if svc.Spec.Type == "ExternalName" { return externalNameRoute(ns, name, host, hostRegexp, svc, servicePort, allowedExternalNames) + } else if forceKubernetesService { + eps = []string{serviceNameBackend(svcName, ns, servicePort)} } else { protocol := "http" if p, ok := metadata.Annotations[skipperBackendProtocolAnnotationKey]; ok { @@ -136,6 +139,7 @@ func (ing *ingress) addEndpointsRuleV1(ic ingressContext, host string, prule *de prule, ic.pathMode, ing.allowedExternalNames, + ing.forceKubernetesService, ) if err != nil { // if the service is not found the route should be removed @@ -332,6 +336,7 @@ func (ing *ingress) addSpecIngressTLSV1(ic ingressContext, ingtls *definitions.T func (ing *ingress) convertDefaultBackendV1( state *clusterState, i *definitions.IngressV1Item, + forceKubernetesService bool, ) (*eskip.Route, bool, error) { // the usage of the default backend depends on what we want // we can generate a hostname out of it based on shared rules @@ -366,6 +371,8 @@ func (ing *ingress) convertDefaultBackendV1( } else if svc.Spec.Type == "ExternalName" { r, err := externalNameRoute(ns, name, "default", nil, svc, servicePort, ing.allowedExternalNames) return r, err == nil, err + } else if forceKubernetesService { + eps = []string{serviceNameBackend(svcName, ns, servicePort)} } else { log.Debugf("convertDefaultBackendV1: Found target port %v, for service %s", servicePort.TargetPort, svcName) protocol := "http" @@ -406,6 +413,14 @@ func (ing *ingress) convertDefaultBackendV1( }, true, nil } +func serviceNameBackend(svcName, svcNamespace string, servicePort *servicePort) string { + scheme := "https" + if n, _ := servicePort.TargetPort.Number(); n != 443 { + scheme = "http" + } + return fmt.Sprintf("%s://%s.%s.svc.cluster.local:%s", scheme, svcName, svcNamespace, servicePort.TargetPort) +} + func (ing *ingress) ingressV1Route( i *definitions.IngressV1Item, redirect *redirectInfo, @@ -438,7 +453,7 @@ func (ing *ingress) ingressV1Route( } var route *eskip.Route - if r, ok, err := ing.convertDefaultBackendV1(state, i); ok { + if r, ok, err := ing.convertDefaultBackendV1(state, i, ing.forceKubernetesService); ok { route = r } else if err != nil { ic.logger.Errorf("error while converting default backend: %v", err) diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 904c39a46d..902f8b5cee 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -215,6 +215,10 @@ type Options struct { AllowedExternalNames []*regexp.Regexp CertificateRegistry *certregistry.CertRegistry + + // ForceKubernetesService overrides the default Skipper functionality to route traffic using + // Kubernetes Endpoint, instead using Kubernetes Services. + ForceKubernetesService bool } // Client is a Skipper DataClient implementation used to create routes based on Kubernetes Ingress settings. diff --git a/dataclients/kubernetes/kubernetestest/fixtures.go b/dataclients/kubernetes/kubernetestest/fixtures.go index 6040e2d74b..2d5294f3ab 100644 --- a/dataclients/kubernetes/kubernetestest/fixtures.go +++ b/dataclients/kubernetes/kubernetestest/fixtures.go @@ -47,6 +47,7 @@ type kubeOptionsParser struct { IngressesLabels map[string]string `yaml:"kubernetes-ingresses-label-selector"` ServicesLabels map[string]string `yaml:"kubernetes-services-label-selector"` EndpointsLabels map[string]string `yaml:"kubernetes-endpoints-label-selector"` + ForceKubernetesService bool `yaml:"force-kubernetes-service"` } func baseNoExt(n string) string { @@ -231,6 +232,7 @@ func testFixture(t *testing.T, f fixtureSet) { o.IngressLabelSelectors = kop.IngressesLabels o.ServicesLabelSelectors = kop.ServicesLabels o.EndpointsLabelSelectors = kop.EndpointsLabels + o.ForceKubernetesService = kop.ForceKubernetesService aen, err := compileRegexps(kop.AllowedExternalNames) if err != nil { diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.eskip new file mode 100644 index 0000000000..390b86ac5d --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.eskip @@ -0,0 +1,3 @@ +kube_foo__baz______: + * + -> "http://bar.foo.svc.cluster.local:8080"; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.kube new file mode 100644 index 0000000000..ddc1e0b996 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.kube @@ -0,0 +1,2 @@ +ingressv1: true +force-kubernetes-service: true diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.yaml new file mode 100644 index 0000000000..fd2382cb93 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-default-backend-and-force-k8s-service-flag-enabled.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: baz +spec: + defaultBackend: + service: + name: bar + port: + number: 8181 +--- +apiVersion: v1 +kind: Service +metadata: + name: bar + namespace: foo +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz + port: 8181 + protocol: TCP + targetPort: 8080 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: bar + namespace: foo +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: baz + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.eskip new file mode 100644 index 0000000000..7068859b2d --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.eskip @@ -0,0 +1,4 @@ +kube_foo__qux__www_example_org_____bar: + Host("^(www[.]example[.]org[.]?(:[0-9]+)?)$") && PathRegexp("^/") + -> setPath("/foo") + -> "http://bar.foo.svc.cluster.local:8080"; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.kube new file mode 100644 index 0000000000..b4ac643040 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.kube @@ -0,0 +1,8 @@ +ingressv1: true +force-kubernetes-service: true +kubernetes-ingresses-label-selector: + skipper-enabled: true +kubernetes-services-label-selector: + skipper-enabled: true +kubernetes-endpoints-label-selector: + skipper-enabled: true diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.yaml new file mode 100644 index 0000000000..fd9a72d409 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-label-selector-filter-and-force-k8s-service-flag.yaml @@ -0,0 +1,103 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux + labels: + skipper-enabled: "true" + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar + port: + name: baz +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar + labels: + skipper-enabled: "true" +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + skipper-enabled: "true" + namespace: foo + name: bar +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: baz + port: 8080 + protocol: TCP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: foo + name: qux-no-label + annotations: + zalando.org/skipper-filter: setPath("/foo") +spec: + rules: + - host: www.example.org + http: + paths: + - path: "/" + pathType: ImplementationSpecific + backend: + service: + name: bar-no-label + port: + name: baz-no-label +--- +apiVersion: v1 +kind: Service +metadata: + namespace: foo + name: bar-no-label +spec: + clusterIP: 10.3.190.97 + ports: + - name: baz-no-label + port: 8181 + protocol: TCP + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + skipper-enabled: "true" + namespace: foo + name: bar-no-label +subsets: + - addresses: + - ip: 10.2.9.105 + - ip: 10.2.9.106 + ports: + - name: baz-no-label + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.eskip b/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.eskip new file mode 100644 index 0000000000..eaeec071da --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.eskip @@ -0,0 +1,18 @@ +kube_namespace1__ingress1______: + * + -> "http://service1v1.namespace1.svc.cluster.local:8080"; + +kube_namespace1__ingress1__test_example_org___test1__service1v1: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) && + PathRegexp(/^(\/test1)/) && + Traffic(0.3) + -> "http://service1v1.namespace1.svc.cluster.local:8080"; + +kube_namespace1__ingress1__test_example_org___test1__service1v2: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) && + PathRegexp(/^(\/test1)/) + -> "http://service1v2.namespace1.svc.cluster.local:8080"; + +kube___catchall__test_example_org____: + Host(/^(test[.]example[.]org[.]?(:[0-9]+)?)$/) + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.kube b/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.kube new file mode 100644 index 0000000000..7ada578b34 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.kube @@ -0,0 +1,3 @@ +ingressv1: true +force-kubernetes-service: true + diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.yaml b/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.yaml new file mode 100644 index 0000000000..99650ebb70 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/lb-traffic-switch-and-force-k8s-service-flag.yaml @@ -0,0 +1,85 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: namespace1 + name: ingress1 + annotations: + zalando.org/backend-weights: '{"service1v1": 30, "service1v2": 70}' +spec: + defaultBackend: + service: + name: service1v1 + port: + name: port1 + rules: + - host: test.example.org + http: + paths: + - path: "/test1" + pathType: ImplementationSpecific + backend: + service: + name: service1v1 + port: + name: port1 + - path: "/test1" + pathType: ImplementationSpecific + backend: + service: + name: service1v2 + port: + name: port1 +--- +apiVersion: v1 +kind: Service +metadata: + namespace: namespace1 + name: service1v1 +spec: + clusterIP: 1.2.3.4 + ports: + - name: port1 + port: 8080 + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + namespace: namespace1 + name: service1v2 +spec: + clusterIP: 1.2.3.5 + ports: + - name: port1 + port: 8080 + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + namespace: namespace1 + name: service1v1 +subsets: +- addresses: + - ip: 42.0.1.2 + - ip: 42.0.1.3 + ports: + - name: port1 + port: 8080 + protocol: TCP +--- +apiVersion: v1 +kind: Endpoints +metadata: + namespace: namespace1 + name: service1v2 +subsets: +- addresses: + - ip: 42.0.1.4 + - ip: 42.0.1.5 + ports: + - name: port1 + port: 8080 + protocol: TCP diff --git a/docs/kubernetes/ingress-controller.md b/docs/kubernetes/ingress-controller.md index 8c1c9a2cec..910d7ecc5e 100644 --- a/docs/kubernetes/ingress-controller.md +++ b/docs/kubernetes/ingress-controller.md @@ -149,7 +149,7 @@ You would point your DNS entries to the load balancer in front of skipper, for example automated using [external-dns](https://github.com/kubernetes-incubator/external-dns). -## Why skipper uses endpoints and not services? +## Why Skipper uses Endpoints and not Services? Skipper does not use the ClusterIP of [Kubernetes Services](http://kubernetes.io/docs/user-guide/services) to route @@ -163,6 +163,25 @@ TLS handshake error to the endpoint. Other reasons are future support of features like session affinity, different load balancer algorithms or distributed loadbalancing also known as service mesh. +### Using Services instead of Endpoints + +While using Endpoints is the preferred way of using Skipper as an +ingress controller as described in the section above, there might be +edge cases that require the use of [Kubernetes +Services](http://kubernetes.io/docs/user-guide/services) instead. + +An example of scenario where you might need to use Services is when you rely +on Istio networking features to connect multiple clusters, as the IPs of +Kubernetes Endpoints will not resolve in all cases. + +If you find yourself in this category, you can override the default behaviour +by setting the `KubernetesForceService` flag to `true` in the `Skipper.Options` struct. +This will cause Skipper to create routes with `BackendType=eskip.NetworkBackend` instead +of `BackendType=eskip.LBBackend` and use the following address format: +`http://..svc.cluster.local:`. See the [Kubernetes Service DNS +documentation](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#services) +for more information. + ## AWS deployment In AWS, this could be an ALB with DNS pointing to the ALB. The ALB can diff --git a/routesrv/options.go b/routesrv/options.go index c5767009b0..8d1d5ea42b 100644 --- a/routesrv/options.go +++ b/routesrv/options.go @@ -101,6 +101,10 @@ type Options struct { // used with external name services (type=ExternalName). KubernetesAllowedExternalNames []*regexp.Regexp + // KubernetesForceService overrides the default Skipper functionality to route traffic using Kubernetes Endpoints, + // instead using Kubernetes Services. + KubernetesForceService bool + // WhitelistedHealthcheckCIDR appends the whitelisted IP Range to the inernalIPS range for healthcheck purposes WhitelistedHealthCheckCIDR []string diff --git a/routesrv/routesrv.go b/routesrv/routesrv.go index e9ee4d4364..4a3b88b39b 100644 --- a/routesrv/routesrv.go +++ b/routesrv/routesrv.go @@ -76,6 +76,7 @@ func New(opts Options) (*RouteServer, error) { ReverseSourcePredicate: opts.ReverseSourcePredicate, RouteGroupClass: opts.KubernetesRouteGroupClass, WhitelistedHealthCheckCIDR: opts.WhitelistedHealthCheckCIDR, + ForceKubernetesService: opts.KubernetesForceService, }) if err != nil { return nil, err diff --git a/skipper.go b/skipper.go index 4745786218..5639772adf 100644 --- a/skipper.go +++ b/skipper.go @@ -272,6 +272,10 @@ type Options struct { // *DEPRECATED* KubernetesRedisServicePort is not used anymore KubernetesRedisServicePort int + // KubernetesForceService overrides the default Skipper functionality to route traffic using Kubernetes Endpoints, + // instead using Kubernetes Services. + KubernetesForceService bool + // *DEPRECATED* API endpoint of the Innkeeper service, storing route definitions. InnkeeperUrl string @@ -1004,6 +1008,7 @@ func createDataClients(o Options, auth innkeeper.Authentication, cr *certregistr ReverseSourcePredicate: o.ReverseSourcePredicate, RouteGroupClass: o.KubernetesRouteGroupClass, WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR, + ForceKubernetesService: o.KubernetesForceService, CertificateRegistry: cr, }) if err != nil { From 43d8f611f5155f2b9239e57e398152fceed35fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 14 Oct 2022 10:49:10 +0200 Subject: [PATCH 0094/1047] doc:update document for ingress-v1 support feature change copied from https://github.com/zalando/skipper/pull/2056/ (#2100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- docs/kubernetes/ingress-controller.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/kubernetes/ingress-controller.md b/docs/kubernetes/ingress-controller.md index 910d7ecc5e..a6e0b60319 100644 --- a/docs/kubernetes/ingress-controller.md +++ b/docs/kubernetes/ingress-controller.md @@ -312,6 +312,10 @@ run: kubectl create -f docs/kubernetes/deploy/deployment ``` +!!! note + The `extensions/v1beta1` and `networking.k8s.io/v1beta1` API versions of Ingress is no longer served as of v1.22. + In order to use `networking.k8s.io/v1` extension, you need to add `-kubernetes-ingress-v1` to the `skipper-ingress` container's arguments. This feature is added from skipper version v0.13.149 + Now, let's see what we have just deployed. This will create serviceaccount, PodSecurityPolicy and RBAC rules such that skipper-ingress is allowed to listen on the hostnetwork. From 608ac46dd277c0e0b6a6f002a7a3a703da64dbc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 14 Oct 2022 13:43:50 +0200 Subject: [PATCH 0095/1047] doc: group filters reference, such that you can navigate more easily (#2101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc: group filters reference, such that you can navigate more easily Signed-off-by: Sandor Szücs --- docs/reference/filters.md | 992 ++++++++++++++++++++------------------ 1 file changed, 515 insertions(+), 477 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 128249ba34..50f9d012d1 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -48,56 +48,38 @@ Example route that creates header from query parameter: r: Path("/redirect") && QueryParam("to") -> status(303) -> setResponseHeader("Location", "${request.query.to}") -> ; ``` -## backendIsProxy +## status -Notifies the proxy that the backend handling this request is also a -proxy. The proxy type is based in the URL scheme which can be either -`http`, `https` or `socks5`. +Sets the response status code to the given value, with no regards to the backend response. -Keep in mind that Skipper currently cannot handle `CONNECT` requests -by tunneling the traffic to the target destination, however, the -`CONNECT` requests can be forwarded to a different proxy using this -filter. +Parameters: +* status code (int) Example: ``` -foo1: - * - -> backendIsProxy() - -> "http://proxy.example.com"; - -foo2: - * - -> backendIsProxy() - -> ; - -foo3: - * - -> setDynamicBackendUrl("http://proxy.example.com") - -> backendIsProxy() - -> ; +route1: Host(/^all401\.example\.org$/) -> status(401) -> ; ``` +## HTTP Headers +### preserveHost -## blockContent - -Block a request based on it's body content. +Sets the incoming `Host: ` header on the outgoing backend connection. -The filter max buffer size is 2MiB by default and can be overidden with `-max-matcher-buffer-size=`. +It can be used to override the `proxyPreserveHost` behavior for individual routes. -Parameters: +Parameters: "true" or "false" -* toblockList (List of strings) +* "true" - use the Host header from the incoming request +* "false" - use the host from the backend address Example: - ``` -* -> blockContent("Malicious Content") -> "http://example.com"; +route1: * -> preserveHost("true") -> "http://backend.example.org"; ``` -## modRequestHeader +### modRequestHeader Replace all matched regex expressions in the given header. @@ -113,7 +95,7 @@ Example: enforce_www: * -> modRequestHeader("Host", "^zalando\.(\w+)$", "www.zalando.$1") -> redirectTo(301); ``` -## setRequestHeader +### setRequestHeader Set headers for requests. @@ -136,12 +118,12 @@ foo: * -> setRequestHeader("X-Passed-Skipper", "true") -> "https://backend.examp Path("/resource/:id") -> setRequestHeader("X-Resource-Id", "${id}") -> clusterClientRatelimit("resource", 10, "1m", "X-Resource-Id") -> "https://backend.example.org"; ``` -## appendRequestHeader +### appendRequestHeader Same as [setRequestHeader](#setrequestheader), but appends the provided value to the already existing ones. -## dropRequestHeader +### dropRequestHeader Removes a header from the request @@ -155,7 +137,7 @@ Example: foo: * -> dropRequestHeader("User-Agent") -> "https://backend.example.org"; ``` -## modResponseHeader +### modResponseHeader Same as [modRequestHeader](#modrequestheader), only for responses @@ -171,7 +153,7 @@ Example: do_not_avoid_caching: * -> modResponseHeader("cache-control", "no-cache", "cache") -> "https://zalando.de"; ``` -## setResponseHeader +### setResponseHeader Same as [setRequestHeader](#setrequestheader), only for responses @@ -185,15 +167,15 @@ set_cookie_with_path_param: -> ``` -## appendResponseHeader +### appendResponseHeader Same as [appendRequestHeader](#appendrequestheader), only for responses -## dropResponseHeader +### dropResponseHeader Same as [dropRequestHeader](#droprequestheader) but for responses from the backend -## setContextRequestHeader +### setContextRequestHeader Set headers for requests using values from the filter context (state bag). If the provided key (second parameter) cannot be found in the state bag, then it doesn't @@ -213,20 +195,20 @@ the header of the outgoing request with the X-Uid name: foo: * -> oauthTokeninfoAllScope("address_service.all") -> setContextRequestHeader("X-Uid", "auth-user") -> "https://backend.example.org"; ``` -## appendContextRequestHeader +### appendContextRequestHeader Same as [setContextRequestHeader](#setcontextrequestheader), but appends the provided value to the already existing ones. -## setContextResponseHeader +### setContextResponseHeader Same as [setContextRequestHeader](#setcontextrequestheader), except for responses. -## appendContextResponseHeader +### appendContextResponseHeader Same as [appendContextRequestHeader](#appendcontextrequestheader), except for responses. -## copyRequestHeader +### copyRequestHeader Copies value of a given request header to another header. @@ -241,206 +223,155 @@ Example: foo: * -> copyRequestHeader("X-Foo", "X-Bar") -> "https://backend.example.org"; ``` -## copyResponseHeader +### copyResponseHeader Same as [copyRequestHeader](#copyrequestheader), except for responses. -## modPath +### corsOrigin -Replace all matched regex expressions in the path. +The filter accepts an optional variadic list of acceptable origin +parameters. If the input argument list is empty, the header will +always be set to `*` which means any origin is acceptable. Otherwise +the header is only set if the request contains an Origin header and +its value matches one of the elements in the input list. The header is +only set on the response. Parameters: -* the expression to match (regex) -* the replacement (string) +* url (variadic string) -Example: +Examples: ``` -rm_api: Path("/api") -> modPath("/api", "/") -> "https://backend.example.org"; -append_bar: Path("/foo") -> modPath("/foo", "/foo/bar") -> "https://backend.example.org"; -new_base: PathSubtree("/base") -> modPath("/base", "/new/base) -> "https://backend.example.org"; -rm_api_regex: Path("/api") -> modPath("^/api/(.*)/v2$", "/$1") -> "https://backend.example.org"; +corsOrigin() +corsOrigin("https://www.example.org") +corsOrigin("https://www.example.org", "http://localhost:9001") ``` -## setPath +### headerToQuery -Replace the path of the original request to the replacement. - -Parameters: - -* the replacement (string) - -The replacement may contain [template placeholders](#template-placeholders). -If a template placeholder can't be resolved then empty value is used for it. - -## redirectTo - -Creates an HTTP redirect response. +Filter which assigns the value of a given header from the incoming Request to a given query param Parameters: -* redirect status code (int) -* location (string) - optional +* The name of the header to pick from request +* The name of the query param key to add to request -Example: +Examples: ``` -redirect1: PathRegexp(/^\/foo\/bar/) -> redirectTo(302, "/foo/newBar") -> ; -redirect2: * -> redirectTo(301) -> ; +headerToQuery("X-Foo-Header", "foo-query-param") ``` -- Route redirect1 will do a redirect with status code 302 to https - with new path `/foo/newBar` for requests, that match the path `/foo/bar`. -- Route redirect2 will do a `https` redirect with status code 301 for all - incoming requests that match no other route - -see also [redirect-handling](../tutorials/common-use-cases.md#redirect-handling) +The above filter will set `foo-query-param` query param respectively to the `X-Foo-Header` header +and will override the value if the queryparam exists already -## redirectToLower +### flowId -Same as [redirectTo](#redirectto), but replaces all strings to lower case. - -## static +Sets an X-Flow-Id header, if it's not already in the request. +This allows you to have a trace in your logs, that traces from +the incoming request on the edge to all backend services. -Serves static content from the filesystem. +Flow IDs must be in a certain format to be reusable in skipper. Valid formats +depend on the generator used in skipper. Default generator creates IDs of +length 16 matching the following regex: `^[0-9a-zA-Z+-]+$` Parameters: -* Request path to strip (string) -* Target base path in the filesystem (string) +* no parameter: resets always the X-Flow-Id header to a new value +* `"reuse"`: only create X-Flow-Id header if not already set or if the value is invalid in the request Example: -This serves files from `/srv/www/dehydrated` when requested via `/.well-known/acme-challenge/`, -e.g. the request `GET /.well-known/acme-challenge/foo` will serve the file `/srv/www/dehydrated/foo`. ``` -acme: Host(/./) && Method("GET") && Path("/.well-known/acme-challenge/*") - -> static("/.well-known/acme-challenge/", "/srv/www/dehydrated") -> ; +* -> flowId() -> "https://some-backend.example.org"; +* -> flowId("reuse") -> "https://some-backend.example.org"; ``` -Notes: - -* redirects to the directory when a file `index.html` exists and it is requested, i.e. `GET /foo/index.html` redirects to `/foo/` which serves then the `/foo/index.html` -* serves the content of the `index.html` when a directory is requested -* does a simple directory listing of files / directories when no `index.html` is present +### xforward -## stripQuery +Standard proxy headers. Appends the client remote IP to the X-Forwarded-For and sets the X-Forwarded-Host +header. -Removes the query parameter from the request URL, and if the first filter -parameter is `"true"`, preserves the query parameter in the form of -`x-query-param-: ` headers, so that `?foo=bar` -becomes `x-query-param-foo: bar` +### xforwardFirst -Example: -``` -* -> stripQuery() -> "http://backend.example.org"; -* -> stripQuery("true") -> "http://backend.example.org"; -``` +Same as [xforward](#xforward), but instead of appending the last remote IP, it prepends it to comply with the +approach of certain LB implementations. -## preserveHost -Sets the incoming `Host: ` header on the outgoing backend connection. +## HTTP Path +### modPath -It can be used to override the `proxyPreserveHost` behavior for individual routes. +Replace all matched regex expressions in the path. -Parameters: "true" or "false" +Parameters: -* "true" - use the Host header from the incoming request -* "false" - use the host from the backend address +* the expression to match (regex) +* the replacement (string) Example: + ``` -route1: * -> preserveHost("true") -> "http://backend.example.org"; +rm_api: Path("/api") -> modPath("/api", "/") -> "https://backend.example.org"; +append_bar: Path("/foo") -> modPath("/foo", "/foo/bar") -> "https://backend.example.org"; +new_base: PathSubtree("/base") -> modPath("/base", "/new/base) -> "https://backend.example.org"; +rm_api_regex: Path("/api") -> modPath("^/api/(.*)/v2$", "/$1") -> "https://backend.example.org"; ``` -## status +### setPath -Sets the response status code to the given value, with no regards to the backend response. +Replace the path of the original request to the replacement. Parameters: -* status code (int) - -Example: - -``` -route1: Host(/^all401\.example\.org$/) -> status(401) -> ; -``` - -## compress +* the replacement (string) -The filter, when executed on the response path, checks if the response entity can -be compressed. To decide, it checks the Content-Encoding, the Cache-Control and -the Content-Type headers. It doesn't compress the content if the Content-Encoding -is set to other than identity, or the Cache-Control applies the no-transform -pragma, or the Content-Type is set to an unsupported value. +The replacement may contain [template placeholders](#template-placeholders). +If a template placeholder can't be resolved then empty value is used for it. -The default supported content types are: `text/plain`, `text/html`, `application/json`, -`application/javascript`, `application/x-javascript`, `text/javascript`, `text/css`, -`image/svg+xml`, `application/octet-stream`. +## HTTP Redirect +### redirectTo -The default set of MIME types can be reset or extended by passing in the desired -types as filter arguments. When extending the defaults, the first argument needs to -be `"..."`. E.g. to compress tiff in addition to the defaults: - -``` -* -> compress("...", "image/tiff") -> "https://www.example.org" -``` +Creates an HTTP redirect response. -To reset the supported types, e.g. to compress only HTML, the "..." argument needs -to be omitted: +Parameters: -``` -* -> compress("text/html") -> "https://www.example.org" -``` +* redirect status code (int) +* location (string) - optional -It is possible to control the compression level, by setting it as the first filter -argument, in front of the MIME types. The default compression level is best-speed. -The possible values are integers between 0 and 9 (inclusive), where 0 means -no-compression, 1 means best-speed and 11 means best-compression. Example: +Example: ``` -* -> compress(11, "image/tiff") -> "https://www.example.org" +redirect1: PathRegexp(/^\/foo\/bar/) -> redirectTo(302, "/foo/newBar") -> ; +redirect2: * -> redirectTo(301) -> ; ``` -The filter also checks the incoming request, if it accepts the supported encodings, -explicitly stated in the Accept-Encoding header. -The filter currently supports by default `gzip`, `deflate` and `br` (can be overridden with flag `compress-encodings`). -It does not assume that the client accepts any encoding if the -Accept-Encoding header is not set. It ignores * in the Accept-Encoding header. - -Supported encodings are prioritized on: -- quality value provided by client -- compress-encodings flag following order as provided if quality value is equal -- `gzip`, `deflate`, `br` in this order otherwise - -When compressing the response, it updates the response header. It deletes the -`Content-Length` value triggering the proxy to always return the response with chunked -transfer encoding, sets the Content-Encoding to the selected encoding and sets the -`Vary: Accept-Encoding` header, if missing. +- Route redirect1 will do a redirect with status code 302 to https + with new path `/foo/newBar` for requests, that match the path `/foo/bar`. +- Route redirect2 will do a `https` redirect with status code 301 for all + incoming requests that match no other route -The compression happens in a streaming way, using only a small internal buffer. +see also [redirect-handling](../tutorials/common-use-cases.md#redirect-handling) -## decompress +### redirectToLower -The filter, when executed on the response path, checks if the response entity is -compressed by a supported algorithm (`gzip`, `deflate`, `br`). To decide, it checks the Content-Encoding -header. +Same as [redirectTo](#redirectto), but replaces all strings to lower case. -When compressing the response, it updates the response header. It deletes the -`Content-Length` value triggering the proxy to always return the response with chunked -transfer encoding, deletes the Content-Encoding and the Vary headers, if set. +## HTTP Query +### stripQuery -The decompression happens in a streaming way, using only a small internal buffer. +Removes the query parameter from the request URL, and if the first filter +parameter is `"true"`, preserves the query parameter in the form of +`x-query-param-: ` headers, so that `?foo=bar` +becomes `x-query-param-foo: bar` Example: - ``` -* -> decompress() -> "https://www.example.org" +* -> stripQuery() -> "http://backend.example.org"; +* -> stripQuery("true") -> "http://backend.example.org"; ``` -## setQuery +### setQuery Set the query string `?k=v` in the request to the backend to a given value. @@ -458,7 +389,7 @@ Example: setQuery("k", "v") ``` -## dropQuery +### dropQuery Delete the query string `?k=v` in the request to the backend for a given key. @@ -476,82 +407,36 @@ Example: dropQuery("k") ``` -## inlineContent - -Returns arbitrary content in the HTTP body. - -Parameters: - -* content (string) -* content type (string) - optional - -Example: +### queryToHeader -``` -* -> inlineContent("

Hello

") -> -* -> inlineContent("[1,2,3]", "application/json") -> -* -> status(418) -> inlineContent("Would you like a cup of tea?") -> -``` - -Content type will be automatically detected when not provided using https://mimesniff.spec.whatwg.org/#rules-for-identifying-an-unknown-mime-type algorithm. -Note that content detection algorithm does not contain any rules for recognizing JSON. - -!!! note - `inlineContent` filter sets the response on request path and starts the response path immediately. - The rest of the filter chain and backend are ignored and therefore `inlineContent` filter must be the last in the chain. - -## inlineContentIfStatus - -Returns arbitrary content in the HTTP body, if the response has the specified status code. - -Parameters: - -* status code (int) -* content (string) -* content type (string) - optional - -Example: - -``` -* -> inlineContentIfStatus(404, "

We don't have what you're looking for.

") -> "https://www.example.org" -* -> inlineContentIfStatus(401, "{\"error\": \"unauthorized\"}", "application/json") -> "https://www.example.org" -``` - -The content type will be automatically detected when not provided. - -## flowId - -Sets an X-Flow-Id header, if it's not already in the request. -This allows you to have a trace in your logs, that traces from -the incoming request on the edge to all backend services. - -Flow IDs must be in a certain format to be reusable in skipper. Valid formats -depend on the generator used in skipper. Default generator creates IDs of -length 16 matching the following regex: `^[0-9a-zA-Z+-]+$` +Filter which assigns the value of a given query param from the +incoming Request to a given Header with optional format string value. Parameters: -* no parameter: resets always the X-Flow-Id header to a new value -* `"reuse"`: only create X-Flow-Id header if not already set or if the value is invalid in the request +* The name of the query param key to pick from request +* The name of the header to add to request +* The format string used to create the header value, which gets the + value from the query value as before -Example: +Examples: ``` -* -> flowId() -> "https://some-backend.example.org"; -* -> flowId("reuse") -> "https://some-backend.example.org"; +queryToHeader("foo-query-param", "X-Foo-Header") +queryToHeader("access_token", "Authorization", "Bearer %s") ``` -## xforward - -Standard proxy headers. Appends the client remote IP to the X-Forwarded-For and sets the X-Forwarded-Host -header. +The first filter will set `X-Foo-Header` header respectively to the `foo-query-param` query param +and will not override the value if the header exists already. -## xforwardFirst +The second filter will set `Authorization` header to the +`access_token` query param with a prefix value `Bearer ` and will +not override the value if the header exists already. -Same as [xforward](#xforward), but instead of appending the last remote IP, it prepends it to comply with the -approach of certain LB implementations. +## Diagnostics +These filters are meant for diagnostic or load testing purposes -## randomContent +### randomContent Generate response with random text of specified length. @@ -565,7 +450,7 @@ Example: * -> randomContent(42) -> ; ``` -## repeatContent +### repeatContent Generate response of specified size from repeated text. @@ -580,7 +465,7 @@ Example: * -> repeatContent("I will not waste chalk. ", 1000) -> ; ``` -## backendTimeout +### backendTimeout Configure backend timeout. Skipper responds with `504 Gateway Timeout` status if obtaining a connection, sending the request, and reading the backend response headers and body takes longer than the configured timeout. @@ -597,7 +482,7 @@ Example: * -> backendTimeout("10ms") -> "https://www.example.org"; ``` -## latency +### latency Enable adding artificial latency @@ -612,7 +497,7 @@ Example: * -> latency("120ms") -> "https://www.example.org"; ``` -## bandwidth +### bandwidth Enable bandwidth throttling. @@ -626,7 +511,7 @@ Example: * -> bandwidth(30) -> "https://www.example.org"; ``` -## chunks +### chunks Enables adding chunking responses with custom chunk size with artificial delays in between response chunks. To disable delays, set @@ -644,22 +529,22 @@ Example: * -> chunks(1024, "0") -> "https://www.example.org"; ``` -## backendLatency +### backendLatency Same as [latency filter](#latency), but on the request path and not on the response path. -## backendBandwidth +### backendBandwidth Same as [bandwidth filter](#bandwidth), but on the request path and not on the response path. -## backendChunks +### backendChunks Same as [chunks filter](#chunks), but on the request path and not on the response path. -## absorb +### absorb The absorb filter reads and discards the payload of the incoming requests. It logs with INFO level and a unique ID per request: @@ -669,12 +554,12 @@ It logs with INFO level and a unique ID per request: - the finishing event of the request - any read errors other than EOF -## absorbSilent +### absorbSilent The absorbSilent filter reads and discards the payload of the incoming requests. It only logs read errors other than EOF. -## uniformRequestLatency +### uniformRequestLatency The uniformRequestLatency filter introduces uniformly distributed jitter latency within `[mean-delta, mean+delta]` interval for @@ -687,7 +572,7 @@ Example: * -> uniformRequestLatency("100ms", "10ms") -> "https://www.example.org"; ``` -## normalRequestLatency +### normalRequestLatency The normalRequestLatency filter introduces normally distributed jitter latency with configured mean value for requests. The first parameter @@ -700,7 +585,7 @@ Example: * -> normalRequestLatency("10ms", "5ms") -> "https://www.example.org"; ``` -## uniformResponseLatency +### uniformResponseLatency The uniformResponseLatency filter introduces uniformly distributed jitter latency within `[mean-delta, mean+delta]` interval for @@ -713,7 +598,7 @@ Example: * -> uniformRequestLatency("100ms", "10ms") -> "https://www.example.org"; ``` -## normalResponseLatency +### normalResponseLatency The normalResponseLatency filter introduces normally distributed jitter latency with configured mean value for responses. The first @@ -723,106 +608,263 @@ https://en.wikipedia.org/wiki/Normal_distribution. Example: ``` -* -> normalRequestLatency("10ms", "5ms") -> "https://www.example.org"; +* -> normalRequestLatency("10ms", "5ms") -> "https://www.example.org"; +``` + +### logHeader + +The logHeader filter prints the request line and the header, but not the body, to +stderr. Note that this filter should be used only in diagnostics setup and with care, +since the request headers may contain sensitive data, and they also can explode the +amount of logs. Authorization headers will be truncated in request and +response header logs. You can log request or response headers, which +defaults for backwards compatibility to request headers. + +Parameters: + +* no arg, similar to: "request" +* "request" or "response" (string varargs) + +Example: + +``` +* -> logHeader() -> "https://www.example.org"; +* -> logHeader("request") -> "https://www.example.org"; +* -> logHeader("response") -> "https://www.example.org"; +* -> logHeader("request", "response") -> "https://www.example.org"; +``` + +## Shadow Traffic +### tee + +Provides a unix-like `tee` feature for routing. + +Using this filter, the request will be sent to a "shadow" backend in addition +to the main backend of the route. + +Example: + +``` +* -> tee("https://audit-logging.example.org") -> "https://foo.example.org"; +``` + +This will send an identical request for foo.example.org to +audit-logging.example.org. Another use case could be using it for benchmarking +a new backend with some real traffic. This we call "shadow traffic". + +The above route will forward the request to `https://foo.example.org` as it +normally would do, but in addition to that, it will send an identical request to +`https://audit-logging.example.org`. The request sent to +`https://audit-logging.example.org` will receive the same method and headers, +and a copy of the body stream. The `tee` response is ignored for this shadow backend. + +It is possible to change the path of the tee request, in a similar way to the +[modPath](#modpath) filter: + +``` +Path("/api/v1") -> tee("https://api.example.org", "^/v1", "/v2" ) -> "http://api.example.org"; +``` + +In the above example, one can test how a new version of an API would behave on +incoming requests. + +### teenf + +The same as [tee filter](#tee), but does not follow redirects from the backend. + +### teeLoopback + +This filter provides a unix-like tee feature for routing, but unlike the [tee](#tee), +this filter feeds the copied request to the start of the routing, including the +route lookup and executing the filters on the matched route. + +It is recommended to use this solution instead of the tee filter, because the same +routing facilities are used for the outgoing tee requests as for the normal +requests, and all the filters and backend types are supported. + +To ensure that the right route, or one of the right set of routes, is matched +after the loopback, use the filter together with the [Tee](predicates.md#tee) +predicate, however, this is not mandatory if the request is changed via other +filters, such that other predicates ensure matching the right route. To avoid +infinite looping, the number of requests spawn from a single incoming request +is limited similarly as in case of the +[loopback backend](backends.md#loopback-backend). + +Parameters: + +* tee group (string): a label identifying which routes should match the loopback + request, marked with the [Tee](predicates.md#tee) predicate + +Example, generate shadow traffic from 10% of the production traffic: + +``` +main: * -> "https://main-backend.example.org; +main-split: Traffic(.1) -> teeLoopback("test-A") -> "https://main-backend.example.org"; +shadow: Tee("test-A") && True() -> "https://test-backend.example.org"; +``` + +See also: + +* [Tee predicate](predicates.md#tee) +* [Shadow Traffic Tutorial](../tutorials/shadow-traffic.md) + +## HTTP Body +### compress + +The filter, when executed on the response path, checks if the response entity can +be compressed. To decide, it checks the Content-Encoding, the Cache-Control and +the Content-Type headers. It doesn't compress the content if the Content-Encoding +is set to other than identity, or the Cache-Control applies the no-transform +pragma, or the Content-Type is set to an unsupported value. + +The default supported content types are: `text/plain`, `text/html`, `application/json`, +`application/javascript`, `application/x-javascript`, `text/javascript`, `text/css`, +`image/svg+xml`, `application/octet-stream`. + +The default set of MIME types can be reset or extended by passing in the desired +types as filter arguments. When extending the defaults, the first argument needs to +be `"..."`. E.g. to compress tiff in addition to the defaults: + +``` +* -> compress("...", "image/tiff") -> "https://www.example.org" +``` + +To reset the supported types, e.g. to compress only HTML, the "..." argument needs +to be omitted: + +``` +* -> compress("text/html") -> "https://www.example.org" +``` + +It is possible to control the compression level, by setting it as the first filter +argument, in front of the MIME types. The default compression level is best-speed. +The possible values are integers between 0 and 9 (inclusive), where 0 means +no-compression, 1 means best-speed and 11 means best-compression. Example: + +``` +* -> compress(11, "image/tiff") -> "https://www.example.org" +``` + +The filter also checks the incoming request, if it accepts the supported encodings, +explicitly stated in the Accept-Encoding header. +The filter currently supports by default `gzip`, `deflate` and `br` (can be overridden with flag `compress-encodings`). +It does not assume that the client accepts any encoding if the +Accept-Encoding header is not set. It ignores * in the Accept-Encoding header. + +Supported encodings are prioritized on: +- quality value provided by client +- compress-encodings flag following order as provided if quality value is equal +- `gzip`, `deflate`, `br` in this order otherwise + +When compressing the response, it updates the response header. It deletes the +`Content-Length` value triggering the proxy to always return the response with chunked +transfer encoding, sets the Content-Encoding to the selected encoding and sets the +`Vary: Accept-Encoding` header, if missing. + +The compression happens in a streaming way, using only a small internal buffer. + +### decompress + +The filter, when executed on the response path, checks if the response entity is +compressed by a supported algorithm (`gzip`, `deflate`, `br`). To decide, it checks the Content-Encoding +header. + +When compressing the response, it updates the response header. It deletes the +`Content-Length` value triggering the proxy to always return the response with chunked +transfer encoding, deletes the Content-Encoding and the Vary headers, if set. + +The decompression happens in a streaming way, using only a small internal buffer. + +Example: + +``` +* -> decompress() -> "https://www.example.org" +``` + +### static + +Serves static content from the filesystem. + +Parameters: + +* Request path to strip (string) +* Target base path in the filesystem (string) + +Example: + +This serves files from `/srv/www/dehydrated` when requested via `/.well-known/acme-challenge/`, +e.g. the request `GET /.well-known/acme-challenge/foo` will serve the file `/srv/www/dehydrated/foo`. +``` +acme: Host(/./) && Method("GET") && Path("/.well-known/acme-challenge/*") + -> static("/.well-known/acme-challenge/", "/srv/www/dehydrated") -> ; ``` -## logHeader +Notes: -The logHeader filter prints the request line and the header, but not the body, to -stderr. Note that this filter should be used only in diagnostics setup and with care, -since the request headers may contain sensitive data, and they also can explode the -amount of logs. Authorization headers will be truncated in request and -response header logs. You can log request or response headers, which -defaults for backwards compatibility to request headers. +* redirects to the directory when a file `index.html` exists and it is requested, i.e. `GET /foo/index.html` redirects to `/foo/` which serves then the `/foo/index.html` +* serves the content of the `index.html` when a directory is requested +* does a simple directory listing of files / directories when no `index.html` is present + +### inlineContent + +Returns arbitrary content in the HTTP body. Parameters: -* no arg, similar to: "request" -* "request" or "response" (string varargs) +* content (string) +* content type (string) - optional Example: ``` -* -> logHeader() -> "https://www.example.org"; -* -> logHeader("request") -> "https://www.example.org"; -* -> logHeader("response") -> "https://www.example.org"; -* -> logHeader("request", "response") -> "https://www.example.org"; +* -> inlineContent("

Hello

") -> +* -> inlineContent("[1,2,3]", "application/json") -> +* -> status(418) -> inlineContent("Would you like a cup of tea?") -> ``` -## tee - -Provides a unix-like `tee` feature for routing. +Content type will be automatically detected when not provided using https://mimesniff.spec.whatwg.org/#rules-for-identifying-an-unknown-mime-type algorithm. +Note that content detection algorithm does not contain any rules for recognizing JSON. -Using this filter, the request will be sent to a "shadow" backend in addition -to the main backend of the route. +!!! note + `inlineContent` filter sets the response on request path and starts the response path immediately. + The rest of the filter chain and backend are ignored and therefore `inlineContent` filter must be the last in the chain. -Example: +### inlineContentIfStatus -``` -* -> tee("https://audit-logging.example.org") -> "https://foo.example.org"; -``` +Returns arbitrary content in the HTTP body, if the response has the specified status code. -This will send an identical request for foo.example.org to -audit-logging.example.org. Another use case could be using it for benchmarking -a new backend with some real traffic. This we call "shadow traffic". +Parameters: -The above route will forward the request to `https://foo.example.org` as it -normally would do, but in addition to that, it will send an identical request to -`https://audit-logging.example.org`. The request sent to -`https://audit-logging.example.org` will receive the same method and headers, -and a copy of the body stream. The `tee` response is ignored for this shadow backend. +* status code (int) +* content (string) +* content type (string) - optional -It is possible to change the path of the tee request, in a similar way to the -[modPath](#modpath) filter: +Example: ``` -Path("/api/v1") -> tee("https://api.example.org", "^/v1", "/v2" ) -> "http://api.example.org"; +* -> inlineContentIfStatus(404, "

We don't have what you're looking for.

") -> "https://www.example.org" +* -> inlineContentIfStatus(401, "{\"error\": \"unauthorized\"}", "application/json") -> "https://www.example.org" ``` -In the above example, one can test how a new version of an API would behave on -incoming requests. - -## teenf - -The same as [tee filter](#tee), but does not follow redirects from the backend. - -## teeLoopback +The content type will be automatically detected when not provided. -This filter provides a unix-like tee feature for routing, but unlike the [tee](#tee), -this filter feeds the copied request to the start of the routing, including the -route lookup and executing the filters on the matched route. +### blockContent -It is recommended to use this solution instead of the tee filter, because the same -routing facilities are used for the outgoing tee requests as for the normal -requests, and all the filters and backend types are supported. +Block a request based on it's body content. -To ensure that the right route, or one of the right set of routes, is matched -after the loopback, use the filter together with the [Tee](predicates.md#tee) -predicate, however, this is not mandatory if the request is changed via other -filters, such that other predicates ensure matching the right route. To avoid -infinite looping, the number of requests spawn from a single incoming request -is limited similarly as in case of the -[loopback backend](backends.md#loopback-backend). +The filter max buffer size is 2MiB by default and can be overidden with `-max-matcher-buffer-size=`. Parameters: -* tee group (string): a label identifying which routes should match the loopback - request, marked with the [Tee](predicates.md#tee) predicate +* toblockList (List of strings) -Example, generate shadow traffic from 10% of the production traffic: +Example: ``` -main: * -> "https://main-backend.example.org; -main-split: Traffic(.1) -> teeLoopback("test-A") -> "https://main-backend.example.org"; -shadow: Tee("test-A") && True() -> "https://test-backend.example.org"; +* -> blockContent("Malicious Content") -> "http://example.com"; ``` -See also: - -* [Tee predicate](predicates.md#tee) -* [Shadow Traffic Tutorial](../tutorials/shadow-traffic.md) - -## sed +### sed The filter sed replaces all occurences of a pattern with a replacement string in the response body. @@ -859,7 +901,7 @@ The filter uses the go regular expression implementation: https://github.com/google/re2/wiki/Syntax . Due to the streaming nature, matches with zero length are ignored. -### Memory handling and limitations +#### Memory handling and limitations In order to avoid unbound buffering of unprocessed data, the sed* filters need to apply some limitations. Some patterns, e.g. `.*` would allow to match the complete @@ -883,7 +925,7 @@ use the delimited variant of the filters, e.g. for line based editing. If the max buffer handling is set to "abort", then the stream editing is stopped and the rest of the payload is dropped. -## sedDelim +### sedDelim Like [sed()](#sed), but it expects an additional argument, before the optional max buffer size argument, that is used to delimit chunks to be processed at once. The pattern @@ -896,7 +938,7 @@ Example: editorRoute: * -> sedDelim("foo", "bar", "\n") -> "https://www.example.org"; ``` -## sedRequest +### sedRequest Like [sed()](#sed), but for the request content. @@ -906,7 +948,7 @@ Example: editorRoute: * -> sedRequest("foo", "bar") -> "https://www.example.org"; ``` -## sedRequestDelim +### sedRequestDelim Like [sedDelim()](#seddelim), but for the request content. @@ -916,7 +958,8 @@ Example: editorRoute: * -> sedRequestDelim("foo", "bar", "\n") -> "https://www.example.org"; ``` -## basicAuth +## Authentication and Authorization +### basicAuth Enable Basic Authentication @@ -933,7 +976,7 @@ basicAuth("/path/to/htpasswd") basicAuth("/path/to/htpasswd", "My Website") ``` -## webhook +### webhook The `webhook` filter makes it possible to have your own authentication and authorization endpoint as a filter. @@ -959,7 +1002,13 @@ webhook("https://custom-webhook.example.org/auth", "X-Copy-Webhook-Header,X-Copy The webhook timeout has a default of 2 seconds and can be globally changed, if skipper is started with `-webhook-timeout=2s` flag. -## oauthTokeninfoAnyScope +### Tokeninfo + +Tokeninfo handled by another service. +The filters just validate the response from the tokeninfo +service to do authorization as defined in the filter. + +#### oauthTokeninfoAnyScope If skipper is started with `-oauth2-tokeninfo-url` flag, you can use this filter. @@ -980,7 +1029,7 @@ Examples: oauthTokeninfoAnyScope("s1", "s2", "s3") ``` -## oauthTokeninfoAllScope +#### oauthTokeninfoAllScope If skipper is started with `-oauth2-tokeninfo-url` flag, you can use this filter. @@ -1001,7 +1050,7 @@ Examples: oauthTokeninfoAllScope("s1", "s2", "s3") ``` -## oauthTokeninfoAnyKV +#### oauthTokeninfoAnyKV If skipper is started with `-oauth2-tokeninfo-url` flag, you can use this filter. @@ -1024,7 +1073,7 @@ oauthTokeninfoAnyKV("k1", "v1", "k2", "v2") oauthTokeninfoAnyKV("k1", "v1", "k1", "v2") ``` -## oauthTokeninfoAllKV +#### oauthTokeninfoAllKV If skipper is started with `-oauth2-tokeninfo-url` flag, you can use this filter. @@ -1046,7 +1095,13 @@ Examples: oauthTokeninfoAllKV("k1", "v1", "k2", "v2") ``` -## oauthTokenintrospectionAnyClaims +### Tokenintrospection + +Tokenintrospection handled by another service. +The filters just validate the response from the tokenintrospection +service to do authorization as defined in the filter. + +#### oauthTokenintrospectionAnyClaims The filter accepts variable number of string arguments, which are used to validate the incoming token from the `Authorization: Bearer @@ -1065,7 +1120,7 @@ Examples: oauthTokenintrospectionAnyClaims("https://accounts.google.com", "c1", "c2", "c3") ``` -## oauthTokenintrospectionAllClaims +#### oauthTokenintrospectionAllClaims The filter accepts variable number of string arguments, which are used to validate the incoming token from the `Authorization: Bearer @@ -1084,7 +1139,7 @@ Examples: oauthTokenintrospectionAllClaims("https://accounts.google.com", "c1", "c2", "c3") ``` -## oauthTokenintrospectionAnyKV +#### oauthTokenintrospectionAnyKV The filter accepts an even number of variable arguments of type string, which are used to validate the incoming token from the @@ -1105,7 +1160,7 @@ oauthTokenintrospectionAnyKV("https://accounts.google.com", "k1", "v1", "k2", "v oauthTokenintrospectionAnyKV("https://accounts.google.com", "k1", "v1", "k1", "v2") ``` -## oauthTokenintrospectionAllKV +#### oauthTokenintrospectionAllKV The filter accepts an even number of variable arguments of type string, which are used to validate the incoming token from the @@ -1125,7 +1180,7 @@ Examples: oauthTokenintrospectionAllKV("https://accounts.google.com", "k1", "v1", "k2", "v2") ``` -## secureOauthTokenintrospectionAnyClaims +#### secureOauthTokenintrospectionAnyClaims The filter accepts variable number of string arguments, which are used to validate the incoming token from the `Authorization: Bearer @@ -1153,7 +1208,7 @@ Read client-id and client-secret from environment variables secureOauthTokenintrospectionAnyClaims("issuerURL", "", "", "claim1", "claim2") ``` -## secureOauthTokenintrospectionAllClaims +#### secureOauthTokenintrospectionAllClaims The filter accepts variable number of string arguments, which are used to validate the incoming token from the `Authorization: Bearer @@ -1181,7 +1236,7 @@ Read client-id and client-secret from environment variables secureOauthTokenintrospectionAllClaims("issuerURL", "", "", "claim1", "claim2") ``` -## secureOauthTokenintrospectionAnyKV +#### secureOauthTokenintrospectionAnyKV The filter accepts an even number of variable arguments of type string, which are used to validate the incoming token from the @@ -1210,7 +1265,7 @@ Read client-id and client-secret from environment variables secureOauthTokenintrospectionAnyKV("issuerURL", "", "", "k1", "v1", "k2", "v2") ``` -## secureOauthTokenintrospectionAllKV +#### secureOauthTokenintrospectionAllKV The filter accepts an even number of variable arguments of type string, which are used to validate the incoming token from the @@ -1239,7 +1294,8 @@ Read client-id and client-secret from environment variables secureOauthTokenintrospectionAllKV("issuerURL", "", "", "k1", "v1", "k2", "v2") ``` -## jwtValidation +### JWT +#### jwtValidation The filter parses bearer jwt token from Authorization header and validates the signature using public keys discovered via /.well-known/openid-configuration endpoint. Takes issuer url as single parameter. @@ -1253,8 +1309,8 @@ jwtValidation("https://login.microsoftonline.com/{tenantId}/v2.0") ``` - -## forwardToken +### Forward Token Data +#### forwardToken The filter takes the header name as its first argument and sets header value to the token info or token introspection result serialized as a JSON object. @@ -1270,7 +1326,7 @@ forwardToken("X-Tokeninfo-Forward") forwardToken("X-Tokeninfo-Forward", "access_token", "token_type") ``` -## forwardTokenField +#### forwardTokenField The filter takes a header name and a field as its first and second arguments. The corresponding field from the result of token info, token introspection or oidc user info is added as corresponding header when the request is passed to the backend. @@ -1286,7 +1342,8 @@ Examples: forwardTokenField("X-Tokeninfo-Forward-Oid", "oid") -> forwardTokenField("X-Tokeninfo-Forward-Sub", "sub") ``` -## oauthGrant +### OAuth2 +#### oauthGrant Enables authentication and authorization with an OAuth2 authorization code grant flow as specified by [RFC 6749 Section 1.3.1](https://tools.ietf.org/html/rfc6749#section-1.3.1). @@ -1339,7 +1396,7 @@ Skipper arguments: | `-oauth2-callback-path` | no | path of the Skipper route containing the `grantCallback()` filter for accepting an authorization code and using it to get an access token. Example: `-oauth2-callback-path=/oauth/callback` | | `-oauth2-token-cookie-name` | no | the name of the cookie where the access tokens should be stored in encrypted form. Default: `oauth-grant`. Example: `-oauth2-token-cookie-name=SESSION` | -## grantCallback +#### grantCallback The filter accepts authorization codes as a result of an OAuth2 authorization code grant flow triggered by [oauthGrant](#oauthgrant). It uses the code to request access and @@ -1363,7 +1420,7 @@ Skipper arguments: | -------- | --------- | ----------- | | `-oauth2-callback-path` | no | path of the Skipper route containing the `grantCallback()` filter. Example: `-oauth2-callback-path=/oauth/callback` | -## grantLogout +#### grantLogout The filter revokes the refresh and access tokens in the cookie set by [oauthGrant](#oauthgrant). It also deletes the cookie by setting the `Set-Cookie` @@ -1381,7 +1438,7 @@ Skipper arguments: | -------- | --------- | ----------- | | `-oauth2-revoke-token-url` | **yes** | URL of the OAuth2 provider's token revocation endpoint. Example: `-oauth2-revoke-token-url=https://identity.example.com/oauth2/revoke` | -## grantClaimsQuery +#### grantClaimsQuery The filter allows defining access control rules based on claims in a tokeninfo JSON payload. @@ -1402,7 +1459,8 @@ Skipper arguments: | -------- | --------- | ----------- | | `-oauth2-tokeninfo-subject-key` | **yes** | the key of the attribute containing the OAuth2 subject ID in the OAuth2 provider's tokeninfo JSON payload. Default: `uid`. Example: `-oauth2-tokeninfo-subject-key=sub` | -## oauthOidcUserInfo +### OpenID Connect +#### oauthOidcUserInfo ``` oauthOidcUserInfo("https://oidc-provider.example.com", "client_id", "client_secret", @@ -1423,7 +1481,7 @@ The filter needs the following parameters: * **Upstream Headers** (optional) The upstream endpoint will receive these headers which values are parsed from the OIDC information. The header definition can be one or more header-query pairs, space delimited. The query syntax is [GJSON](https://github.com/tidwall/gjson/blob/master/SYNTAX.md). * **SubdomainsToRemove** (optional, default "1") Configures number of subdomains to remove from the request hostname to derive OIDC cookie domain. By default one subdomain is removed, e.g. for the www.example.com request hostname the OIDC cookie domain will be example.com (to support SSO for all subdomains of the example.com). Configure "0" to use the same hostname. Note that value is a string. -## oauthOidcAnyClaims +#### oauthOidcAnyClaims ``` oauthOidcAnyClaims("https://oidc-provider.example.com", "client_id", "client_secret", @@ -1443,7 +1501,7 @@ The filter needs the following parameters: * **Auth Code Options** (optional) Passes key/value parameters to a provider's authorization endpoint. The value can be dynamically set by a query parameter with the same key name if the placeholder `skipper-request-query` is used. * **Upstream Headers** (optional) The upstream endpoint will receive these headers which values are parsed from the OIDC information. The header definition can be one or more header-query pairs, space delimited. The query syntax is [GJSON](https://github.com/tidwall/gjson/blob/master/SYNTAX.md). -## oauthOidcAllClaims +#### oauthOidcAllClaims ``` oauthOidcAllClaims("https://oidc-provider.example.com", "client_id", "client_secret", @@ -1463,22 +1521,7 @@ The filter needs the following parameters: * **Auth Code Options** (optional) Passes key/value parameters to a provider's authorization endpoint. The value can be dynamically set by a query parameter with the same key name if the placeholder `skipper-request-query` is used. * **Upstream Headers** (optional) The upstream endpoint will receive these headers which values are parsed from the OIDC information. The header definition can be one or more header-query pairs, space delimited. The query syntax is [GJSON](https://github.com/tidwall/gjson/blob/master/SYNTAX.md). -## requestCookie - -Append a cookie to the request header. - -Parameters: - -* cookie name (string) -* cookie value (string) - -Example: - -``` -requestCookie("test-session", "abc") -``` - -## oidcClaimsQuery +#### oidcClaimsQuery ``` oidcClaimsQuery(":[]", ...) @@ -1519,7 +1562,23 @@ oidcClaimsQuery("/:name%\"*One\"", "/path:groups.#[%\"*-Test-Users\"] groups.#[= As of now there is no negative/deny rule possible. The first matching path is evaluated against the defined query/queries and if positive, permitted. -## responseCookie +## Cookie Handling +### requestCookie + +Append a cookie to the request header. + +Parameters: + +* cookie name (string) +* cookie value (string) + +Example: + +``` +requestCookie("test-session", "abc") +``` + +### responseCookie Appends cookies to responses in the "Set-Cookie" header. The response cookie accepts an optional argument to control the max-age property of the cookie, @@ -1536,7 +1595,7 @@ responseCookie("test-session", "abc", 31536000), responseCookie("test-session", "abc", 31536000, "change-only") ``` -## jsCookie +### jsCookie The JS cookie behaves exactly as the response cookie, but it does not set the `HttpOnly` directive, so these cookies will be accessible from JS code running @@ -1548,7 +1607,8 @@ Example: jsCookie("test-session-info", "abc-debug", 31536000, "change-only") ``` -## consecutiveBreaker +## Circuit Breakers +### consecutiveBreaker This breaker opens when the proxy could not connect to a backend or received a >=500 status code at least N times in a row. When open, the proxy returns @@ -1569,7 +1629,7 @@ See also the [circuit breaker docs](https://godoc.org/github.com/zalando/skipper Can be used as [egress](egress.md) feature. -## rateBreaker +### rateBreaker The "rate breaker" works similar to the [consecutiveBreaker](#consecutivebreaker), but instead of considering N consecutive failures for going open, it maintains a sliding @@ -1589,7 +1649,7 @@ See also the [circuit breaker docs](https://godoc.org/github.com/zalando/skipper Can be used as [egress](egress.md) feature. -## disableBreaker +### disableBreaker Change (or set) the breaker configurations for an individual route and disable for another, in eskip: @@ -1607,7 +1667,7 @@ See also the [circuit breaker docs](https://godoc.org/github.com/zalando/skipper Can be used as [egress](egress.md) feature. -## rate limits +## Rate Limit ### ~~localRatelimit~~ @@ -1900,7 +1960,7 @@ In case `clusterRatelimit` could not reach the swarm (f.e. redis): * Route `fail_open` will allow the request * Route `fail_closed` will deny the request -## shedder +## Load Shedding The basic idea of load shedding is to reduce errors by early stopping some of the ingress requests that create too much load and serving @@ -1976,72 +2036,9 @@ probability you have to use values lower than 1: See [the scripts page](scripts.md) -## corsOrigin - -The filter accepts an optional variadic list of acceptable origin -parameters. If the input argument list is empty, the header will -always be set to `*` which means any origin is acceptable. Otherwise -the header is only set if the request contains an Origin header and -its value matches one of the elements in the input list. The header is -only set on the response. - -Parameters: - -* url (variadic string) - -Examples: - -``` -corsOrigin() -corsOrigin("https://www.example.org") -corsOrigin("https://www.example.org", "http://localhost:9001") -``` - -## headerToQuery - -Filter which assigns the value of a given header from the incoming Request to a given query param - -Parameters: - -* The name of the header to pick from request -* The name of the query param key to add to request - -Examples: - -``` -headerToQuery("X-Foo-Header", "foo-query-param") -``` - -The above filter will set `foo-query-param` query param respectively to the `X-Foo-Header` header -and will override the value if the queryparam exists already - -## queryToHeader - -Filter which assigns the value of a given query param from the -incoming Request to a given Header with optional format string value. - -Parameters: - -* The name of the query param key to pick from request -* The name of the header to add to request -* The format string used to create the header value, which gets the - value from the query value as before - -Examples: - -``` -queryToHeader("foo-query-param", "X-Foo-Header") -queryToHeader("access_token", "Authorization", "Bearer %s") -``` -The first filter will set `X-Foo-Header` header respectively to the `foo-query-param` query param -and will not override the value if the header exists already. - -The second filter will set `Authorization` header to the -`access_token` query param with a prefix value `Bearer ` and will -not override the value if the header exists already. - -## ~~accessLogDisabled~~ +## Logs +### ~~accessLogDisabled~~ **Deprecated:** use [disableAccessLog](#disableaccesslog) or [enableAccessLog](#enableaccesslog) @@ -2054,7 +2051,7 @@ Example: accessLogDisabled("false") ``` -## disableAccessLog +### disableAccessLog Filter overrides global Skipper `AccessLogDisabled` setting and allows to turn-off the access log for specific route while access log, in general, is enabled. It is also possible to disable access logs only for a subset of response codes @@ -2073,7 +2070,7 @@ disableAccessLog(1, 301, 40) This disables logs of all requests with status codes `1xxs`, `301` and all `40xs`. -## enableAccessLog +### enableAccessLog Filter overrides global Skipper `AccessLogDisabled` setting and allows to turn-on the access log for specific route while access log, in general, is disabled. It is also possible to enable access logs only for a subset of response codes @@ -2092,7 +2089,7 @@ enableAccessLog(1, 301, 20) This enables logs of all requests with status codes `1xxs`, `301` and all `20xs`. -## auditLog +### auditLog Filter `auditLog()` logs the request and N bytes of the body into the log file. N defaults to 1024 and can be overidden with @@ -2104,7 +2101,7 @@ Example: auditLog() ``` -## unverifiedAuditLog +### unverifiedAuditLog Filter `unverifiedAuditLog()` adds a Header, `X-Unverified-Audit`, to the request, the content of which, will also be written to the log file. By default, the value of the audit header will be equal to the value of the `sub` key, from @@ -2125,7 +2122,40 @@ unverifiedAuditLog() unverifiedAuditLog("azp") ``` -## setDynamicBackendHostFromHeader +## Backend +### backendIsProxy + +Notifies the proxy that the backend handling this request is also a +proxy. The proxy type is based in the URL scheme which can be either +`http`, `https` or `socks5`. + +Keep in mind that Skipper currently cannot handle `CONNECT` requests +by tunneling the traffic to the target destination, however, the +`CONNECT` requests can be forwarded to a different proxy using this +filter. + + +Example: + +``` +foo1: + * + -> backendIsProxy() + -> "http://proxy.example.com"; + +foo2: + * + -> backendIsProxy() + -> ; + +foo3: + * + -> setDynamicBackendUrl("http://proxy.example.com") + -> backendIsProxy() + -> ; +``` + +### setDynamicBackendHostFromHeader Filter sets the backend host for a route, value is taken from the provided header. Can be used only with `` backend. Meant to be used together with [setDynamicBackendSchemeFromHeader](#setdynamicbackendschemefromheader) @@ -2142,7 +2172,7 @@ Example: foo: * -> setDynamicBackendHostFromHeader("X-Forwarded-Host") -> ; ``` -## setDynamicBackendSchemeFromHeader +### setDynamicBackendSchemeFromHeader Filter sets the backend scheme for a route, value is taken from the provided header. Can be used only with `` backend. Meant to be used together with [setDynamicBackendHostFromHeader](#setdynamicbackendhostfromheader) @@ -2159,7 +2189,7 @@ Example: foo: * -> setDynamicBackendSchemeFromHeader("X-Forwarded-Proto") -> ; ``` -## setDynamicBackendUrlFromHeader +### setDynamicBackendUrlFromHeader Filter sets the backend url for a route, value is taken from the provided header. Can be used only with `` backend. @@ -2174,7 +2204,7 @@ Example: foo: * -> setDynamicBackendUrlFromHeader("X-Custom-Url") -> ; ``` -## setDynamicBackendHost +### setDynamicBackendHost Filter sets the backend host for a route. Can be used only with `` backend. Meant to be used together with [setDynamicBackendSchemeFromHeader](#setdynamicbackendschemefromheader) @@ -2191,7 +2221,7 @@ Example: foo: * -> setDynamicBackendHost("example.com") -> ; ``` -## setDynamicBackendScheme +### setDynamicBackendScheme Filter sets the backend scheme for a route. Can be used only with `` backend. Meant to be used together with [setDynamicBackendHostFromHeader](#setdynamicbackendhostfromheader) @@ -2208,7 +2238,7 @@ Example: foo: * -> setDynamicBackendScheme("https") -> ; ``` -## setDynamicBackendUrl +### setDynamicBackendUrl Filter sets the backend url for a route. Can be used only with `` backend. @@ -2437,7 +2467,26 @@ E.g.: apiUsageMonitoring.custom.my-app.{unknown}.{unknown}.GET.{no-match}.*.*.http_count ``` -## fifo +## originMarker + +This filter is used to measure the time it took to create a route. Other than that, it's a no-op. +You can include the same origin marker when you re-create the route. As long as the `origin` and `id` are the same, the route creation time will not be measured again. +If there are multiple origin markers with the same origin, the earliest timestamp will be used. + +Parameters: + +* the name of the origin +* the ID of the object that is the logical source for the route +* the creation timestamp (rfc3339) + +Example: + +``` +originMarker("apiUsageMonitoring", "deployment1", "2019-08-30T09:55:51Z") +``` + +## Scheduler +### fifo This Filter is similar to the [lifo](#lifo) filter in regards to parameters and status codes. @@ -2460,7 +2509,7 @@ Example: fifo(100, 150, "10s") ``` -## lifo +### lifo This Filter changes skipper to handle the route with a bounded last in first out queue (LIFO), instead of an unbounded first in first out @@ -2495,7 +2544,7 @@ to 150 and Timeout to 10 seconds. When there are multiple lifo filters on the route, only the last one will be applied. -## lifoGroup +### lifoGroup This filter is similar to the [lifo](#lifo) filter. @@ -2526,7 +2575,8 @@ It is possible to use the lifoGroup filter together with the single lifo filter, a route belongs to a group, but needs to have additional stricter settings then the whole group. -## rfcHost +## RFC Compliance +### rfcHost This filter removes the optional trailing dot in the outgoing host header. @@ -2539,7 +2589,7 @@ rfcHost() ``` -## rfcPath +### rfcPath This filter forces an alternative interpretation of the RFC 2616 and RFC 3986 standards, where paths containing reserved characters will have these characters unescaped when the @@ -2571,7 +2621,8 @@ It is also possible to enable this behavior centrally for a Skipper instance wit the -rfc-patch-path flag. See [URI standards interpretation](../operation/operation.md#uri-standards-interpretation). -## bearerinjector +## Egress +### bearerinjector This filter injects `Bearer` tokens into `Authorization` headers read from file providing the token as content. This is only for use cases @@ -2597,7 +2648,8 @@ have to be filenames `write-token` and `read-token` within the specified credential paths `/tmp/secrets/`, resulting in `/tmp/secrets/write-token` and `/tmp/secrets/read-token`. -## tracingBaggageToTag +## Open Tracing +### tracingBaggageToTag This filter adds an opentracing tag for a given baggage item in the trace. @@ -2611,7 +2663,7 @@ Example: If a trace consists of baggage item named `foo` with a value `bar`. Add tracingBaggageToTag("foo", "baz") ``` -## stateBagToTag +### stateBagToTag This filter sets an opentracing tag from the filter context (state bag). If the provided key (first parameter) cannot be found in the state bag, then it doesn't set the tag. @@ -2630,7 +2682,7 @@ the opentracing tag "client_id": foo: * -> oauthTokeninfoAllScope("address_service.all") -> stateBagToTag("auth-user", "client_id") -> "https://backend.example.org"; ``` -## tracingTag +### tracingTag This filter adds an opentracing tag. @@ -2652,7 +2704,7 @@ Example: Set tag from request header tracingTag("http.flow_id", "${request.header.X-Flow-Id}") ``` -## tracingSpanName +### tracingSpanName This filter sets the name of the outgoing (client span) in opentracing. The default name is "proxy". Example: @@ -2660,25 +2712,11 @@ This filter sets the name of the outgoing (client span) in opentracing. The defa tracingSpanName("api-operation") ``` -## originMarker - -This filter is used to measure the time it took to create a route. Other than that, it's a no-op. -You can include the same origin marker when you re-create the route. As long as the `origin` and `id` are the same, the route creation time will not be measured again. -If there are multiple origin markers with the same origin, the earliest timestamp will be used. - -Parameters: - -* the name of the origin -* the ID of the object that is the logical source for the route -* the creation timestamp (rfc3339) - -Example: +## Load Balancing -``` -originMarker("apiUsageMonitoring", "deployment1", "2019-08-30T09:55:51Z") -``` +Some filters influence how load balancing will be done -## fadeIn +### fadeIn When this filter is set, and the route has a load balanced backend, then the newly added endpoints will receive the traffic in a gradually increasing way, starting from their detection for the specified duration, after which @@ -2718,7 +2756,7 @@ of your deployment or stackset, according to your fadeIn duration. ![Rolling Restart and Fade-In](../img/fadein_traffic_skew.png) -## endpointCreated +### endpointCreated This filter marks the creation time of a load balanced endpoint. When used together with the fadeIn filter, it prevents missing the detection of a new backend instance with the same hostname. This filter is @@ -2735,7 +2773,7 @@ Example: endpointCreated("http://10.0.0.1:8080", "2020-12-18T15:30:00Z01:00") ``` -## consistentHashKey +### consistentHashKey This filter sets the request key used by the [`consistentHash`](backends.md#load-balancer-backend) algorithm to select the backend endpoint. @@ -2759,7 +2797,7 @@ consistentHashKey("${request.header.Authorization}") consistentHashKey("${request.source}") // same as the default key ``` -## consistentHashBalanceFactor +### consistentHashBalanceFactor This filter sets the balance factor used by the [`consistentHash`](backends.md#load-balancer-backend) algorithm to prevent a single backend endpoint from being overloaded. The number of in-flight requests for an endpoint can be no higher than `(average-in-flight-requests * balanceFactor) + 1`. From ae00af96464d0d2bdfa0c2c3e3578cce631d793e Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 14 Oct 2022 17:52:47 +0200 Subject: [PATCH 0096/1047] net: add custom open tracing tag example (#2103) Updates https://github.com/zalando/skipper/issues/2102 Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- net/httpclient_example_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/net/httpclient_example_test.go b/net/httpclient_example_test.go index 21fec1514b..bdd836c4b3 100644 --- a/net/httpclient_example_test.go +++ b/net/httpclient_example_test.go @@ -1,6 +1,7 @@ package net_test import ( + "fmt" "log" stdlibnet "net" "net/http" @@ -8,6 +9,8 @@ import ( "time" "github.com/lightstep/lightstep-tracer-go" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/net" "github.com/zalando/skipper/secrets" ) @@ -194,6 +197,35 @@ func ExampleClient_staticSecret() { } } +type customTracer struct { + opentracing.Tracer +} + +func (t *customTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { + span := t.Tracer.StartSpan(operationName, opts...) + span.SetTag("customtag", "test") + return span +} + +func ExampleClient_customTracer() { + mockTracer := mocktracer.New() + cli := net.NewClient(net.Options{ + Tracer: &customTracer{mockTracer}, + OpentracingSpanName: "clientSpan", + }) + defer cli.Close() + + srv := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) + defer srv.Close() + + cli.Get("http://" + srv.Listener.Addr().String() + "/") + + fmt.Printf("customtag: %s", mockTracer.FinishedSpans()[0].Tags()["customtag"]) + + // Output: + // customtag: test +} + type testSecretsReader struct { h map[string][]byte } From d5e7d48f1cd860b5e29d5da52171ac48c5cf24b8 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Sun, 16 Oct 2022 02:02:35 +0200 Subject: [PATCH 0097/1047] proxy: fix connection upgrade test for go 1.19 (#2105) Mikkel Oscar Lyderik Larsen identified the change https://github.com/golang/go/commit/770e0e584a98dfd5e8d0d00558085c339fda0ed7 that processes 1xx response status headers differently. This change refactors tests and adds handling of non-standard `101 Switching Protocols` response with `Connection: close` header. Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- proxy/upgrade.go | 7 ++ proxy/upgrade_test.go | 161 +++++++++++++++++++----------------------- 2 files changed, 79 insertions(+), 89 deletions(-) diff --git a/proxy/upgrade.go b/proxy/upgrade.go index ba6a5e0ccf..c25f3706d0 100644 --- a/proxy/upgrade.go +++ b/proxy/upgrade.go @@ -132,6 +132,13 @@ func (p *upgradeProxy) serveHTTP(w http.ResponseWriter, req *http.Request) { return } + // Backend sent Connection: close + if resp.Close { + w.WriteHeader(http.StatusServiceUnavailable) + w.Write([]byte(http.StatusText(http.StatusServiceUnavailable))) + return + } + requestHijackedConn, _, err := w.(http.Hijacker).Hijack() if err != nil { log.Errorf("Error hijacking request connection: %s", err) diff --git a/proxy/upgrade_test.go b/proxy/upgrade_test.go index dfeccf9b96..0e60a48589 100644 --- a/proxy/upgrade_test.go +++ b/proxy/upgrade_test.go @@ -22,6 +22,9 @@ import ( "github.com/zalando/skipper/routing" "github.com/zalando/skipper/routing/testdataclient" "golang.org/x/net/websocket" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func getEmptyUpgradeRequest() *http.Request { @@ -86,14 +89,15 @@ func TestValidGetUpgradeRequest(t *testing.T) { func TestServeHTTP(t *testing.T) { for _, ti := range []struct { - msg string - route string - method string - backendClosesConnection bool - backendHangs bool - noBackend bool - backendStatusCode int - expectedResponseBody string + msg string + route string + method string + backendClosesConnection bool + backendHangs bool + noBackend bool + backendStatusCode int + expectedResponseStatusCode int + expectedResponseBody string }{ { msg: "Load balanced route", @@ -114,26 +118,37 @@ func TestServeHTTP(t *testing.T) { backendStatusCode: http.StatusSwitchingProtocols, }, { - msg: "Closed connection", - route: `route: Path("/ws") -> "%s";`, - method: http.MethodGet, - backendClosesConnection: true, - backendStatusCode: http.StatusSwitchingProtocols, + msg: "Closed connection 101", + route: `route: Path("/ws") -> "%s";`, + method: http.MethodGet, + backendClosesConnection: true, + backendStatusCode: http.StatusSwitchingProtocols, + expectedResponseStatusCode: http.StatusServiceUnavailable, }, { - msg: "No backend", - route: `route: Path("/ws") -> "%s";`, - method: http.MethodGet, - noBackend: true, - backendStatusCode: http.StatusSwitchingProtocols, + msg: "Closed connection 204", + route: `route: Path("/ws") -> "%s";`, + method: http.MethodGet, + backendClosesConnection: true, + backendStatusCode: http.StatusNoContent, + expectedResponseStatusCode: http.StatusNoContent, }, { - msg: "backend reject upgrade", - route: `route: Path("/ws") -> "%s";`, - method: http.MethodPost, - backendStatusCode: http.StatusBadRequest, - expectedResponseBody: "BACKEND ERROR", - backendClosesConnection: true, + msg: "No backend", + route: `route: Path("/ws") -> "%s";`, + method: http.MethodGet, + noBackend: true, + backendStatusCode: http.StatusSwitchingProtocols, + expectedResponseStatusCode: http.StatusServiceUnavailable, + }, + { + msg: "backend reject upgrade", + route: `route: Path("/ws") -> "%s";`, + method: http.MethodPost, + backendClosesConnection: true, + backendStatusCode: http.StatusBadRequest, + expectedResponseStatusCode: http.StatusBadRequest, + expectedResponseBody: "BACKEND ERROR", }, { msg: "backend hangs", @@ -151,24 +166,24 @@ func TestServeHTTP(t *testing.T) { } backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(ti.backendStatusCode) if ti.backendClosesConnection { + // Set header first as 1xx headers are sent immediately by w.WriteHeader w.Header().Set("Connection", "close") + w.WriteHeader(ti.backendStatusCode) if len(ti.expectedResponseBody) > 0 { w.Write([]byte(ti.expectedResponseBody)) } return } + + w.WriteHeader(ti.backendStatusCode) + hj, ok := w.(http.Hijacker) - if !ok { - t.Error("webserver doesn't support hijacking") - return - } + require.True(t, ok, "webserver doesn't support hijacking") + conn, bufrw, err := hj.Hijack() - if err != nil { - t.Error(err.Error()) - return - } + require.NoError(t, err) + defer conn.Close() for { @@ -201,29 +216,27 @@ func TestServeHTTP(t *testing.T) { } } })) - defer backend.Close() + routes := fmt.Sprintf(ti.route, backend.URL) + if ti.noBackend { backend.Close() + } else { + defer backend.Close() } + tp, err := newTestProxyWithParams(routes, Params{ExperimentalUpgrade: true}) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) + defer tp.close() - skipper := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - tp.proxy.ServeHTTP(w, r) - })) + skipper := httptest.NewServer(tp.proxy) defer skipper.Close() skipperUrl, _ := url.Parse(skipper.URL) conn, err := net.Dial("tcp", skipperUrl.Host) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) + defer func() { clientConnClosed.Store(true) conn.Close() @@ -239,63 +252,33 @@ func TestServeHTTP(t *testing.T) { }, } err = r.Write(conn) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) reader := bufio.NewReader(conn) resp, err := http.ReadResponse(reader, r) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) + if resp.StatusCode != http.StatusSwitchingProtocols { - if resp.StatusCode == ti.backendStatusCode { - // check Body + assert.Equal(t, ti.expectedResponseStatusCode, resp.StatusCode) + + if ti.expectedResponseBody != "" { data, err := io.ReadAll(resp.Body) - if err != nil { - t.Error(err) - return - } - if string(data) != ti.expectedResponseBody { - t.Errorf("wrong response body: %s, expected %s", string(data), ti.expectedResponseBody) - } - return - } + require.NoError(t, err) - if ti.method == http.MethodPost || ti.noBackend { - return + assert.Equal(t, ti.expectedResponseBody, string(data)) } - t.Errorf("wrong response status <%d>, expected <%d>", resp.StatusCode, ti.backendStatusCode) return } _, err = conn.Write([]byte("ping\n")) - if err != nil { - t.Error(err) - return - } + require.NoError(t, err) + pong, err := reader.ReadString('\n') if ti.backendHangs { - if err != io.EOF { - t.Error("expected EOF on closed connection read") - } - return - } - if err != nil { - t.Error(err) - return - } - if ti.backendClosesConnection { - if pong != "HTTP/1.1 400 Bad Request\r\n" { - t.Errorf("wrong bad response <%s>", pong) - } - return - } - if pong != "pong\n" { - t.Errorf("wrong response <%s>", pong) - return + assert.Equal(t, io.EOF, err, "expected EOF on closed connection read") + } else { + require.NoError(t, err) + assert.Equal(t, "pong\n", pong) } }) } From cce1b0f6b4501d57597caba0da220af85f7d380d Mon Sep 17 00:00:00 2001 From: Mikkel Oscar Lyderik Larsen Date: Mon, 17 Oct 2022 18:33:30 +0200 Subject: [PATCH 0098/1047] Update build to Go 1.19 (#2053) Signed-off-by: Mikkel Oscar Lyderik Larsen Signed-off-by: Mikkel Oscar Lyderik Larsen --- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- delivery.yaml | 4 ++-- go.mod | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 24659837fd..c5552935b0 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '~1.18' + go-version: '^1.19' - run: go version - run: make deps - run: make check-fmt diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 513f5460a8..51bd251ce7 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -8,7 +8,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '~1.18' + go-version: '^1.19' - run: go version - run: make deps - run: make check-race @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-go@v2 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '~1.18' + go-version: '^1.19' - run: go version - run: make deps - run: make check-fmt diff --git a/delivery.yaml b/delivery.yaml index 759041c789..49de045e99 100644 --- a/delivery.yaml +++ b/delivery.yaml @@ -5,7 +5,7 @@ pipeline: depends_on: [] vm_config: type: linux - image: "cdp-runtime/go-1.18" + image: "cdp-runtime/go" type: script when: branch: master @@ -18,7 +18,7 @@ pipeline: depends_on: [] vm_config: type: linux - image: "cdp-runtime/go-1.18" + image: "cdp-runtime/go" type: script commands: - desc: Setup BuildKit diff --git a/go.mod b/go.mod index 2c27d4cad1..fb0bf95049 100644 --- a/go.mod +++ b/go.mod @@ -136,6 +136,6 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect ) -go 1.18 +go 1.19 replace github.com/go-redis/redis/v9 => github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220801200609-6f7f800107ba From f6d255e7c7f5f6c5672df308221646f787ba18ee Mon Sep 17 00:00:00 2001 From: Nikhil Goel Date: Tue, 18 Oct 2022 16:36:09 +0200 Subject: [PATCH 0099/1047] make routesrv `/routes` conditional, based on request header and if route table has changed since last requested (#2093) Signed-off-by: Nikhil Goel Signed-off-by: Nikhil Goel --- routesrv/eskipbytes.go | 32 +++++++--- routesrv/polling.go | 5 ++ routesrv/routesrv_test.go | 122 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 9 deletions(-) diff --git a/routesrv/eskipbytes.go b/routesrv/eskipbytes.go index 22824640ec..ac5d70c661 100644 --- a/routesrv/eskipbytes.go +++ b/routesrv/eskipbytes.go @@ -2,8 +2,11 @@ package routesrv import ( "bytes" + "crypto/sha256" + "fmt" "net/http" "sync" + "time" ot "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/eskip" @@ -14,20 +17,22 @@ import ( // provides synchronized r/w access to them. Additionally it can // serve as an HTTP handler exposing its content. type eskipBytes struct { - data []byte - initialized bool - mu sync.RWMutex + data []byte + etag string + lastModified time.Time + initialized bool + mu sync.RWMutex tracer ot.Tracer } // bytes returns a slice to stored bytes, which are safe for reading, // and if there were already initialized. -func (e *eskipBytes) bytes() ([]byte, bool) { +func (e *eskipBytes) bytes() ([]byte, string, time.Time, bool) { e.mu.RLock() defer e.mu.RUnlock() - return e.data, e.initialized + return e.data, e.etag, e.lastModified, e.initialized } // formatAndSet takes a slice of routes and stores them eskip-formatted @@ -39,7 +44,13 @@ func (e *eskipBytes) formatAndSet(routes []*eskip.Route) (int, bool) { e.mu.Lock() defer e.mu.Unlock() - e.data = buf.Bytes() + if updated := buf.Bytes(); !bytes.Equal(e.data, updated) { + e.lastModified = time.Now() + e.data = updated + h := sha256.New() + h.Write(e.data) + e.etag = fmt.Sprintf(`"%x"`, h.Sum(nil)) + } oldInitialized := e.initialized e.initialized = true @@ -50,8 +61,11 @@ func (e *eskipBytes) ServeHTTP(w http.ResponseWriter, r *http.Request) { span := tracing.CreateSpan("serve_routes", r.Context(), e.tracer) defer span.Finish() - if data, initialized := e.bytes(); initialized { - w.Write(data) + data, etag, lastModified, initialized := e.bytes() + if initialized { + w.Header().Add("Etag", etag) + w.Header().Add("Content-Type", "text/plain; charset=utf-8") + http.ServeContent(w, r, "", lastModified, bytes.NewReader(data)) } else { w.WriteHeader(http.StatusNotFound) } @@ -66,7 +80,7 @@ type eskipBytesStatus struct { const msgRoutesNotInitialized = "routes were not initialized yet" func (s *eskipBytesStatus) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if _, initialized := s.b.bytes(); initialized { + if _, _, _, initialized := s.b.bytes(); initialized { w.WriteHeader(http.StatusNoContent) } else { http.Error(w, msgRoutesNotInitialized, http.StatusServiceUnavailable) diff --git a/routesrv/polling.go b/routesrv/polling.go index 240a5303f0..ac8b1314ce 100644 --- a/routesrv/polling.go +++ b/routesrv/polling.go @@ -3,6 +3,7 @@ package routesrv import ( "context" "fmt" + "sort" "sync" "time" @@ -98,6 +99,10 @@ func (p *poller) poll(wg *sync.WaitGroup) { "message", msg, ) case routesCount > 0: + // sort the routes, otherwise it will lead to different etag values for the same route list for different orders + sort.SliceStable(routes, func(i, j int) bool { + return routes[i].Id < routes[j].Id + }) routesBytes, initialized = p.b.formatAndSet(routes) logger := log.WithFields(log.Fields{"count": routesCount, "bytes": routesBytes}) if initialized { diff --git a/routesrv/routesrv_test.go b/routesrv/routesrv_test.go index 8456e4463f..ddcec930d7 100644 --- a/routesrv/routesrv_test.go +++ b/routesrv/routesrv_test.go @@ -102,6 +102,17 @@ func getRoutes(rs *routesrv.RouteServer) *httptest.ResponseRecorder { return w } +func getRoutesWithRequestHeadersSetting(rs *routesrv.RouteServer, header map[string]string) *httptest.ResponseRecorder { + w := httptest.NewRecorder() + r := httptest.NewRequest("GET", "/routes", nil) + for k, v := range header { + r.Header.Add(k, v) + } + rs.ServeHTTP(w, r) + + return w +} + func wantHTTPCode(t *testing.T, w *httptest.ResponseRecorder, want int) { got := w.Code if got != want { @@ -360,3 +371,114 @@ func TestRoutesWithEastWest(t *testing.T) { } wantHTTPCode(t, w, http.StatusOK) } + +func TestESkipBytesHandlerWithCorrectEtag(t *testing.T) { + defer tl.Reset() + ks, _ := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServer(t, ks) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Fatal("routes not initialized") + } + w1 := getRoutes(rs) + + etag := w1.Header().Get("Etag") + header := map[string]string{"If-None-Match": etag} + w2 := getRoutesWithRequestHeadersSetting(rs, header) + + if len(w2.Body.String()) > 0 { + t.Errorf("expected empty routes list but got %s", w2.Body.String()) + } + if w2.Code != http.StatusNotModified { + t.Errorf("expected 304 status code but received incorrect status code: %d", w2.Code) + } +} + +func TestESkipBytesHandlerWithStaleEtag(t *testing.T) { + defer tl.Reset() + ks, handler := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServer(t, ks) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Fatal("routes not initialized") + } + w1 := getRoutes(rs) + etag := w1.Header().Get("Etag") + header := map[string]string{"If-None-Match": etag} + + // update the routes, which also updates e.etag + handler.set(newKubeAPI(t, loadKubeYAML(t, "testdata/lb-target-single.yaml"))) + if err := tl.WaitForN(routesrv.LogRoutesUpdated, 2, waitTimeout*2); err != nil { + t.Error("routes not updated") + } + + w2 := getRoutesWithRequestHeadersSetting(rs, header) + + if len(w2.Body.String()) == 0 { + t.Errorf("expected non-empty routes list") + } + if w2.Code == http.StatusNotModified { + t.Errorf("received incorrect 304 status code") + } +} + +func TestESkipBytesHandlerWithLastModified(t *testing.T) { + defer tl.Reset() + ks, _ := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServer(t, ks) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Fatal("routes not initialized") + } + w1 := getRoutes(rs) + + lastModified := w1.Header().Get("Last-Modified") + header := map[string]string{"If-Modified-Since": lastModified} + w2 := getRoutesWithRequestHeadersSetting(rs, header) + + if len(w2.Body.String()) > 0 { + t.Errorf("expected empty routes list but got %s", w2.Body.String()) + } + if w2.Code != http.StatusNotModified { + t.Errorf("expected 304 status code but received incorrect status code: %d", w2.Code) + } +} + +func TestESkipBytesHandlerWithOldLastModified(t *testing.T) { + defer tl.Reset() + ks, handler := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServer(t, ks) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Fatal("routes not initialized") + } + w1 := getRoutes(rs) + lastModified := w1.Header().Get("Last-Modified") + header := map[string]string{"If-Modified-Since": lastModified} + // update the routes, which also updated the e.lastModified + handler.set(newKubeAPI(t, loadKubeYAML(t, "testdata/lb-target-single.yaml"))) + if err := tl.WaitForN(routesrv.LogRoutesUpdated, 2, waitTimeout*2); err != nil { + t.Error("routes not updated") + } + + w2 := getRoutesWithRequestHeadersSetting(rs, header) + + if len(w2.Body.String()) == 0 { + t.Errorf("expected non-empty routes list") + } + if w2.Code == http.StatusNotModified { + t.Errorf("received incorrect 304 status code") + } +} From 756e2d6df6b92188622738a10b065b55b1026e13 Mon Sep 17 00:00:00 2001 From: herojan Date: Tue, 18 Oct 2022 19:35:43 +0100 Subject: [PATCH 0100/1047] Select consistent hash endpoints in round robin fashion (#2108) Signed-off-by: Francis Moloney Signed-off-by: Francis Moloney --- loadbalancer/fadein_test.go | 45 +++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/loadbalancer/fadein_test.go b/loadbalancer/fadein_test.go index fcd86bac46..c953585ef4 100644 --- a/loadbalancer/fadein_test.go +++ b/loadbalancer/fadein_test.go @@ -13,7 +13,7 @@ import ( const ( fadeInDuration = 100 * time.Millisecond bucketCount = 20 - monotonyTolerance = 0.3 // we need to use a high tolerance for CI testing + monotonyTolerance = 0.4 // we need to use a high tolerance for CI testing ) func absint(i int) int { @@ -53,12 +53,12 @@ func testFadeIn( detectionTimes = append(detectionTimes, now.Add(-ea)) } - var ep []string + var eps []string for i := range endpointAges { - ep = append(ep, string('a'+rune(i))) + eps = append(eps, string('a'+rune(i))) } - a := algorithm(ep) + a := algorithm(eps) ctx := &routing.LBContext{ Params: map[string]interface{}{}, @@ -68,20 +68,20 @@ func testFadeIn( }, } - for i := range ep { + for i := range eps { ctx.Route.LBEndpoints = append(ctx.Route.LBEndpoints, routing.LBEndpoint{ - Host: ep[i], + Host: eps[i], Detected: detectionTimes[i], }) } + hashKeys := findHashKeys(a, ctx) t.Log("test start", time.Now()) - rnd := rand.New(rand.NewSource(time.Now().UnixNano())) var stats []string stop := time.After(fadeInDuration) func() { for { - ctx.Params[ConsistentHashKey] = strconv.Itoa(rnd.Intn(1000)) + ctx.Params[ConsistentHashKey] = hashKeys[len(stats)%len(hashKeys)] ep := a.Apply(ctx) stats = append(stats, ep.Host) select { @@ -93,7 +93,7 @@ func testFadeIn( }() t.Log("test done", time.Now()) - t.Log("CSV " + strings.Join(ep, ",")) + t.Log("CSV " + strings.Join(eps, ",")) bucketSize := len(stats) / bucketCount var allBuckets []map[string]int for i := 0; i < bucketCount; i++ { @@ -106,7 +106,7 @@ func testFadeIn( } directions := make(map[string]int) - for _, epi := range ep { + for _, epi := range eps { first := allBuckets[0][epi] last := allBuckets[len(allBuckets)-1][epi] t := tolerance(first, last) @@ -124,7 +124,7 @@ func testFadeIn( continue } - for _, epi := range ep { + for _, epi := range eps { if !checkMonotony( directions[epi], allBuckets[i-1][epi], @@ -137,7 +137,7 @@ func testFadeIn( for _, bucketStats := range allBuckets { var showStats []string - for _, epi := range ep { + for _, epi := range eps { showStats = append(showStats, fmt.Sprintf("%d", bucketStats[epi])) } @@ -146,6 +146,27 @@ func testFadeIn( }) } +// For each endpoint, return a hash key which will make the consistent hash algorithm select it. +// This allows the test to emulate round robin, useful for showing the increase in requests to each endpoint is monotonic. +func findHashKeys(a routing.LBAlgorithm, ctx *routing.LBContext) []string { + // temporarily disable fadein + ctx.Route.LBFadeInDuration = 0 + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) + var hashKeys []string + for _, ep := range ctx.Route.LBEndpoints { + for { + ctx.Params[ConsistentHashKey] = strconv.Itoa(rnd.Intn(1000)) + if ep == a.Apply(ctx) { + hashKeys = append(hashKeys, ctx.Params[ConsistentHashKey].(string)) + break + } + } + } + delete(ctx.Params, ConsistentHashKey) + ctx.Route.LBFadeInDuration = fadeInDuration + return hashKeys +} + func TestFadeIn(t *testing.T) { old := 2 * fadeInDuration testFadeIn(t, "round-robin, 0", newRoundRobin, old, old) From 46b86bdb6dfd12b0a5054fb230148cb4732658f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 20 Oct 2022 16:39:32 +0200 Subject: [PATCH 0101/1047] feature: routesv HEAD response with X-Count header (#2109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: routesv HEAD response with X-Count header Signed-off-by: Sandor Szücs --- routesrv/eskipbytes.go | 34 ++++++++++++++++++++++----------- routesrv/routesrv_test.go | 40 +++++++++++++++++++++++++++++++++++++++ routing/routing.go | 6 +++--- routing/routing_test.go | 4 ++-- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/routesrv/eskipbytes.go b/routesrv/eskipbytes.go index ac5d70c661..b9498a6bac 100644 --- a/routesrv/eskipbytes.go +++ b/routesrv/eskipbytes.go @@ -5,11 +5,13 @@ import ( "crypto/sha256" "fmt" "net/http" + "strconv" "sync" "time" ot "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/routing" "github.com/zalando/skipper/tracing" ) @@ -21,20 +23,12 @@ type eskipBytes struct { etag string lastModified time.Time initialized bool + count int mu sync.RWMutex tracer ot.Tracer } -// bytes returns a slice to stored bytes, which are safe for reading, -// and if there were already initialized. -func (e *eskipBytes) bytes() ([]byte, string, time.Time, bool) { - e.mu.RLock() - defer e.mu.RUnlock() - - return e.data, e.etag, e.lastModified, e.initialized -} - // formatAndSet takes a slice of routes and stores them eskip-formatted // in a synchronized way. It returns a number of stored bytes and a boolean, // being true, when the stored bytes were set for the first time. @@ -53,6 +47,7 @@ func (e *eskipBytes) formatAndSet(routes []*eskip.Route) (int, bool) { } oldInitialized := e.initialized e.initialized = true + e.count = len(routes) return len(e.data), !oldInitialized } @@ -61,10 +56,24 @@ func (e *eskipBytes) ServeHTTP(w http.ResponseWriter, r *http.Request) { span := tracing.CreateSpan("serve_routes", r.Context(), e.tracer) defer span.Finish() - data, etag, lastModified, initialized := e.bytes() + if r.Method != "GET" && r.Method != "HEAD" { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + e.mu.RLock() + count := e.count + data := e.data + etag := e.etag + lastModified := e.lastModified + initialized := e.initialized + e.mu.RUnlock() + if initialized { w.Header().Add("Etag", etag) w.Header().Add("Content-Type", "text/plain; charset=utf-8") + w.Header().Add(routing.RoutesCountName, strconv.Itoa(count)) + http.ServeContent(w, r, "", lastModified, bytes.NewReader(data)) } else { w.WriteHeader(http.StatusNotFound) @@ -80,7 +89,10 @@ type eskipBytesStatus struct { const msgRoutesNotInitialized = "routes were not initialized yet" func (s *eskipBytesStatus) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if _, _, _, initialized := s.b.bytes(); initialized { + s.b.mu.RLock() + initialized := s.b.initialized + s.b.mu.RUnlock() + if initialized { w.WriteHeader(http.StatusNoContent) } else { http.Error(w, msgRoutesNotInitialized, http.StatusServiceUnavailable) diff --git a/routesrv/routesrv_test.go b/routesrv/routesrv_test.go index ddcec930d7..4a1aa2f1bb 100644 --- a/routesrv/routesrv_test.go +++ b/routesrv/routesrv_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "os" + "strconv" "sync" "testing" "time" @@ -17,6 +18,7 @@ import ( "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/logging/loggingtest" "github.com/zalando/skipper/routesrv" + "github.com/zalando/skipper/routing" ) type muxHandler struct { @@ -102,6 +104,14 @@ func getRoutes(rs *routesrv.RouteServer) *httptest.ResponseRecorder { return w } +func headRoutes(rs *routesrv.RouteServer) *httptest.ResponseRecorder { + w := httptest.NewRecorder() + r := httptest.NewRequest("HEAD", "/routes", nil) + rs.ServeHTTP(w, r) + + return w +} + func getRoutesWithRequestHeadersSetting(rs *routesrv.RouteServer, header map[string]string) *httptest.ResponseRecorder { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/routes", nil) @@ -482,3 +492,33 @@ func TestESkipBytesHandlerWithOldLastModified(t *testing.T) { t.Errorf("received incorrect 304 status code") } } + +func TestESkipBytesHandlerWithXCount(t *testing.T) { + defer tl.Reset() + ks, _ := newKubeServer(t, loadKubeYAML(t, "testdata/lb-target-multi.yaml")) + ks.Start() + defer ks.Close() + rs := newRouteServerWithOptions(t, routesrv.Options{ + SourcePollTimeout: pollInterval, + KubernetesURL: ks.URL, + }) + + rs.StartUpdates() + if err := tl.WaitFor(routesrv.LogRoutesInitialized, waitTimeout); err != nil { + t.Fatal("routes not initialized") + } + w1 := headRoutes(rs) + if n := w1.Body.Len(); n != 0 { + t.Fatalf("Failed to HEAD and get a response with body: %v", n) + } + countStr := w1.Header().Get(routing.RoutesCountName) + count, err := strconv.Atoi(countStr) + if err != nil { + t.Fatalf("Failed to convert response header %s value '%v' to int: %v", routing.RoutesCountName, countStr, err) + } + + N := 3 + if count != N { + t.Errorf("Failed to get %d number of routes, got: %d", N, count) + } +} diff --git a/routing/routing.go b/routing/routing.go index 93cb9a5d29..fefb6abf6d 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -26,7 +26,7 @@ const ( WeightPredicateName = predicates.WeightName routesTimestampName = "X-Timestamp" - routesCountName = "X-Count" + RoutesCountName = "X-Count" defaultRouteListingLimit = 1024 ) @@ -314,7 +314,7 @@ func (r *Routing) ServeHTTP(w http.ResponseWriter, req *http.Request) { if req.Method == "HEAD" { w.Header().Set(routesTimestampName, createdUnix) - w.Header().Set(routesCountName, strconv.Itoa(len(rt.validRoutes))) + w.Header().Set(RoutesCountName, strconv.Itoa(len(rt.validRoutes))) if strings.Contains(req.Header.Get("Accept"), "application/json") { w.Header().Set("Content-Type", "application/json") @@ -338,7 +338,7 @@ func (r *Routing) ServeHTTP(w http.ResponseWriter, req *http.Request) { } w.Header().Set(routesTimestampName, createdUnix) - w.Header().Set(routesCountName, strconv.Itoa(len(rt.validRoutes))) + w.Header().Set(RoutesCountName, strconv.Itoa(len(rt.validRoutes))) routes := slice(rt.validRoutes, offset, limit) if strings.Contains(req.Header.Get("Accept"), "application/json") { diff --git a/routing/routing_test.go b/routing/routing_test.go index 7998653b05..868258672d 100644 --- a/routing/routing_test.go +++ b/routing/routing_test.go @@ -690,7 +690,7 @@ func TestRoutingHandlerPagination(t *testing.T) { req.Header.Set("accept", "application/json") resp, _ := http.DefaultClient.Do(req) - if resp.Header.Get("X-Count") != "3" { + if resp.Header.Get(routing.RoutesCountName) != "3" { t.Error("invalid or missing route count header") } @@ -751,7 +751,7 @@ func TestRoutingHandlerHEAD(t *testing.T) { return } - if rsp.Header.Get("X-Count") != "3" { + if rsp.Header.Get(routing.RoutesCountName) != "3" { t.Error("invalid count header") } } From eec94bd559eed6acfa929ba5e427372794a7fcd4 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 26 Oct 2022 15:38:09 +0200 Subject: [PATCH 0102/1047] webhook: remove github.com/szuecs/routegroup-client dependency (#2113) `github.com/szuecs/routegroup-client` was only used in tests and webhook depends on `github.com/zalando/skipper/dataclients/kubernetes/definitions` anyway which has manually defined RouteGroup type. - `go mod tidy` - use stdlib `context` Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- cmd/webhook/admission/admission_test.go | 19 ++-- cmd/webhook/main.go | 2 +- go.mod | 1 - go.sum | 131 ------------------------ 4 files changed, 10 insertions(+), 143 deletions(-) diff --git a/cmd/webhook/admission/admission_test.go b/cmd/webhook/admission/admission_test.go index 6fc4bdb02f..ec483ca388 100644 --- a/cmd/webhook/admission/admission_test.go +++ b/cmd/webhook/admission/admission_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - zv1 "github.com/szuecs/routegroup-client/apis/zalando.org/v1" "github.com/zalando/skipper/dataclients/kubernetes/definitions" admissionsv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -64,8 +63,8 @@ func TestUnsupportedContentType(t *testing.T) { } func TestRequestDecoding(t *testing.T) { - expectedRg := zv1.RouteGroup{ - ObjectMeta: metav1.ObjectMeta{ + expectedRg := definitions.RouteGroupItem{ + Metadata: &definitions.Metadata{ Name: "r1", Namespace: "n1", }, @@ -103,8 +102,8 @@ func TestRequestDecoding(t *testing.T) { err := json.Unmarshal(req.Object.Raw, &rg) assert.NoError(t, err) - assert.Equal(t, expectedRg.Name, rg.Metadata.Name) - assert.Equal(t, expectedRg.Namespace, rg.Metadata.Namespace) + assert.Equal(t, expectedRg.Metadata.Name, rg.Metadata.Name) + assert.Equal(t, expectedRg.Metadata.Namespace, rg.Metadata.Namespace) return &admissionsv1.AdmissionResponse{ Allowed: true, @@ -163,8 +162,8 @@ func TestResponseEncoding(t *testing.T) { // Test RouteGroupAdmitter.Admit func TestAdmitRouteGroups(t *testing.T) { - rg := zv1.RouteGroup{ - ObjectMeta: metav1.ObjectMeta{ + rg := definitions.RouteGroupItem{ + Metadata: &definitions.Metadata{ Name: "r1", Namespace: "n1", }, @@ -210,8 +209,8 @@ func TestAdmitRouteGroups(t *testing.T) { } func TestExtractName(t *testing.T) { - rg := zv1.RouteGroup{ - ObjectMeta: metav1.ObjectMeta{ + rg := definitions.RouteGroupItem{ + Metadata: &definitions.Metadata{ Name: "r2", Namespace: "n1", }, @@ -224,5 +223,5 @@ func TestExtractName(t *testing.T) { Object: runtime.RawExtension{Raw: rb}, } name := extractName(request) - assert.Equal(t, rg.Name, name) + assert.Equal(t, rg.Metadata.Name, name) } diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index c7f03b98e2..19fb4f611f 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "net/http" "os" "os/signal" @@ -10,7 +11,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" "github.com/zalando/skipper/cmd/webhook/admission" - "golang.org/x/net/context" "gopkg.in/alecthomas/kingpin.v2" ) diff --git a/go.mod b/go.mod index fb0bf95049..12cdf9f19c 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,6 @@ require ( github.com/sony/gobreaker v0.5.0 github.com/stretchr/testify v1.7.0 github.com/szuecs/rate-limit-buffer v0.7.1 - github.com/szuecs/routegroup-client v0.21.0 github.com/testcontainers/testcontainers-go v0.12.0 github.com/tidwall/gjson v1.12.1 github.com/tsenart/vegeta v12.7.0+incompatible diff --git a/go.sum b/go.sum index 6a8c97efb0..c4b8cacadc 100644 --- a/go.sum +++ b/go.sum @@ -38,21 +38,13 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7O github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -87,7 +79,6 @@ github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:m github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -96,7 +87,6 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= @@ -106,7 +96,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -115,7 +104,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/aryszka/jobqueue v0.0.2 h1:LYPhzklo0XFpVF+QtzfP9XRQPEsbJ2EW5Pur6pxxaS4= github.com/aryszka/jobqueue v0.0.2/go.mod h1:SdxqI6HZ4E1Lss94tey5OfjcAu3bdCDWS1AQzzIN4m4= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -127,7 +115,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= @@ -262,7 +249,6 @@ github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgU github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -277,9 +263,7 @@ github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+ github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -319,7 +303,6 @@ github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY= github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -330,7 +313,6 @@ github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6Uezg github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= @@ -348,7 +330,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -363,8 +344,6 @@ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYis github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -383,57 +362,23 @@ github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-restit/lzjson v0.0.0-20161206095556-efe3c53acc68/go.mod h1:7vXSKQt83WmbPeyVjCfNT9YDJ5BUFmcwFsEjI9SCvYM= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -469,7 +414,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -527,12 +471,9 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= @@ -593,7 +534,6 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -634,17 +574,13 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -714,7 +650,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 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.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= @@ -756,7 +691,6 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -813,7 +747,6 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sanity-io/litter v1.5.2 h1:AnC8s9BMORWH5a4atZ4D6FPVvKGzHcnc5/IVTa87myw= @@ -826,7 +759,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.21.2 h1:fIOk3hyqV1oGKogfGNjUZa0lUbtlkx3+ZT0IoJth2uM= github.com/shirou/gopsutil/v3 v3.21.2/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -853,7 +785,6 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -862,7 +793,6 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -889,8 +819,6 @@ github.com/szuecs/rate-limit-buffer v0.7.1 h1:kpVLwDvpCTFQi8uhiXQrhAKWzNUaEKhArF github.com/szuecs/rate-limit-buffer v0.7.1/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220801200609-6f7f800107ba h1:XbMix18lOs+rRJwTaqnN6pmMc8z2kRLvFOfON9fFTA8= github.com/szuecs/redis/v9 v9.0.0-beta.1.0.20220801200609-6f7f800107ba/go.mod h1:Bldcd/M/bm9HbnNPi/LUtYBSD8ttcZYBMupwMXhdU0o= -github.com/szuecs/routegroup-client v0.21.0 h1:XWLxK2OCs4lZeIIHOJVVaFpe7nFmJjM3r+fOsZ2eyWw= -github.com/szuecs/routegroup-client v0.21.0/go.mod h1:GbR5pqdcJdiFx9aL2hoq1ghAMZ9E1qFKBf88w7vqyK0= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/testcontainers/testcontainers-go v0.12.0 h1:SK0NryGHIx7aifF6YqReORL18aGAA4bsDPtikDVCEyg= github.com/testcontainers/testcontainers-go v0.12.0/go.mod h1:SIndOQXZng0IW8iWU1Js0ynrfZ8xcxrTtDfF6rD2pxs= @@ -898,7 +826,6 @@ github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= @@ -916,12 +843,10 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMW github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= @@ -951,11 +876,7 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -975,22 +896,16 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1029,22 +944,18 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1079,7 +990,6 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1109,19 +1019,14 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1185,7 +1090,6 @@ golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1204,12 +1108,9 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1226,7 +1127,6 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1234,7 +1134,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1246,7 +1145,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -1254,7 +1152,6 @@ golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDq golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1279,7 +1176,6 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1288,7 +1184,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1441,7 +1336,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1459,34 +1353,23 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= k8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8= k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.21.4/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= k8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s= k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.21.4/go.mod h1:t0/eMKyUAq/DoQ7vW8NVVA00/nomlwC+eInsS8PxSew= -k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/code-generator v0.21.4/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= -k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= @@ -1494,29 +1377,19 @@ k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc= @@ -1524,12 +1397,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-tools v0.4.0/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= From a5082dc407081517a6f73ac7c90dcf1aeb676525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 27 Oct 2022 10:52:31 +0200 Subject: [PATCH 0103/1047] test: skipper basic startup routes file, inlinecontent, status checks, .. (#2115) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs Signed-off-by: Sandor Szücs --- skipper_test.go | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/skipper_test.go b/skipper_test.go index 2a74346685..e031b89106 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -23,6 +23,7 @@ import ( "github.com/zalando/skipper/filters/auth" "github.com/zalando/skipper/filters/builtin" fscheduler "github.com/zalando/skipper/filters/scheduler" + "github.com/zalando/skipper/innkeeper" "github.com/zalando/skipper/loadbalancer" "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/net/httptest" @@ -641,3 +642,139 @@ func createFilterRegistry(specs ...filters.Spec) filters.Registry { } return fr } + +func createRoutesFile(route string) (string, error) { + fd, err := os.CreateTemp("/tmp", "test_data_clients_") + if err != nil { + return "", fmt.Errorf("Failed to create tempfile: %w", err) + } + _, err = fd.WriteString(route) + if err != nil { + return "", fmt.Errorf("Failed to write tempfile: %w", err) + } + + filePath := fd.Name() + err = fd.Close() + + return filePath, err +} + +func TestDataClients(t *testing.T) { + // routesfile + routesFileStatus := 201 + routeStringFmt := `r%d: Path("/routes-file") -> status(%d) -> inlineContent("Got it") -> ;` + filePath, err := createRoutesFile(fmt.Sprintf(routeStringFmt, routesFileStatus, routesFileStatus)) + if err != nil { + t.Fatalf("Failed to create routes file: %v", err) + } + defer os.Remove(filePath) + + // application log + fdApp, err := os.CreateTemp("/tmp", "app_log_") + if err != nil { + t.Fatalf("Failed to create tempfile: %v", err) + } + defer fdApp.Close() + + // access log + fdAccess, err := os.CreateTemp("/tmp", "access_log_") + if err != nil { + t.Fatalf("Failed to create tempfile: %v", err) + } + defer fdAccess.Close() + + // run skipper proxy that we want to test + o := Options{ + Address: ":8090", + EnableRatelimiters: true, + SourcePollTimeout: 1500 * time.Millisecond, + WaitFirstRouteLoad: true, + SuppressRouteUpdateLogs: false, + MetricsListener: ":8091", + testOptions: testOptions{}, + RoutesFile: filePath, + InlineRoutes: `healthz: Path("/healthz") -> status(200) -> inlineContent("OK") -> ;`, + ApplicationLogOutput: fdApp.Name(), + AccessLogOutput: fdAccess.Name(), + AccessLogDisabled: false, + MaxTCPListenerConcurrency: 0, + ExpectedBytesPerRequest: 1024, + ReadHeaderTimeoutServer: 0, + ReadTimeoutServer: 1 * time.Second, + MetricsFlavours: []string{"codahale"}, + EnablePrometheusMetrics: true, + LoadBalancerHealthCheckInterval: 3 * time.Second, + OAuthTokeninfoURL: "http://127.0.0.1:12345", + CredentialsPaths: []string{"/does-not-exist"}, + EnableSwarm: true, + SwarmPort: 9001, + CompressEncodings: []string{"gzip"}, + IgnoreTrailingSlash: true, + EnableBreakers: true, + DebugListener: ":8092", + StatusChecks: []string{"http://127.0.0.1:8091/metrics", "http://127.0.0.1:8092"}, + } + + dcs, err := createDataClients(o, innkeeper.CreateInnkeeperAuthentication(innkeeper.AuthOptions{}), nil) + if err != nil { + t.Fatalf("Failed to createDataclients: %v", err) + } + + fr := createFilterRegistry( + fscheduler.NewFifo(), + flog.NewEnableAccessLog(), + builtin.NewStatus(), + builtin.NewInlineContent(), + ) + metrics := &metricstest.MockMetrics{} + reg := scheduler.RegistryWith(scheduler.Options{ + Metrics: metrics, + EnableRouteFIFOMetrics: true, + }) + defer reg.Close() + + // create LB in front of apiservers to be able to switch the data served by apiserver + ro := routing.Options{ + SignalFirstLoad: true, + FilterRegistry: fr, + DataClients: dcs, //[]routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{ + loadbalancer.NewAlgorithmProvider(), + reg, + }, + SuppressLogs: true, + } + rt := routing.New(ro) + defer rt.Close() + <-rt.FirstLoad() + tracer := &tracingtest.Tracer{} + pr := proxy.WithParams(proxy.Params{ + Routing: rt, + OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + }) + defer pr.Close() + lb := stdlibhttptest.NewServer(pr) + defer lb.Close() + + sigs := make(chan os.Signal, 1) + go run(o, sigs, nil) + + // wait for proxy being ready + ready := false + for !ready { + rsp, _ := http.DefaultClient.Get("http://localhost:8090/healthz") + if rsp != nil && rsp.StatusCode == 200 { + ready = true + } + time.Sleep(100 * time.Millisecond) + } + + rsp, err := http.DefaultClient.Get("http://localhost:8090/routes-file") + if err != nil { + t.Fatalf("Failed to GET routes file route: %v", err) + } + if rsp.StatusCode != routesFileStatus { + t.Fatalf("Failed to GET the status of routes file route: %d", rsp.StatusCode) + } + +} From 07904d15ad410bcc46a8443aaa109bfd31f04f73 Mon Sep 17 00:00:00 2001 From: Noor Muhammad Malik Date: Thu, 27 Oct 2022 14:02:45 +0200 Subject: [PATCH 0104/1047] Fix swarm.Join() when NodeInfo pointer is nil (#2116) Signed-off-by: noor Signed-off-by: noor --- swarm/nodeinfo.go | 2 +- swarm/swarm.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/swarm/nodeinfo.go b/swarm/nodeinfo.go index 71a735884e..fc6ce1153b 100644 --- a/swarm/nodeinfo.go +++ b/swarm/nodeinfo.go @@ -18,7 +18,7 @@ type EntryPoint interface { Nodes() []*NodeInfo } -// NodeInfo is a value object tat contains information about swarm +// NodeInfo is a value object that contains information about swarm // cluster nodes, that is required to access member nodes. type NodeInfo struct { Name string diff --git a/swarm/swarm.go b/swarm/swarm.go index 72d579a167..b48c120811 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -200,6 +200,9 @@ func Start(o Options) (*Swarm, error) { // Join will join given Swarm peers and return an initialiazed Swarm // object if successful. func Join(o Options, self *NodeInfo, nodes []*NodeInfo, cleanupF func()) (*Swarm, error) { + if self == nil { + return nil, fmt.Errorf("cannot join node to swarm, NodeInfo pointer is nil") + } log.Infof("SWARM: %s is going to join swarm of %d nodes (%v)", self, len(nodes), nodes) cfg := memberlist.DefaultLocalConfig() if !o.Debug { From 070db36179268e993c838830bf054dca114e6f44 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 28 Oct 2022 11:06:06 +0200 Subject: [PATCH 0105/1047] lua: add flag to selectively enable modules and symbols (#2112) Signed-off-by: Alexander Yastrebov Signed-off-by: Alexander Yastrebov --- config/config.go | 7 ++ config/config_test.go | 1 + docs/reference/scripts.md | 21 +++++- script/module.go | 117 ++++++++++++++++++++++++++++++ script/options_test.go | 147 ++++++++++++++++++++++++++++++++++++++ script/script.go | 122 +++++++++++++++++++++---------- script/script_test.go | 57 +++++++++++---- skipper.go | 12 ++++ 8 files changed, 435 insertions(+), 49 deletions(-) create mode 100644 script/module.go create mode 100644 script/options_test.go diff --git a/config/config.go b/config/config.go index 6781b69039..7381229909 100644 --- a/config/config.go +++ b/config/config.go @@ -268,6 +268,8 @@ type Config struct { SwarmStaticOther string `yaml:"swarm-static-other"` ClusterRatelimitMaxGroupShards int `yaml:"cluster-ratelimit-max-group-shards"` + + LuaModules *listFlag `yaml:"lua-modules"` } const ( @@ -298,6 +300,7 @@ func NewConfig() *Config { cfg.ForwardedHeadersList = commaListFlag() cfg.ForwardedHeadersExcludeCIDRList = commaListFlag() cfg.CompressEncodings = commaListFlag("gzip", "deflate", "br") + cfg.LuaModules = commaListFlag() flag.StringVar(&cfg.ConfigFile, "config-file", "", "if provided the flags will be loaded/overwritten by the values on the file (yaml)") @@ -534,6 +537,8 @@ func NewConfig() *Config { flag.IntVar(&cfg.ClusterRatelimitMaxGroupShards, "cluster-ratelimit-max-group-shards", 1, "sets the maximum number of group shards for the clusterRatelimit filter") + flag.Var(cfg.LuaModules, "lua-modules", "comma separated list of lua filter modules. Use . to selectively enable module symbols, for example: package,base._G,base.print,json") + return cfg } @@ -886,6 +891,8 @@ func (c *Config) ToOptions() skipper.Options { SwarmStaticOther: c.SwarmStaticOther, ClusterRatelimitMaxGroupShards: c.ClusterRatelimitMaxGroupShards, + + LuaModules: c.LuaModules.values, } if c.PluginDir != "" { diff --git a/config/config_test.go b/config/config_test.go index 2dd6a34d17..27d2efe31f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -173,6 +173,7 @@ func Test_NewConfig(t *testing.T) { RefusePayload: multiFlag{"foo", "bar", "baz"}, ValidateQuery: true, ValidateQueryLog: true, + LuaModules: commaListFlag(), }, wantErr: false, }, diff --git a/docs/reference/scripts.md b/docs/reference/scripts.md index 9c65b4acb5..d8fce831a3 100644 --- a/docs/reference/scripts.md +++ b/docs/reference/scripts.md @@ -52,7 +52,7 @@ Lua `print` builtin function writes skipper info log messages. ## Available lua modules Besides the [standard modules](https://www.lua.org/manual/5.1/manual.html#5) - except -for `debug` - the following modules have been preloaded and can be used with e.g. +for `debug` - the following additional modules have been preloaded and can be used with e.g. `local http = require("http")`, see also the examples below * `http` [gluahttp](https://github.com/cjoudrey/gluahttp) - TODO: configurable @@ -68,6 +68,25 @@ Any other module can be loaded in non-byte code form from the lua path (by defau for `require("mod")` this is `./mod.lua`, `/usr/local/share/lua/5.1/mod.lua` and `/usr/local/share/lua/5.1/mod/init.lua`). + +You may selectively enable standard and additional Lua modules using `-lua-modules` flag: +```sh +-lua-modules=package,base,json +``` +Note that preloaded additional modules require `package` module. + +For standard modules you may enable only a subset of module symbols: +```sh +-lua-modules=base.print,base.assert +``` + +Use `none` to disable all modules: +```sh +-lua-modules=none +``` + +See also http://lua-users.org/wiki/SandBoxes + ## Lua states There is no guarantee that the `request()` and `response()` functions of a diff --git a/script/module.go b/script/module.go new file mode 100644 index 0000000000..f32d0fd9c2 --- /dev/null +++ b/script/module.go @@ -0,0 +1,117 @@ +package script + +import ( + "strings" + "time" + + log "github.com/sirupsen/logrus" + lua "github.com/yuin/gopher-lua" +) + +type luaModule struct { + name string + loader lua.LGFunction + + disabledSymbols []string +} + +var standardModules = []luaModule{ + // Load Package and Base first, see lua.LState.OpenLibs() + {lua.LoadLibName, lua.OpenPackage, nil}, + {lua.BaseLibName, lua.OpenBase, nil}, + {lua.TabLibName, lua.OpenTable, nil}, + {lua.IoLibName, lua.OpenIo, nil}, + {lua.OsLibName, lua.OpenOs, nil}, + {lua.StringLibName, lua.OpenString, nil}, + {lua.MathLibName, lua.OpenMath, nil}, + {lua.DebugLibName, lua.OpenDebug, nil}, + {lua.ChannelLibName, lua.OpenChannel, nil}, + {lua.CoroutineLibName, lua.OpenCoroutine, nil}, +} + +// load loads standard lua module, see lua.LState.OpenLibs() +func (m luaModule) load(L *lua.LState) { + L.Push(L.NewFunction(m.loader)) + L.Push(lua.LString(m.name)) + L.Call(1, 0) + + if m.name == lua.BaseLibName { + L.SetGlobal("print", L.NewFunction(printToLog)) + L.SetGlobal("sleep", L.NewFunction(sleep)) + } + + if len(m.disabledSymbols) > 0 { + st := m.table(L) + for _, name := range m.disabledSymbols { + st.RawSetString(name, lua.LNil) + } + } +} + +// withSymbols returns copy of module with selected symbols +func (m luaModule) withSymbols(L *lua.LState, enabledSymbols []string) luaModule { + // gopher-lua does not have API to select enabled symbols, + // see https://github.com/yuin/gopher-lua/discussions/393 + // + // Instead collect symbols to disable as difference + // between all and enabled module symbols + allSymbols := make(map[string]struct{}) + + m.load(L) + m.table(L).ForEach(func(k, _ lua.LValue) { + if name, ok := k.(lua.LString); ok { + allSymbols[name.String()] = struct{}{} + } + }) + + for _, s := range enabledSymbols { + delete(allSymbols, s) + } + + result := luaModule{m.name, m.loader, nil} + for s := range allSymbols { + result.disabledSymbols = append(result.disabledSymbols, s) + } + return result +} + +func (m luaModule) table(L *lua.LState) *lua.LTable { + name := m.name + if m.name == lua.BaseLibName { + name = "_G" + } + return L.GetGlobal(name).(*lua.LTable) +} + +func (m luaModule) preload(L *lua.LState) { + L.PreloadModule(m.name, m.loader) +} + +func printToLog(L *lua.LState) int { + top := L.GetTop() + args := make([]interface{}, 0, top) + for i := 1; i <= top; i++ { + args = append(args, L.ToStringMeta(L.Get(i)).String()) + } + log.Print(args...) + return 0 +} + +func sleep(L *lua.LState) int { + time.Sleep(time.Duration(L.CheckInt64(1)) * time.Millisecond) + return 0 +} + +func moduleConfig(modules []string) map[string][]string { + config := make(map[string][]string) + for _, m := range modules { + if module, symbol, found := strings.Cut(m, "."); found { + config[module] = append(config[module], symbol) + } else { + if _, ok := config[module]; !ok { + config[module] = []string{} + } + } + } + return config +} diff --git a/script/options_test.go b/script/options_test.go new file mode 100644 index 0000000000..9af38cd80d --- /dev/null +++ b/script/options_test.go @@ -0,0 +1,147 @@ +package script + +func ExampleLuaOptions_default() { + runExampleWithOptions( + LuaOptions{}, + testScript(` + local base64 = require("base64") + + function request(ctx, params) + print(table.concat({"Hello", "World"}, " ")) + print(string.lower("Hello World")) + print(math.abs(-1)) + print(base64.encode("Hello World")) + end + `), + ) + // Output: + // Hello World + // hello world + // 1 + // SGVsbG8gV29ybGQ= +} + +const printTable = ` +function printTable(p, t) + local g = {} + for n in pairs(t) do table.insert(g, n) end + table.sort(g) + for i, n in ipairs(g) do print(p..n) end +end` + +func ExampleLuaOptions_printGlobals() { + runExampleWithOptions( + LuaOptions{}, + testScript(printTable+` + function request() + printTable("", _G) + end + `), + ) + // Output: + // _G + // _GOPHER_LUA_VERSION + // _VERSION + // _printregs + // assert + // channel + // collectgarbage + // coroutine + // debug + // dofile + // error + // getfenv + // getmetatable + // io + // ipairs + // load + // loadfile + // loadstring + // math + // module + // newproxy + // next + // os + // package + // pairs + // pcall + // print + // printTable + // rawequal + // rawget + // rawset + // request + // require + // select + // setfenv + // setmetatable + // sleep + // string + // table + // tonumber + // tostring + // type + // unpack + // xpcall +} + +func ExampleLuaOptions_enableModules() { + runExampleWithOptions( + LuaOptions{ + Modules: []string{ + "base._G", "base.pairs", "base.ipairs", "base.print", "base.require", + "table.sort", "table.insert", + // enable all symbols from "package" module as + // additional preloaded modules require it + "package", + // preload additional module + "base64", + }, + }, + testScript(printTable+` + local base64 = require("base64") + + function request() + printTable("", _G) + printTable("table.", table) + printTable("package.", package) + printTable("package.preload.", package.preload) + end + `), + ) + // Output: + // _G + // ipairs + // package + // pairs + // print + // printTable + // request + // require + // table + // table.insert + // table.sort + // package.cpath + // package.loaded + // package.loaders + // package.loadlib + // package.path + // package.preload + // package.seeall + // package.preload.base64 +} + +func ExampleLuaOptions_disableAll() { + runExampleWithOptions( + LuaOptions{ + // use non-existing module + Modules: []string{"none"}, + }, + testScript(`function request() print("test") end`), + ) + // Output: + // Error calling request from function request() print("test") end: + + + `) + -> oauthGrant() + -> "http://localhost:9090"; +``` + Skipper arguments: | Argument | Required? | Description | diff --git a/filters/auth/grant.go b/filters/auth/grant.go index 577f385940..43cac8c4ef 100644 --- a/filters/auth/grant.go +++ b/filters/auth/grant.go @@ -4,10 +4,14 @@ import ( "context" "errors" "fmt" + "io" "net/http" + "strconv" + "strings" "time" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/annotate" "golang.org/x/oauth2" ) @@ -84,12 +88,25 @@ func loginRedirectWithOverride(ctx filters.FilterContext, config *OAuthConfig, o return } - ctx.Serve(&http.Response{ - StatusCode: http.StatusTemporaryRedirect, - Header: http.Header{ - "Location": []string{authConfig.AuthCodeURL(state, config.GetAuthURLParameters(redirect)...)}, - }, - }) + authCodeURL := authConfig.AuthCodeURL(state, config.GetAuthURLParameters(redirect)...) + + if lrs, ok := annotate.GetAnnotations(ctx)["oauthGrant.loginRedirectStub"]; ok { + lrs = strings.ReplaceAll(lrs, "{{authCodeURL}}", authCodeURL) + ctx.Serve(&http.Response{ + StatusCode: http.StatusOK, + Header: http.Header{ + "Content-Length": []string{strconv.Itoa(len(lrs))}, + }, + Body: io.NopCloser(strings.NewReader(lrs)), + }) + } else { + ctx.Serve(&http.Response{ + StatusCode: http.StatusTemporaryRedirect, + Header: http.Header{ + "Location": []string{authCodeURL}, + }, + }) + } } func (f *grantFilter) refreshToken(token *oauth2.Token, req *http.Request) (*oauth2.Token, error) { diff --git a/filters/auth/grant_test.go b/filters/auth/grant_test.go index 1d7bda2276..d0d10a4e8e 100644 --- a/filters/auth/grant_test.go +++ b/filters/auth/grant_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "encoding/json" + "fmt" "net" "net/http" "net/http/cookiejar" @@ -1000,3 +1001,32 @@ func TestGrantInsecure(t *testing.T) { } } } + +func TestGrantLoginRedirectStub(t *testing.T) { + provider := newGrantTestAuthServer(testToken, testAccessCode) + defer provider.Close() + + tokeninfo := newGrantTestTokeninfo(testToken, "") + defer tokeninfo.Close() + + config := newGrantTestConfig(tokeninfo.URL, provider.URL) + + const stubContent = "stub content" + + routes := eskip.MustParse(fmt.Sprintf(`* + -> annotate("oauthGrant.loginRedirectStub", "%s") + -> oauthGrant() + -> status(204) + -> + `, stubContent)) + + proxy, client := newAuthProxy(t, config, routes) + defer proxy.Close() + + rsp, body, err := client.GetBody(proxy.URL + "/test") + require.NoError(t, err) + + assert.Equal(t, rsp.StatusCode, http.StatusOK) + assert.Equal(t, int64(len(stubContent)), rsp.ContentLength) + assert.Equal(t, stubContent, string(body)) +} From 5dc80983de3a89dc9d63bdeefbdff64e34317023 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 22 Apr 2024 10:20:39 +0200 Subject: [PATCH 0753/1047] proxy: support configurable metrics (#3027) Use configed metrics and fallback to global for backwards compatibility. Updates #3026 Signed-off-by: Alexander Yastrebov --- proxy/metrics_test.go | 9 ++++----- proxy/proxy.go | 10 +++++++++- skipper.go | 11 ++++++----- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/proxy/metrics_test.go b/proxy/metrics_test.go index 5342f2adcf..f601c4e319 100644 --- a/proxy/metrics_test.go +++ b/proxy/metrics_test.go @@ -9,19 +9,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zalando/skipper/filters/builtin" - "github.com/zalando/skipper/metrics" "github.com/zalando/skipper/metrics/metricstest" + "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/proxy/proxytest" "github.com/zalando/skipper/routing" "github.com/zalando/skipper/routing/testdataclient" ) func TestMetricsUncompressed(t *testing.T) { - dm := metrics.Default - t.Cleanup(func() { metrics.Default = dm }) - m := &metricstest.MockMetrics{} - metrics.Default = m // will update routes after proxy address is known dc := testdataclient.New(nil) @@ -32,6 +28,9 @@ func TestMetricsUncompressed(t *testing.T) { FilterRegistry: builtin.MakeRegistry(), DataClients: []routing.DataClient{dc}, }, + ProxyParams: proxy.Params{ + Metrics: m, + }, }.Create() defer p.Close() diff --git a/proxy/proxy.go b/proxy/proxy.go index d2156d8f55..6e0616530b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -217,6 +217,10 @@ type Params struct { // Control flags. See the Flags values. Flags Flags + // Metrics collector. + // If not specified proxy uses global metrics.Default. + Metrics metrics.Metrics + // And optional list of priority routes to be used for matching // before the general lookup tree. PriorityRoutes []PriorityRoute @@ -766,7 +770,11 @@ func WithParams(p Params) *Proxy { } } - m := metrics.Default + m := p.Metrics + if m == nil { + m = metrics.Default + } + if p.Flags.Debug() { m = metrics.Void } diff --git a/skipper.go b/skipper.go index ef179a5252..c7e9aa2d71 100644 --- a/skipper.go +++ b/skipper.go @@ -1309,9 +1309,8 @@ func listenAndServeQuit( } if o.EnableConnMetricsServer { - m := metrics.Default srv.ConnState = func(conn net.Conn, state http.ConnState) { - m.IncCounter(fmt.Sprintf("lb-conn-%s", state)) + mtr.IncCounter(fmt.Sprintf("lb-conn-%s", state)) } } @@ -1521,6 +1520,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { if mtr == nil { mtr = metrics.NewMetrics(mtrOpts) } + // set global instance for backwards compatibility metrics.Default = mtr // *DEPRECATED* client tracking parameter @@ -2010,10 +2010,8 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { proxyFlags := proxy.Flags(o.ProxyOptions) | o.ProxyFlags proxyParams := proxy.Params{ Routing: routing, - EndpointRegistry: endpointRegistry, - EnablePassiveHealthCheck: passiveHealthCheckEnabled, - PassiveHealthCheck: passiveHealthCheck, Flags: proxyFlags, + Metrics: mtr, PriorityRoutes: o.PriorityRoutes, IdleConnectionsPerHost: o.IdleConnectionsPerHost, CloseIdleConnsPeriod: o.CloseIdleConnsPeriod, @@ -2034,6 +2032,9 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ClientTLS: o.ClientTLS, CustomHttpRoundTripperWrap: o.CustomHttpRoundTripperWrap, RateLimiters: ratelimitRegistry, + EndpointRegistry: endpointRegistry, + EnablePassiveHealthCheck: passiveHealthCheckEnabled, + PassiveHealthCheck: passiveHealthCheck, } if o.EnableBreakers || len(o.BreakerSettings) > 0 { From eb185b0be7130aa021d92e4c374825208f91e589 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:07:42 +0200 Subject: [PATCH 0754/1047] build(deps): bump github.com/miekg/dns from 1.1.58 to 1.1.59 (#3030) Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.58 to 1.1.59. - [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release) - [Commits](https://github.com/miekg/dns/compare/v1.1.58...v1.1.59) --- updated-dependencies: - dependency-name: github.com/miekg/dns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2b7da6e4e5..9fad714076 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/hashicorp/memberlist v0.5.1 github.com/instana/go-sensor v1.62.0 github.com/lightstep/lightstep-tracer-go v0.26.0 - github.com/miekg/dns v1.1.58 + github.com/miekg/dns v1.1.59 github.com/oklog/ulid v1.3.1 github.com/open-policy-agent/opa v0.63.0 github.com/open-policy-agent/opa-envoy-plugin v0.63.0-envoy diff --git a/go.sum b/go.sum index c923ed203b..21f05420df 100644 --- a/go.sum +++ b/go.sum @@ -297,8 +297,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= -github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= +github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= From 32892b929eab797e00f96af6276fd1ff8b6cf5d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:08:19 +0200 Subject: [PATCH 0755/1047] build(deps): bump actions/checkout from 4.1.2 to 4.1.3 (#3032) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/9bb56186c3b09b4f86b1c65136769dd318469633...1d96c772d19495a3b5c517cd2bc0cb401ea0529f) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8b800e4854..be2d930a21 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 66da3a83cb..21857eabbd 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ae93cbeaaf..ff90d3d8c3 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index de15391562..6ecede95d6 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 82605e910d..698b688154 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.1.0 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 337f30a9d0..62667e13b2 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From 2330b7582cd8d50b0b646b401faab2637af8768d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:08:47 +0200 Subject: [PATCH 0756/1047] build(deps): bump actions/upload-artifact from 4.3.1 to 4.3.2 (#3033) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.1 to 4.3.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/5d5d22a31266ced268874388b861e4b58bb5c2f3...1746f4ab65b179e0ea60a494b83293b640dd5bba) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 698b688154..5f1ea00bef 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 with: name: SARIF file path: results.sarif From 1216610456f9cf94dd37943b8f258c4791fc7c79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:39:26 +0200 Subject: [PATCH 0757/1047] build(deps): bump amazonlinux from `ef9435f` to `5478f82` in /fuzz (#3031) Bumps amazonlinux from `ef9435f` to `5478f82`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index a12b34e967..5c518e2c96 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:ef9435f95b6fc5e7fd9ea156499e62a86f770d9928dfc83ecaa74af4fac3a663 +FROM amazonlinux:2023@sha256:5478f82c47e435ed988fa12a00b623ef3c920fadd669d40c596249cf81960c4c WORKDIR /workspace From 708e97af1d1e1511d7bc7803e1b2a0d5599df52b Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 22 Apr 2024 16:39:47 +0200 Subject: [PATCH 0758/1047] filters/auth: implement opt-out support for jwtMetrics (#3025) Extend configuration of `jwtMetrics` (#3020) to support opt-out - disable metrics collection when any of the configured route annotations (#3022) is present. This can be used to collect data about missing/invalid JWT tokens per hostname in multitenant ingress setup. Add `jwtMetrics` filter to all routes using `-default-filters-append` flag and allow users to annotate routes that do not require JWT token. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 4 + filters/auth/jwt_metrics.go | 13 ++- filters/auth/jwt_metrics_test.go | 161 +++++++++++++++++++++---------- 3 files changed, 125 insertions(+), 53 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index d83bb2d088..bc15fe1c94 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1584,6 +1584,10 @@ Examples: ``` jwtMetrics("{issuers: ['https://example.com', 'https://example.org']}") + +// opt-out +annotate("oauth.disabled", "this endpoint is public") -> +jwtMetrics("{issuers: ['https://example.com', 'https://example.org'], optOutAnnotations: [oauth.disabled]}") ``` diff --git a/filters/auth/jwt_metrics.go b/filters/auth/jwt_metrics.go index 71e093b6c3..48a66226bf 100644 --- a/filters/auth/jwt_metrics.go +++ b/filters/auth/jwt_metrics.go @@ -8,6 +8,7 @@ import ( "github.com/ghodss/yaml" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/annotate" "github.com/zalando/skipper/jwt" ) @@ -15,7 +16,8 @@ type ( jwtMetricsSpec struct{} jwtMetricsFilter struct { - Issuers []string `json:"issuers,omitempty"` + Issuers []string `json:"issuers,omitempty"` + OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` } ) @@ -46,6 +48,15 @@ func (s *jwtMetricsSpec) CreateFilter(args []interface{}) (filters.Filter, error func (f *jwtMetricsFilter) Request(ctx filters.FilterContext) {} func (f *jwtMetricsFilter) Response(ctx filters.FilterContext) { + if len(f.OptOutAnnotations) > 0 { + annotations := annotate.GetAnnotations(ctx) + for _, annotation := range f.OptOutAnnotations { + if _, ok := annotations[annotation]; ok { + return // opt-out + } + } + } + response := ctx.Response() if response.StatusCode >= 400 && response.StatusCode < 500 { diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index c1f5535fdf..5b2486ae98 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -3,155 +3,212 @@ package auth_test import ( "encoding/base64" "encoding/json" + "fmt" "net/http" + "net/url" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters/auth" - "github.com/zalando/skipper/filters/filtertest" + "github.com/zalando/skipper/filters/builtin" "github.com/zalando/skipper/metrics/metricstest" + "github.com/zalando/skipper/proxy" + "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/routing" ) func TestJwtMetrics(t *testing.T) { - spec := auth.NewJwtMetrics() - for _, tc := range []struct { name string - def string + filters string request *http.Request - response *http.Response + status int expected map[string]int64 }{ { name: "ignores 401 response", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test"}, - response: &http.Response{StatusCode: http.StatusUnauthorized}, + status: http.StatusUnauthorized, expected: map[string]int64{}, }, { name: "ignores 403 response", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test"}, - response: &http.Response{StatusCode: http.StatusForbidden}, + status: http.StatusForbidden, expected: map[string]int64{}, }, { name: "ignores 404 response", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test"}, - response: &http.Response{StatusCode: http.StatusNotFound}, + status: http.StatusNotFound, expected: map[string]int64{}, }, { - name: "missing-token", - def: `jwtMetrics("{issuers: [foo, bar]}")`, - request: &http.Request{Method: "GET", Host: "foo.test"}, - response: &http.Response{StatusCode: http.StatusOK}, + name: "missing-token", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, + request: &http.Request{Method: "GET", Host: "foo.test"}, + status: http.StatusOK, expected: map[string]int64{ - "GET.foo_test.200.missing-token": 1, + "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, }, }, { - name: "invalid-token-type", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + name: "invalid-token-type", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{"Basic foobarbaz"}}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, expected: map[string]int64{ - "GET.foo_test.200.invalid-token-type": 1, + "jwtMetrics.custom.GET.foo_test.200.invalid-token-type": 1, }, }, { - name: "invalid-token", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + name: "invalid-token", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{"Bearer invalid-token"}}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, expected: map[string]int64{ - "GET.foo_test.200.invalid-token": 1, + "jwtMetrics.custom.GET.foo_test.200.invalid-token": 1, }, }, { - name: "missing-issuer", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + name: "missing-issuer", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ "Bearer header." + marshalBase64JSON(t, map[string]any{"sub": "baz"}) + ".signature", }}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, expected: map[string]int64{ - "GET.foo_test.200.missing-issuer": 1, + "jwtMetrics.custom.GET.foo_test.200.missing-issuer": 1, }, }, { - name: "invalid-issuer", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + name: "invalid-issuer", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ "Bearer header." + marshalBase64JSON(t, map[string]any{"iss": "baz"}) + ".signature", }}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, expected: map[string]int64{ - "GET.foo_test.200.invalid-issuer": 1, + "jwtMetrics.custom.GET.foo_test.200.invalid-issuer": 1, }, }, { - name: "no invalid-issuer for empty issuers", - def: `jwtMetrics()`, + name: "no invalid-issuer for empty issuers", + filters: `jwtMetrics()`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ "Bearer header." + marshalBase64JSON(t, map[string]any{"iss": "baz"}) + ".signature", }}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, expected: map[string]int64{}, }, { - name: "no invalid-issuer when matches first", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + name: "no invalid-issuer when matches first", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ "Bearer header." + marshalBase64JSON(t, map[string]any{"iss": "foo"}) + ".signature", }}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, expected: map[string]int64{}, }, { - name: "no invalid-issuer when matches second", - def: `jwtMetrics("{issuers: [foo, bar]}")`, + name: "no invalid-issuer when matches second", + filters: `jwtMetrics("{issuers: [foo, bar]}")`, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ "Bearer header." + marshalBase64JSON(t, map[string]any{"iss": "bar"}) + ".signature", }}, }, - response: &http.Response{StatusCode: http.StatusOK}, + status: http.StatusOK, + expected: map[string]int64{}, + }, + { + name: "missing-token without opt-out", + filters: `jwtMetrics("{issuers: [foo, bar], optOutAnnotations: [oauth.disabled]}")`, + request: &http.Request{Method: "GET", Host: "foo.test"}, + status: http.StatusOK, + expected: map[string]int64{ + "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, + }, + }, + { + name: "no metrics when opted-out", + filters: ` + annotate("oauth.disabled", "this endpoint is public") -> + jwtMetrics("{issuers: [foo, bar], optOutAnnotations: [oauth.disabled, jwtMetrics.disabled]}") + `, + request: &http.Request{Method: "GET", Host: "foo.test"}, + status: http.StatusOK, expected: map[string]int64{}, }, + { + name: "no metrics when opted-out by alternative annotation", + filters: ` + annotate("jwtMetrics.disabled", "skip jwt metrics collection") -> + jwtMetrics("{issuers: [foo, bar], optOutAnnotations: [oauth.disabled, jwtMetrics.disabled]}") + `, + request: &http.Request{Method: "GET", Host: "foo.test"}, + status: http.StatusOK, + expected: map[string]int64{}, + }, + { + name: "counts missing-token when annotation does not match", + filters: ` + annotate("foo", "bar") -> + jwtMetrics("{issuers: [foo, bar], optOutAnnotations: [oauth.disabled, jwtMetrics.disabled]}") + `, + request: &http.Request{Method: "GET", Host: "foo.test"}, + status: http.StatusOK, + expected: map[string]int64{ + "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, + }, + }, } { t.Run(tc.name, func(t *testing.T) { - args := eskip.MustParseFilters(tc.def)[0].Args + m := &metricstest.MockMetrics{} + defer m.Close() + + fr := builtin.MakeRegistry() + fr.Register(auth.NewJwtMetrics()) + p := proxytest.Config{ + RoutingOptions: routing.Options{ + FilterRegistry: fr, + }, + Routes: eskip.MustParse(fmt.Sprintf(`* -> %s -> status(%d) -> `, tc.filters, tc.status)), + ProxyParams: proxy.Params{ + Metrics: m, + }, + }.Create() + defer p.Close() + + u, err := url.Parse(p.URL) + require.NoError(t, err) + tc.request.URL = u - filter, err := spec.CreateFilter(args) + resp, err := p.Client().Do(tc.request) require.NoError(t, err) + resp.Body.Close() - metrics := &metricstest.MockMetrics{} - ctx := &filtertest.Context{ - FRequest: tc.request, - FMetrics: metrics, - } - filter.Request(ctx) - ctx.FResponse = tc.response - filter.Response(ctx) + m.WithCounters(func(counters map[string]int64) { + // add incoming counter to simplify comparison + tc.expected["incoming.HTTP/1.1"] = 1 - metrics.WithCounters(func(counters map[string]int64) { assert.Equal(t, tc.expected, counters) }) }) From 4db806b0d3bf413f50ac0d92b2e4c64b5da97b8c Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Tue, 23 Apr 2024 19:54:01 +0200 Subject: [PATCH 0759/1047] Added PHC-related logs (#3036) Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- proxy/healthy_endpoints.go | 8 +++++--- proxy/proxy.go | 2 +- routing/endpointregistry.go | 5 +++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index b79a132769..20a6e7a656 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -11,7 +11,7 @@ type healthyEndpoints struct { endpointRegistry *routing.EndpointRegistry } -func (h *healthyEndpoints) filterHealthyEndpoints(endpoints []routing.LBEndpoint, rt *routing.Route) []routing.LBEndpoint { +func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []routing.LBEndpoint) []routing.LBEndpoint { if h == nil { return endpoints } @@ -20,8 +20,10 @@ func (h *healthyEndpoints) filterHealthyEndpoints(endpoints []routing.LBEndpoint filtered := make([]routing.LBEndpoint, 0, len(endpoints)) for _, e := range endpoints { - if p < e.Metrics.HealthCheckDropProbability() { - /* drop */ + dropProbability := e.Metrics.HealthCheckDropProbability() + if p < dropProbability { + ctx.Logger().Infof("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", + e.Host, p, dropProbability) } else { filtered = append(filtered, e) } diff --git a/proxy/proxy.go b/proxy/proxy.go index 6e0616530b..d11850141e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -540,7 +540,7 @@ func (p *Proxy) selectEndpoint(ctx *context) *routing.LBEndpoint { rt := ctx.route endpoints := rt.LBEndpoints endpoints = p.fadein.filterFadeIn(endpoints, rt) - endpoints = p.heathlyEndpoints.filterHealthyEndpoints(endpoints, rt) + endpoints = p.heathlyEndpoints.filterHealthyEndpoints(ctx, endpoints) lbctx := &routing.LBContext{ Request: ctx.request, diff --git a/routing/endpointregistry.go b/routing/endpointregistry.go index e43cb7f867..539f7c8fc4 100644 --- a/routing/endpointregistry.go +++ b/routing/endpointregistry.go @@ -5,6 +5,8 @@ import ( "sync/atomic" "time" + log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/eskip" ) @@ -166,6 +168,9 @@ func (r *EndpointRegistry) updateStats() { requests := e.totalRequests[curSlot].Load() if requests > r.minRequests { failedRoundTripsRatio := float64(failed) / float64(requests) + if failedRoundTripsRatio > 0.0 { + log.Infof("Passive health check: marking %q as unhealthy due to failed round trips ratio: %0.2f", key, failedRoundTripsRatio) + } e.healthCheckDropProbability.Store(min(failedRoundTripsRatio, r.maxHealthCheckDropProbability)) } else { e.healthCheckDropProbability.Store(0.0) From b5b0a32b634daac9cce286db4193dd0c2cbb746b Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Tue, 23 Apr 2024 19:54:17 +0200 Subject: [PATCH 0760/1047] Removed rtFailureProbability variable (#3038) This variable is actually is not being used during sending requests to the test proxy, but left in the assert statement. The test were working before, because the result of multiplication is close enough to zero. Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- proxy/healthy_endpoints_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index bc0fdea46a..c5c3a906b5 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -13,9 +13,8 @@ import ( ) const ( - nRequests = 15_000 - rtFailureProbability = 0.8 - period = 100 * time.Millisecond + nRequests = 15_000 + period = 100 * time.Millisecond ) func defaultEndpointRegistry() *routing.EndpointRegistry { @@ -178,7 +177,7 @@ func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { defer tp.close() defer ps.Close() failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0.33*rtFailureProbability*(1.0-rtFailureProbability)*float64(nRequests), failedReqs, 0.1*float64(nRequests)) + assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) }) } @@ -188,6 +187,6 @@ func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { defer tp.close() defer ps.Close() failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0.33*rtFailureProbability*(1.0-rtFailureProbability)*float64(nRequests), failedReqs, 0.1*float64(nRequests)) + assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) }) } From a45de70878093260ce41d283436867e051df122b Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Wed, 24 Apr 2024 16:13:36 +0200 Subject: [PATCH 0761/1047] phc: add a metric counter for endpoints that opted out before load balancing (#3035) Signed-off-by: Mustafa Abdelrahman --- docs/operation/operation.md | 6 ++++++ proxy/healthy_endpoints.go | 4 +++- proxy/healthy_endpoints_test.go | 37 +++++++++++++++------------------ proxy/proxy.go | 6 ++---- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/docs/operation/operation.md b/docs/operation/operation.md index b530f1f339..8969916091 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -921,6 +921,12 @@ The parameters of `-passive-health-check` option are: + `max-drop-probabilty=` - the maximum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request +### Metrics + +A set of metrics will be exposed to track passive health check: + +* `passive-health-check.endpoints.dropped`: Number of all endpoints dropped before load balancing a request, so after N requests and M endpoints are being dropped this counter would be N*M. + ## Memory consumption While Skipper is generally not memory bound, some features may require diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index 20a6e7a656..8e172993bd 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -3,6 +3,7 @@ package proxy import ( "math/rand" + "github.com/zalando/skipper/metrics" "github.com/zalando/skipper/routing" ) @@ -11,7 +12,7 @@ type healthyEndpoints struct { endpointRegistry *routing.EndpointRegistry } -func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []routing.LBEndpoint) []routing.LBEndpoint { +func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []routing.LBEndpoint, metrics metrics.Metrics) []routing.LBEndpoint { if h == nil { return endpoints } @@ -24,6 +25,7 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout if p < dropProbability { ctx.Logger().Infof("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", e.Host, p, dropProbability) + metrics.IncCounter("passive-health-check.endpoints.dropped") } else { filtered = append(filtered, e) } diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index c5c3a906b5..0ce0682879 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/routing" ) @@ -47,18 +48,23 @@ func sendGetRequests(t *testing.T, ps *httptest.Server) (failed int) { return } -func setupProxy(t *testing.T, doc string) (*testProxy, *httptest.Server) { +func setupProxy(t *testing.T, doc string) (*metricstest.MockMetrics, *httptest.Server) { endpointRegistry := defaultEndpointRegistry() + m := &metricstest.MockMetrics{} tp, err := newTestProxyWithParams(doc, Params{ EnablePassiveHealthCheck: true, EndpointRegistry: endpointRegistry, + Metrics: m, }) require.NoError(t, err) ps := httptest.NewServer(tp.proxy) - return tp, ps + t.Cleanup(tp.close) + t.Cleanup(ps.Close) + + return m, ps } func TestPHCWithoutRequests(t *testing.T) { @@ -73,10 +79,8 @@ func TestPHCWithoutRequests(t *testing.T) { for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { - tp, ps := setupProxy(t, fmt.Sprintf(`* -> <%s, "%s", "%s", "%s">`, + _, ps := setupProxy(t, fmt.Sprintf(`* -> <%s, "%s", "%s", "%s">`, algorithm, services[0].URL, services[1].URL, services[2].URL)) - defer tp.close() - defer ps.Close() rsp := sendGetRequest(t, ps, 0) assert.Equal(t, http.StatusOK, rsp.StatusCode) rsp.Body.Close() @@ -87,10 +91,8 @@ func TestPHCWithoutRequests(t *testing.T) { } t.Run("consistent hash with balance factor", func(t *testing.T) { - tp, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashBalanceFactor(1.25) -> `, + _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashBalanceFactor(1.25) -> `, services[0].URL, services[1].URL, services[2].URL)) - defer tp.close() - defer ps.Close() rsp := sendGetRequest(t, ps, 0) assert.Equal(t, http.StatusOK, rsp.StatusCode) rsp.Body.Close() @@ -136,20 +138,16 @@ func TestPHCForMultipleHealthyEndpoints(t *testing.T) { for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { - tp, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, + _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, algorithm, services[0].URL, services[1].URL, services[2].URL)) - defer tp.close() - defer ps.Close() failedReqs := sendGetRequests(t, ps) assert.Equal(t, 0, failedReqs) }) } t.Run("consistent hash with balance factor", func(t *testing.T) { - tp, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, + _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, services[0].URL, services[1].URL, services[2].URL)) - defer tp.close() - defer ps.Close() failedReqs := sendGetRequests(t, ps) assert.Equal(t, 0, failedReqs) }) @@ -172,20 +170,19 @@ func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { - tp, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, + mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, algorithm, services[0].URL, services[1].URL, services[2].URL)) - defer tp.close() - defer ps.Close() failedReqs := sendGetRequests(t, ps) assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error + }) }) } t.Run("consistent hash with balance factor", func(t *testing.T) { - tp, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, + _, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, services[0].URL, services[1].URL, services[2].URL)) - defer tp.close() - defer ps.Close() failedReqs := sendGetRequests(t, ps) assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) }) diff --git a/proxy/proxy.go b/proxy/proxy.go index d11850141e..442ea71324 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -540,7 +540,7 @@ func (p *Proxy) selectEndpoint(ctx *context) *routing.LBEndpoint { rt := ctx.route endpoints := rt.LBEndpoints endpoints = p.fadein.filterFadeIn(endpoints, rt) - endpoints = p.heathlyEndpoints.filterHealthyEndpoints(ctx, endpoints) + endpoints = p.heathlyEndpoints.filterHealthyEndpoints(ctx, endpoints, p.metrics) lbctx := &routing.LBContext{ Request: ctx.request, @@ -1193,9 +1193,7 @@ func (p *Proxy) do(ctx *context, parentSpan ot.Span) (err error) { if err := p.do(loopCTX, loopSpan); err != nil { // in case of error we have to copy the response in this recursion unwinding ctx.response = loopCTX.response - if err != nil { - p.applyFiltersOnError(ctx, processedFilters) - } + p.applyFiltersOnError(ctx, processedFilters) return err } From 429e8b5b031240223c6547a6fe15cc367fb5410b Mon Sep 17 00:00:00 2001 From: Magnus Jungsbluth Date: Fri, 26 Apr 2024 09:55:30 +0200 Subject: [PATCH 0762/1047] OPA: Add tracing for outbound http calls (#3034) Signed-off-by: Magnus Jungsbluth --- filters/openpolicyagent/evaluation.go | 3 +- .../opaauthorizerequest_test.go | 3 +- .../opaserveresponse/opaserveresponse_test.go | 3 +- filters/openpolicyagent/openpolicyagent.go | 54 ++++++-- filters/openpolicyagent/tracing.go | 71 ++++++++++- filters/openpolicyagent/tracing_test.go | 116 +++++++++++++++--- skipper.go | 3 +- 7 files changed, 218 insertions(+), 35 deletions(-) diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go index 29f1cd5415..f01b1be948 100644 --- a/filters/openpolicyagent/evaluation.go +++ b/filters/openpolicyagent/evaluation.go @@ -11,7 +11,6 @@ import ( "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/server" - "github.com/open-policy-agent/opa/tracing" "github.com/opentracing/opentracing-go" ) @@ -58,7 +57,7 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return nil, err } - err = envoyauth.Eval(ctx, opa, inputValue, result, rego.DistributedTracingOpts(tracing.Options{opa})) + err = envoyauth.Eval(ctx, opa, inputValue, result, rego.DistributedTracingOpts(opa.DistributedTracing())) if err != nil { return nil, err } diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index e95836c3ec..f0850dec79 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -19,6 +19,7 @@ import ( "github.com/zalando/skipper/filters" "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/tracing/tracingtest" "github.com/zalando/skipper/filters/filtertest" "github.com/zalando/skipper/filters/openpolicyagent" @@ -359,7 +360,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { } }`, opaControlPlane.URL(), ti.regoQuery)) - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) fr.Register(ftSpec) ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go index 5ffe0b41a1..0951cdf3c7 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -14,6 +14,7 @@ import ( "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/tracing/tracingtest" "github.com/zalando/skipper/filters/openpolicyagent" ) @@ -239,7 +240,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { } }`, opaControlPlane.URL(), ti.regoQuery)) - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) ftSpec := NewOpaServeResponseSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) fr.Register(ftSpec) ftSpec = NewOpaServeResponseWithReqBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 20e7dd269e..2970860493 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -47,6 +47,8 @@ const ( DefaultMaxRequestBodySize = 1 << 20 // 1 MB DefaultMaxMemoryBodyParsing = 100 * DefaultMaxRequestBodySize defaultBodyBufferSize = 8192 * 1024 + + spanNameEval = "open-policy-agent" ) type OpenPolicyAgentRegistry struct { @@ -69,6 +71,8 @@ type OpenPolicyAgentRegistry struct { maxMemoryBodyParsingSem *semaphore.Weighted maxRequestBodyBytes int64 bodyReadBufferSize int64 + + tracer opentracing.Tracer } type OpenPolicyAgentFilter interface { @@ -110,6 +114,13 @@ func WithCleanInterval(interval time.Duration) func(*OpenPolicyAgentRegistry) er } } +func WithTracer(tracer opentracing.Tracer) func(*OpenPolicyAgentRegistry) error { + return func(cfg *OpenPolicyAgentRegistry) error { + cfg.tracer = tracer + return nil + } +} + func NewOpenPolicyAgentRegistry(opts ...func(*OpenPolicyAgentRegistry) error) *OpenPolicyAgentRegistry { registry := &OpenPolicyAgentRegistry{ reuseDuration: defaultReuseDuration, @@ -395,6 +406,22 @@ func interpolateConfigTemplate(configTemplate []byte, bundleName string) ([]byte return buf.Bytes(), nil } +func buildTracingOptions(tracer opentracing.Tracer, bundleName string, manager *plugins.Manager) opatracing.Options { + return opatracing.NewOptions(WithTracingOptTracer(tracer), WithTracingOptBundleName(bundleName), WithTracingOptManager(manager)) +} + +func (registry *OpenPolicyAgentRegistry) withTracingOptions(bundleName string) func(*plugins.Manager) { + return func(m *plugins.Manager) { + options := buildTracingOptions( + registry.tracer, + bundleName, + m, + ) + + plugins.WithDistributedTracingOpts(options)(m) + } +} + // new returns a new OPA object. func (registry *OpenPolicyAgentRegistry) new(store storage.Store, configBytes []byte, instanceConfig OpenPolicyAgentInstanceConfig, filterName string, bundleName string, maxBodyBytes int64, bodyReadBufferSize int64) (*OpenPolicyAgentInstance, error) { id := uuid.New().String() @@ -412,7 +439,8 @@ func (registry *OpenPolicyAgentRegistry) new(store storage.Store, configBytes [] var logger logging.Logger = &QuietLogger{target: logging.Get()} logger = logger.WithFields(map[string]interface{}{"skipper-filter": filterName}) - manager, err := plugins.New(configBytes, id, store, configLabelsInfo(*opaConfig), plugins.Logger(logger)) + manager, err := plugins.New(configBytes, id, store, configLabelsInfo(*opaConfig), plugins.Logger(logger), registry.withTracingOptions(bundleName)) + if err != nil { return nil, err } @@ -544,20 +572,28 @@ func (opa *OpenPolicyAgentInstance) EnvoyPluginConfig() envoy.PluginConfig { return defaultConfig } +func setSpanTags(span opentracing.Span, bundleName string, manager *plugins.Manager) { + if bundleName != "" { + span.SetTag("opa.bundle_name", bundleName) + } + + if manager != nil { + for label, value := range manager.Labels() { + span.SetTag("opa.label."+label, value) + } + } +} + func (opa *OpenPolicyAgentInstance) startSpanFromContextWithTracer(tr opentracing.Tracer, parent opentracing.Span, ctx context.Context) (opentracing.Span, context.Context) { var span opentracing.Span if parent != nil { - span = tr.StartSpan("open-policy-agent", opentracing.ChildOf(parent.Context())) + span = tr.StartSpan(spanNameEval, opentracing.ChildOf(parent.Context())) } else { - span = tracing.CreateSpan("open-policy-agent", ctx, tr) + span = tracing.CreateSpan(spanNameEval, ctx, tr) } - span.SetTag("opa.bundle_name", opa.bundleName) - - for label, value := range opa.manager.Labels() { - span.SetTag("opa.label."+label, value) - } + setSpanTags(span, opa.bundleName, opa.manager) return span, opentracing.ContextWithSpan(ctx, span) } @@ -730,7 +766,7 @@ func (opa *OpenPolicyAgentInstance) Config() *config.Config { return opa.opaConf // DistributedTracing is an implementation of the envoyauth.EvalContext interface func (opa *OpenPolicyAgentInstance) DistributedTracing() opatracing.Options { - return opatracing.NewOptions(opa) + return buildTracingOptions(opa.registry.tracer, opa.bundleName, opa.manager) } // logging.Logger that does not pollute info with debug logs diff --git a/filters/openpolicyagent/tracing.go b/filters/openpolicyagent/tracing.go index d0b6b60244..bc63c780b9 100644 --- a/filters/openpolicyagent/tracing.go +++ b/filters/openpolicyagent/tracing.go @@ -3,8 +3,15 @@ package openpolicyagent import ( "net/http" + "github.com/open-policy-agent/opa/plugins" opatracing "github.com/open-policy-agent/opa/tracing" "github.com/opentracing/opentracing-go" + "github.com/zalando/skipper/logging" + "github.com/zalando/skipper/proxy" +) + +const ( + spanNameHttpOut = "open-policy-agent.http" ) func init() { @@ -14,15 +21,48 @@ func init() { type tracingFactory struct{} type transport struct { - opa *OpenPolicyAgentInstance + tracer opentracing.Tracer + bundleName string + manager *plugins.Manager + wrapped http.RoundTripper } +func WithTracingOptTracer(tracer opentracing.Tracer) func(*transport) { + return func(t *transport) { + t.tracer = tracer + } +} + +func WithTracingOptBundleName(bundleName string) func(*transport) { + return func(t *transport) { + t.bundleName = bundleName + } +} + +func WithTracingOptManager(manager *plugins.Manager) func(*transport) { + return func(t *transport) { + t.manager = manager + } +} + func (*tracingFactory) NewTransport(tr http.RoundTripper, opts opatracing.Options) http.RoundTripper { - return &transport{ - opa: opts[0].(*OpenPolicyAgentInstance), + log := &logging.DefaultLog{} + + wrapper := &transport{ wrapped: tr, } + + for _, o := range opts { + opt, ok := o.(func(*transport)) + if !ok { + log.Warnf("invalid type for OPA tracing option, expected func(*transport) got %T, tracing information might be incomplete", o) + } else { + opt(wrapper) + } + } + + return wrapper } func (*tracingFactory) NewHandler(f http.Handler, label string, opts opatracing.Options) http.Handler { @@ -32,15 +72,36 @@ func (*tracingFactory) NewHandler(f http.Handler, label string, opts opatracing. func (tr *transport) RoundTrip(req *http.Request) (*http.Response, error) { ctx := req.Context() parentSpan := opentracing.SpanFromContext(ctx) + var span opentracing.Span if parentSpan != nil { - span := parentSpan.Tracer().StartSpan("http.send", opentracing.ChildOf(parentSpan.Context())) + span = parentSpan.Tracer().StartSpan(spanNameHttpOut, opentracing.ChildOf(parentSpan.Context())) + } else if tr.tracer != nil { + span = tr.tracer.StartSpan(spanNameHttpOut) + } + + if span != nil { defer span.Finish() + + span.SetTag(proxy.HTTPMethodTag, req.Method) + span.SetTag(proxy.HTTPUrlTag, req.URL.String()) + span.SetTag(proxy.HostnameTag, req.Host) + span.SetTag(proxy.HTTPPathTag, req.URL.Path) + span.SetTag(proxy.ComponentTag, "skipper") + span.SetTag(proxy.SpanKindTag, proxy.SpanKindClient) + + setSpanTags(span, tr.bundleName, tr.manager) req = req.WithContext(opentracing.ContextWithSpan(ctx, span)) carrier := opentracing.HTTPHeadersCarrier(req.Header) span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, carrier) } - return tr.wrapped.RoundTrip(req) + resp, err := tr.wrapped.RoundTrip(req) + if err != nil && span != nil { + span.SetTag("error", true) + span.LogKV("event", "error", "message", err.Error()) + } + + return resp, err } diff --git a/filters/openpolicyagent/tracing_test.go b/filters/openpolicyagent/tracing_test.go index aad4f77ad4..dc5bd33370 100644 --- a/filters/openpolicyagent/tracing_test.go +++ b/filters/openpolicyagent/tracing_test.go @@ -3,39 +3,123 @@ package openpolicyagent import ( "context" "net/http" + "net/url" "testing" - opatracing "github.com/open-policy-agent/opa/tracing" + "github.com/open-policy-agent/opa/config" + "github.com/open-policy-agent/opa/plugins" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" "github.com/zalando/skipper/tracing/tracingtest" ) type MockTransport struct { + resp *http.Response + err error } func (t *MockTransport) RoundTrip(*http.Request) (*http.Response, error) { - return &http.Response{}, nil + return t.resp, t.err } func TestTracingFactory(t *testing.T) { - f := &tracingFactory{} - - tr := f.NewTransport(&MockTransport{}, opatracing.Options{&OpenPolicyAgentInstance{}}) - tracer := &tracingtest.Tracer{} - span := tracer.StartSpan("open-policy-agent") - ctx := opentracing.ContextWithSpan(context.Background(), span) - req := &http.Request{ - Header: map[string][]string{}, + testCases := []struct { + name string + req *http.Request + tracer opentracing.Tracer + parentSpan opentracing.Span + resp *http.Response + resperr error + }{ + { + name: "Sub-span created with parent span without tracer set", + req: &http.Request{ + Header: map[string][]string{}, + Method: "GET", + Host: "example.com", + URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, + }, + tracer: nil, + parentSpan: tracer.StartSpan("open-policy-agent"), + }, + { + name: "Sub-span created with parent span without tracer set", + req: &http.Request{ + Header: map[string][]string{}, + Method: "GET", + Host: "example.com", + URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, + }, + tracer: tracer, + parentSpan: tracer.StartSpan("open-policy-agent"), + }, + { + name: "Sub-span created without parent span", + req: &http.Request{ + Header: map[string][]string{}, + Method: "GET", + Host: "example.com", + URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, + }, + tracer: tracer, + }, + { + name: "Span contains error information", + req: &http.Request{ + Header: map[string][]string{}, + Method: "GET", + Host: "example.com", + URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, + }, + tracer: tracer, + resperr: assert.AnError, + }, } - req = req.WithContext(ctx) - _, err := tr.RoundTrip(req) - assert.NoError(t, err) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + f := &tracingFactory{} + tracer.Reset("") + + tr := f.NewTransport(&MockTransport{tc.resp, tc.resperr}, buildTracingOptions(tc.tracer, "bundle", &plugins.Manager{ + ID: "manager-id", + Config: &config.Config{ + Labels: map[string]string{"label": "value"}, + }, + })) + + if tc.parentSpan != nil { + ctx := opentracing.ContextWithSpan(context.Background(), tc.parentSpan) + tc.req = tc.req.WithContext(ctx) + } + + resp, err := tr.RoundTrip(tc.req) + if tc.parentSpan != nil { + tc.parentSpan.Finish() + } - span.Finish() - _, ok := tracer.FindSpan("http.send") - assert.True(t, ok, "No http.send span was created") + createdSpan, ok := tracer.FindSpan("open-policy-agent.http") + assert.True(t, ok, "No span was created") + + if tc.resperr == nil { + assert.NoError(t, err) + } else { + assert.Equal(t, true, createdSpan.Tags["error"], "Error tag was not set") + assert.Equal(t, tc.resperr, err, "Error was not propagated") + } + + assert.Equal(t, tc.resp, resp, "Response was not propagated") + + assert.Equal(t, tc.req.Method, createdSpan.Tags["http.method"]) + assert.Equal(t, tc.req.URL.String(), createdSpan.Tags["http.url"]) + assert.Equal(t, tc.req.Host, createdSpan.Tags["hostname"]) + assert.Equal(t, tc.req.URL.Path, createdSpan.Tags["http.path"]) + assert.Equal(t, "skipper", createdSpan.Tags["component"]) + assert.Equal(t, "client", createdSpan.Tags["span.kind"]) + assert.Equal(t, "bundle", createdSpan.Tags["opa.bundle_name"]) + assert.Equal(t, "value", createdSpan.Tags["opa.label.label"]) + }) + } } diff --git a/skipper.go b/skipper.go index c7e9aa2d71..f8b6d30130 100644 --- a/skipper.go +++ b/skipper.go @@ -1835,7 +1835,8 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { opaRegistry = openpolicyagent.NewOpenPolicyAgentRegistry( openpolicyagent.WithMaxRequestBodyBytes(o.OpenPolicyAgentMaxRequestBodySize), openpolicyagent.WithMaxMemoryBodyParsing(o.OpenPolicyAgentMaxMemoryBodyParsing), - openpolicyagent.WithCleanInterval(o.OpenPolicyAgentCleanerInterval)) + openpolicyagent.WithCleanInterval(o.OpenPolicyAgentCleanerInterval), + openpolicyagent.WithTracer(tracer)) defer opaRegistry.Close() opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0) From b02d6811bcda5477b62b36d1ec08cab8a7b90c8e Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 26 Apr 2024 12:37:38 +0200 Subject: [PATCH 0763/1047] Specify tracing span kind on creation (#3039) OpenTelemetry-OpenTracing bridge span kind can not be changed after creation, see https://github.com/open-telemetry/opentelemetry-go/issues/3953 The workaround is to specify span kind on creation which works for both Open Tracing and Open Telemetry bridge spans. Note that this change removes non-standard "shedder" kind from spans created by `admissionControl` filter. Use operation name "admission_control" to query its spans instead if needed. For #2104 Signed-off-by: Alexander Yastrebov --- filters/openpolicyagent/tracing.go | 26 +++++++++-------- filters/shedder/admission.go | 1 - net/httpclient.go | 21 ++++++-------- proxy/proxy.go | 45 +++++++++++++++++++----------- ratelimit/leakybucket.go | 15 +++++----- ratelimit/redis.go | 22 +++++++-------- tracing/tracingtest/testtracer.go | 3 ++ 7 files changed, 71 insertions(+), 62 deletions(-) diff --git a/filters/openpolicyagent/tracing.go b/filters/openpolicyagent/tracing.go index bc63c780b9..531e563913 100644 --- a/filters/openpolicyagent/tracing.go +++ b/filters/openpolicyagent/tracing.go @@ -71,25 +71,27 @@ func (*tracingFactory) NewHandler(f http.Handler, label string, opts opatracing. func (tr *transport) RoundTrip(req *http.Request) (*http.Response, error) { ctx := req.Context() - parentSpan := opentracing.SpanFromContext(ctx) - var span opentracing.Span - if parentSpan != nil { - span = parentSpan.Tracer().StartSpan(spanNameHttpOut, opentracing.ChildOf(parentSpan.Context())) + spanOpts := []opentracing.StartSpanOption{opentracing.Tags{ + proxy.HTTPMethodTag: req.Method, + proxy.HTTPUrlTag: req.URL.String(), + proxy.HostnameTag: req.Host, + proxy.HTTPPathTag: req.URL.Path, + proxy.ComponentTag: "skipper", + proxy.SpanKindTag: proxy.SpanKindClient, + }} + + var span opentracing.Span + if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil { + spanOpts = append(spanOpts, opentracing.ChildOf(parentSpan.Context())) + span = parentSpan.Tracer().StartSpan(spanNameHttpOut, spanOpts...) } else if tr.tracer != nil { - span = tr.tracer.StartSpan(spanNameHttpOut) + span = tr.tracer.StartSpan(spanNameHttpOut, spanOpts...) } if span != nil { defer span.Finish() - span.SetTag(proxy.HTTPMethodTag, req.Method) - span.SetTag(proxy.HTTPUrlTag, req.URL.String()) - span.SetTag(proxy.HostnameTag, req.Host) - span.SetTag(proxy.HTTPPathTag, req.URL.Path) - span.SetTag(proxy.ComponentTag, "skipper") - span.SetTag(proxy.SpanKindTag, proxy.SpanKindClient) - setSpanTags(span, tr.bundleName, tr.manager) req = req.WithContext(opentracing.ContextWithSpan(ctx, span)) diff --git a/filters/shedder/admission.go b/filters/shedder/admission.go index c963330d8c..80d63e49d0 100644 --- a/filters/shedder/admission.go +++ b/filters/shedder/admission.go @@ -456,7 +456,6 @@ func (ac *admissionControl) startSpan(ctx context.Context) (span opentracing.Spa if parent != nil { span = ac.tracer.StartSpan(admissionControlSpanName, opentracing.ChildOf(parent.Context())) ext.Component.Set(span, "skipper") - ext.SpanKind.Set(span, "shedder") span.SetTag("mode", ac.mode.String()) } return diff --git a/net/httpclient.go b/net/httpclient.go index bf9c7d7aa5..833bae6203 100644 --- a/net/httpclient.go +++ b/net/httpclient.go @@ -381,20 +381,17 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { } func (t *Transport) injectSpan(req *http.Request) (*http.Request, opentracing.Span) { - parentSpan := opentracing.SpanFromContext(req.Context()) - var span opentracing.Span - if parentSpan != nil { + spanOpts := []opentracing.StartSpanOption{opentracing.Tags{ + string(ext.Component): t.componentName, + string(ext.SpanKind): "client", + string(ext.HTTPMethod): req.Method, + string(ext.HTTPUrl): req.URL.String(), + }} + if parentSpan := opentracing.SpanFromContext(req.Context()); parentSpan != nil { req = req.WithContext(opentracing.ContextWithSpan(req.Context(), parentSpan)) - span = t.tracer.StartSpan(t.spanName, opentracing.ChildOf(parentSpan.Context())) - } else { - span = t.tracer.StartSpan(t.spanName) + spanOpts = append(spanOpts, opentracing.ChildOf(parentSpan.Context())) } - - // add Tags - ext.Component.Set(span, t.componentName) - ext.HTTPUrl.Set(span, req.URL.String()) - ext.HTTPMethod.Set(span, req.Method) - ext.SpanKind.Set(span, "client") + span := t.tracer.StartSpan(t.spanName, spanOpts...) _ = t.tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) diff --git a/proxy/proxy.go b/proxy/proxy.go index 442ea71324..549cc6778a 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -958,14 +958,19 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co if !ok { spanName = "proxy" } - ctx.proxySpan = tracing.CreateSpan(spanName, req.Context(), p.tracing.tracer) + + proxySpanOpts := []ot.StartSpanOption{ot.Tags{ + SpanKindTag: SpanKindClient, + SkipperRouteIDTag: ctx.route.Id, + }} + if parentSpan := ot.SpanFromContext(req.Context()); parentSpan != nil { + proxySpanOpts = append(proxySpanOpts, ot.ChildOf(parentSpan.Context())) + } + ctx.proxySpan = p.tracing.tracer.StartSpan(spanName, proxySpanOpts...) u := cloneURL(req.URL) u.RawQuery = "" - p.tracing. - setTag(ctx.proxySpan, SpanKindTag, SpanKindClient). - setTag(ctx.proxySpan, SkipperRouteIDTag, ctx.route.Id). - setTag(ctx.proxySpan, HTTPUrlTag, u.String()) + p.tracing.setTag(ctx.proxySpan, HTTPUrlTag, u.String()) p.setCommonSpanInfo(u, req, ctx.proxySpan) carrier := ot.HTTPHeadersCarrier(req.Header) @@ -1181,10 +1186,16 @@ func (p *Proxy) do(ctx *context, parentSpan ot.Span) (err error) { ctx.ensureDefaultResponse() } else if ctx.route.BackendType == eskip.LoopBackend { loopCTX := ctx.clone() - loopSpan := tracing.CreateSpan("loopback", ctx.request.Context(), p.tracing.tracer) - p.tracing. - setTag(loopSpan, SpanKindTag, SpanKindServer). - setTag(loopSpan, SkipperRouteIDTag, ctx.route.Id) + + loopSpanOpts := []ot.StartSpanOption{ot.Tags{ + SpanKindTag: SpanKindServer, + SkipperRouteIDTag: ctx.route.Id, + }} + if parentSpan := ot.SpanFromContext(ctx.request.Context()); parentSpan != nil { + loopSpanOpts = append(loopSpanOpts, ot.ChildOf(parentSpan.Context())) + } + loopSpan := p.tracing.tracer.StartSpan("loopback", loopSpanOpts...) + p.setCommonSpanInfo(ctx.Request().URL, ctx.Request(), loopSpan) ctx.parentSpan = loopSpan @@ -1481,12 +1492,15 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.metrics.IncCounter("incoming." + r.Proto) var ctx *context - var span ot.Span - if wireContext, err := p.tracing.tracer.Extract(ot.HTTPHeaders, ot.HTTPHeadersCarrier(r.Header)); err != nil { - span = p.tracing.tracer.StartSpan(p.tracing.initialOperationName) - } else { - span = p.tracing.tracer.StartSpan(p.tracing.initialOperationName, ext.RPCServerOption(wireContext)) + spanOpts := []ot.StartSpanOption{ot.Tags{ + SpanKindTag: SpanKindServer, + HTTPRemoteIPTag: stripPort(r.RemoteAddr), + }} + if wireContext, err := p.tracing.tracer.Extract(ot.HTTPHeaders, ot.HTTPHeadersCarrier(r.Header)); err == nil { + spanOpts = append(spanOpts, ext.RPCServerOption(wireContext)) } + span := p.tracing.tracer.StartSpan(p.tracing.initialOperationName, spanOpts...) + defer func() { if ctx != nil && ctx.proxySpan != nil { ctx.proxySpan.Finish() @@ -1533,9 +1547,6 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { r.URL.Path = rfc.PatchPath(r.URL.Path, r.URL.RawPath) } - p.tracing. - setTag(span, SpanKindTag, SpanKindServer). - setTag(span, HTTPRemoteIPTag, stripPort(r.RemoteAddr)) p.setCommonSpanInfo(r.URL, r, span) r = r.WithContext(ot.ContextWithSpan(r.Context(), span)) r = r.WithContext(routing.NewContext(r.Context())) diff --git a/ratelimit/leakybucket.go b/ratelimit/leakybucket.go index 7a432d1fac..a1253605e4 100644 --- a/ratelimit/leakybucket.go +++ b/ratelimit/leakybucket.go @@ -112,13 +112,12 @@ func (b *ClusterLeakyBucket) getBucketId(label string) string { } func (b *ClusterLeakyBucket) startSpan(ctx context.Context) (span opentracing.Span) { - parent := opentracing.SpanFromContext(ctx) - if parent != nil { - span = b.ringClient.StartSpan(leakyBucketSpanName, opentracing.ChildOf(parent.Context())) - } else { - span = opentracing.NoopTracer{}.StartSpan("") + spanOpts := []opentracing.StartSpanOption{opentracing.Tags{ + string(ext.Component): "skipper", + string(ext.SpanKind): "client", + }} + if parent := opentracing.SpanFromContext(ctx); parent != nil { + spanOpts = append(spanOpts, opentracing.ChildOf(parent.Context())) } - ext.Component.Set(span, "skipper") - ext.SpanKind.Set(span, "client") - return + return b.ringClient.StartSpan(leakyBucketSpanName, spanOpts...) } diff --git a/ratelimit/redis.go b/ratelimit/redis.go index 1c6804c0b2..7ac45c0dd3 100644 --- a/ratelimit/redis.go +++ b/ratelimit/redis.go @@ -84,14 +84,14 @@ func parentSpan(ctx context.Context) opentracing.Span { return opentracing.SpanFromContext(ctx) } -func (c *clusterLimitRedis) setCommonTags(span opentracing.Span) { - if span != nil { - ext.Component.Set(span, "skipper") - ext.SpanKind.Set(span, "client") - span.SetTag("ratelimit_type", c.typ) - span.SetTag("group", c.group) - span.SetTag("max_hits", c.maxHits) - span.SetTag("window", c.window.String()) +func (c *clusterLimitRedis) commonTags() opentracing.Tags { + return opentracing.Tags{ + string(ext.Component): "skipper", + string(ext.SpanKind): "client", + "ratelimit_type": c.typ, + "group": c.group, + "max_hits": c.maxHits, + "window": c.window.String(), } } @@ -114,10 +114,9 @@ func (c *clusterLimitRedis) Allow(ctx context.Context, clearText string) bool { var span opentracing.Span if parentSpan := parentSpan(ctx); parentSpan != nil { - span = c.ringClient.StartSpan(allowSpanName, opentracing.ChildOf(parentSpan.Context())) + span = c.ringClient.StartSpan(allowSpanName, opentracing.ChildOf(parentSpan.Context()), c.commonTags()) defer span.Finish() } - c.setCommonTags(span) allow, err := c.allow(ctx, clearText) failed := err != nil @@ -227,10 +226,9 @@ func (c *clusterLimitRedis) oldest(ctx context.Context, clearText string) (time. var span opentracing.Span if parentSpan := parentSpan(ctx); parentSpan != nil { - span = c.ringClient.StartSpan(oldestScoreSpanName, opentracing.ChildOf(parentSpan.Context())) + span = c.ringClient.StartSpan(oldestScoreSpanName, opentracing.ChildOf(parentSpan.Context()), c.commonTags()) defer span.Finish() } - c.setCommonTags(span) res, err := c.ringClient.ZRangeByScoreWithScoresFirst(ctx, key, 0.0, float64(now.UnixNano()), 0, 1) diff --git a/tracing/tracingtest/testtracer.go b/tracing/tracingtest/testtracer.go index 40fc5a742f..4da0a6d4ba 100644 --- a/tracing/tracingtest/testtracer.go +++ b/tracing/tracingtest/testtracer.go @@ -102,6 +102,9 @@ func (t *Tracer) StartSpan(operationName string, opts ...tracing.StartSpanOption s := t.createSpanBase() s.operationName = operationName s.Refs = sso.References + for k, v := range sso.Tags { + s.Tags[k] = v + } return s } From 8270cfc2e9e2eba48ee3d85a3289f2e0d34e7b21 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 26 Apr 2024 13:20:40 +0200 Subject: [PATCH 0764/1047] config: refactor test defaultConfig (#3042) Make `defaultConfig` return configuration equal to one created from default flags and modified by a helper function for defining expected test case values. Signed-off-by: Alexander Yastrebov --- config/config_test.go | 106 +++++++++++++++++++++----------------- config/testdata/test.yaml | 2 + 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index 5d740c9e93..6ce6c76cb3 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -62,15 +62,14 @@ func TestEnvOverrides_SwarmRedisPassword(t *testing.T) { } } -func defaultConfig() *Config { - return &Config{ - ConfigFile: "testdata/test.yaml", +func defaultConfig(with func(*Config)) *Config { + cfg := &Config{ Flags: nil, - Address: "localhost:8080", - StatusChecks: nil, + Address: ":9090", + StatusChecks: commaListFlag(), ExpectedBytesPerRequest: 50 * 1024, SupportListener: ":9911", - MaxLoopbacks: 12, + MaxLoopbacks: proxy.DefaultMaxLoopbacks, DefaultHTTPStatus: 404, MaxAuditBody: 1024, MaxMatcherBufferSize: 2097152, @@ -92,7 +91,7 @@ func defaultConfig() *Config { ApplicationLogLevelString: "INFO", ApplicationLogPrefix: "[APP]", EtcdPrefix: "/skipper", - EtcdTimeout: 2 * time.Second, + EtcdTimeout: time.Second, AppendFilters: &defaultFiltersFlags{}, PrependFilters: &defaultFiltersFlags{}, DisabledFilters: commaListFlag(), @@ -138,7 +137,6 @@ func defaultConfig() *Config { ServeMethodMetric: true, ServeStatusCodeMetric: true, SwarmRedisURLs: commaListFlag(), - SwarmRedisPassword: "set_from_file", SwarmRedisDialTimeout: 25 * time.Millisecond, SwarmRedisReadTimeout: 25 * time.Millisecond, SwarmRedisWriteTimeout: 25 * time.Millisecond, @@ -156,7 +154,6 @@ func defaultConfig() *Config { ForwardedHeadersList: commaListFlag(), ForwardedHeadersExcludeCIDRList: commaListFlag(), ClusterRatelimitMaxGroupShards: 1, - RefusePayload: multiFlag{"foo", "bar", "baz"}, ValidateQuery: true, ValidateQueryLog: true, LuaModules: commaListFlag(), @@ -166,44 +163,40 @@ func defaultConfig() *Config { OpenPolicyAgentMaxRequestBodySize: openpolicyagent.DefaultMaxRequestBodySize, OpenPolicyAgentMaxMemoryBodyParsing: openpolicyagent.DefaultMaxMemoryBodyParsing, } -} - -func defaultConfigWithoutNil() *Config { - cfg := defaultConfig() - cfg.StatusChecks = newListFlag("", "") + with(cfg) return cfg } func TestToOptions(t *testing.T) { - c := defaultConfigWithoutNil() + c := defaultConfig(func(c *Config) { + // ProxyFlags + c.Insecure = true // 1 + c.ProxyPreserveHost = true // 4 + c.RemoveHopHeaders = true // 16 + c.RfcPatchPath = true // 32 - // ProxyFlags - c.Insecure = true // 1 - c.ProxyPreserveHost = true // 4 - c.RemoveHopHeaders = true // 16 - c.RfcPatchPath = true // 32 - - // config - c.EtcdUrls = "127.0.0.1:5555" - c.WhitelistedHealthCheckCIDR = "127.0.0.0/8,10.0.0.0/8" - c.ForwardedHeadersList = commaListFlag("X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Method,X-Forwarded-Uri,X-Forwarded-Port=,X-Forwarded-Proto=http") - c.ForwardedHeadersList.Set("X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Method,X-Forwarded-Uri,X-Forwarded-Port=,X-Forwarded-Proto=http") - c.HostPatch = net.HostPatch{ - ToLower: true, - RemoteTrailingDot: true, - } - c.RefusePayload = append(c.RefusePayload, "refuse") - c.ValidateQuery = true - c.ValidateQueryLog = true + // config + c.EtcdUrls = "127.0.0.1:5555" + c.WhitelistedHealthCheckCIDR = "127.0.0.0/8,10.0.0.0/8" + c.ForwardedHeadersList = commaListFlag("X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Method,X-Forwarded-Uri,X-Forwarded-Port=,X-Forwarded-Proto=http") + c.ForwardedHeadersList.Set("X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Method,X-Forwarded-Uri,X-Forwarded-Port=,X-Forwarded-Proto=http") + c.HostPatch = net.HostPatch{ + ToLower: true, + RemoteTrailingDot: true, + } + c.RefusePayload = append(c.RefusePayload, "refuse") + c.ValidateQuery = true + c.ValidateQueryLog = true - c.CloneRoute = routeChangerConfig{} - if err := c.CloneRoute.Set("/foo/bar/"); err != nil { - t.Fatalf("Failed to set: %v", err) - } - c.EditRoute = routeChangerConfig{} - if err := c.EditRoute.Set("/foo/bar/"); err != nil { - t.Fatalf("Failed to set: %v", err) - } + c.CloneRoute = routeChangerConfig{} + if err := c.CloneRoute.Set("/foo/bar/"); err != nil { + t.Fatalf("Failed to set: %v", err) + } + c.EditRoute = routeChangerConfig{} + if err := c.EditRoute.Set("/foo/bar/"); err != nil { + t.Fatalf("Failed to set: %v", err) + } + }) if err := validate(c); err != nil { t.Fatalf("Failed to validate config: %v", err) @@ -322,22 +315,39 @@ func Test_NewConfigWithArgs(t *testing.T) { wantErr: true, }, { - name: "test only valid flag overwrite yaml file", - args: []string{"skipper", "-config-file=testdata/test.yaml", "-address=localhost:8080", "-refuse-payload=baz"}, - want: defaultConfig(), - wantErr: false, + name: "test only valid flag overwrite yaml file", + args: []string{"skipper", "-config-file=testdata/test.yaml", "-address=localhost:8080", "-refuse-payload=baz"}, + want: defaultConfig(func(c *Config) { + c.ConfigFile = "testdata/test.yaml" + c.Address = "localhost:8080" + c.MaxLoopbacks = 12 + c.EtcdTimeout = 2 * time.Second + c.StatusChecks = &listFlag{ + sep: ",", + allowed: map[string]bool{}, + value: "http://foo.test/bar,http://baz.test/qux", + values: []string{"http://foo.test/bar", "http://baz.test/qux"}, + } + c.SwarmRedisPassword = "set_from_file" + c.RefusePayload = multiFlag{"foo", "bar", "baz"} + }), }, } { t.Run(tt.name, func(t *testing.T) { cfg := NewConfig() err := cfg.ParseArgs(tt.args[0], tt.args[1:]) + if (err != nil) != tt.wantErr { - t.Errorf("config.NewConfig() error = %v, wantErr %v", err, tt.wantErr) + t.Fatalf("config.NewConfig() error: %v, wantErr: %v", err, tt.wantErr) } if !tt.wantErr { - if cmp.Equal(cfg, tt.want, cmp.AllowUnexported(listFlag{}, pluginFlag{}, defaultFiltersFlags{}, mapFlags{}), cmpopts.IgnoreUnexported(Config{}), cmpopts.IgnoreFields(Config{}, "Flags")) == false { - t.Errorf("config.NewConfig() got vs. want:\n%v", cmp.Diff(cfg, tt.want, cmp.AllowUnexported(listFlag{}, pluginFlag{}, defaultFiltersFlags{}, mapFlags{}), cmpopts.IgnoreUnexported(Config{}), cmpopts.IgnoreFields(Config{}, "Flags"))) + d := cmp.Diff(cfg, tt.want, + cmp.AllowUnexported(listFlag{}, pluginFlag{}, defaultFiltersFlags{}, mapFlags{}), + cmpopts.IgnoreUnexported(Config{}), cmpopts.IgnoreFields(Config{}, "Flags"), + ) + if d != "" { + t.Errorf("config.NewConfig() want vs got:\n%s", d) } } }) diff --git a/config/testdata/test.yaml b/config/testdata/test.yaml index 76410e66b4..26714a9871 100644 --- a/config/testdata/test.yaml +++ b/config/testdata/test.yaml @@ -2,6 +2,8 @@ address: localhost:9090 max-loopbacks: 12 etcd-timeout: 2s status-checks: + - http://foo.test/bar + - http://baz.test/qux swarm-redis-password: set_from_file refuse-payload: - foo From 7e226676c05035eb6aa63edf4d41256254d68c85 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 26 Apr 2024 14:07:30 +0200 Subject: [PATCH 0765/1047] proxy: use proxy tracing setTag helper (#3043) Use setTag helper to enable tag exclusion This is follow up fix for #3039 Signed-off-by: Alexander Yastrebov --- proxy/proxy.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 549cc6778a..dc61d23d14 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -960,8 +960,7 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co } proxySpanOpts := []ot.StartSpanOption{ot.Tags{ - SpanKindTag: SpanKindClient, - SkipperRouteIDTag: ctx.route.Id, + SpanKindTag: SpanKindClient, }} if parentSpan := ot.SpanFromContext(req.Context()); parentSpan != nil { proxySpanOpts = append(proxySpanOpts, ot.ChildOf(parentSpan.Context())) @@ -970,7 +969,9 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co u := cloneURL(req.URL) u.RawQuery = "" - p.tracing.setTag(ctx.proxySpan, HTTPUrlTag, u.String()) + p.tracing. + setTag(ctx.proxySpan, HTTPUrlTag, u.String()). + setTag(ctx.proxySpan, SkipperRouteIDTag, ctx.route.Id) p.setCommonSpanInfo(u, req, ctx.proxySpan) carrier := ot.HTTPHeadersCarrier(req.Header) @@ -1188,14 +1189,13 @@ func (p *Proxy) do(ctx *context, parentSpan ot.Span) (err error) { loopCTX := ctx.clone() loopSpanOpts := []ot.StartSpanOption{ot.Tags{ - SpanKindTag: SpanKindServer, - SkipperRouteIDTag: ctx.route.Id, + SpanKindTag: SpanKindServer, }} if parentSpan := ot.SpanFromContext(ctx.request.Context()); parentSpan != nil { loopSpanOpts = append(loopSpanOpts, ot.ChildOf(parentSpan.Context())) } loopSpan := p.tracing.tracer.StartSpan("loopback", loopSpanOpts...) - + p.tracing.setTag(loopSpan, SkipperRouteIDTag, ctx.route.Id) p.setCommonSpanInfo(ctx.Request().URL, ctx.Request(), loopSpan) ctx.parentSpan = loopSpan @@ -1493,8 +1493,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { var ctx *context spanOpts := []ot.StartSpanOption{ot.Tags{ - SpanKindTag: SpanKindServer, - HTTPRemoteIPTag: stripPort(r.RemoteAddr), + SpanKindTag: SpanKindServer, }} if wireContext, err := p.tracing.tracer.Extract(ot.HTTPHeaders, ot.HTTPHeadersCarrier(r.Header)); err == nil { spanOpts = append(spanOpts, ext.RPCServerOption(wireContext)) @@ -1547,6 +1546,7 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { r.URL.Path = rfc.PatchPath(r.URL.Path, r.URL.RawPath) } + p.tracing.setTag(span, HTTPRemoteIPTag, stripPort(r.RemoteAddr)) p.setCommonSpanInfo(r.URL, r, span) r = r.WithContext(ot.ContextWithSpan(r.Context(), span)) r = r.WithContext(routing.NewContext(r.Context())) From 6ee45b17b37d9f00ab373755aafd62956a3b23ec Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 26 Apr 2024 15:54:09 +0200 Subject: [PATCH 0766/1047] filters: deprecate OpenTracing in FilterContext (#3044) Deprecate OpenTracing FilterContext getters and remove their usage from filters. For #2104 Signed-off-by: Alexander Yastrebov --- filters/filters.go | 7 +++++++ filters/openpolicyagent/openpolicyagent.go | 2 +- proxy/context.go | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/filters/filters.go b/filters/filters.go index ac8e06d3bd..d3e060e826 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -108,9 +108,16 @@ type FilterContext interface { Metrics() Metrics // Allow filters to add Tags, Baggage to the trace or set the ComponentName. + // + // Deprecated: OpenTracing is deprecated, see https://github.com/zalando/skipper/issues/2104. + // Use opentracing.SpanFromContext(ctx.Request().Context()).Tracer() to get the proxy span Tracer. Tracer() opentracing.Tracer // Allow filters to create their own spans + // + // Deprecated: OpenTracing is deprecated, see https://github.com/zalando/skipper/issues/2104. + // Filter spans should be children of the proxy span, + // use opentracing.SpanFromContext(ctx.Request().Context()) to get the proxy span. ParentSpan() opentracing.Span // Returns a clone of the FilterContext including a brand new request object. diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 2970860493..3144c6c2ea 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -599,7 +599,7 @@ func (opa *OpenPolicyAgentInstance) startSpanFromContextWithTracer(tr opentracin } func (opa *OpenPolicyAgentInstance) StartSpanFromFilterContext(fc filters.FilterContext) (opentracing.Span, context.Context) { - return opa.startSpanFromContextWithTracer(fc.Tracer(), fc.ParentSpan(), fc.Request().Context()) + return opa.StartSpanFromContext(fc.Request().Context()) } func (opa *OpenPolicyAgentInstance) StartSpanFromContext(ctx context.Context) (opentracing.Span, context.Context) { diff --git a/proxy/context.go b/proxy/context.go index 468dc4ac98..f65757a426 100644 --- a/proxy/context.go +++ b/proxy/context.go @@ -296,7 +296,7 @@ func (c *context) Split() (filters.FilterContext, error) { } func (c *context) Loopback() { - loopSpan := c.Tracer().StartSpan(c.proxy.tracing.initialOperationName, opentracing.ChildOf(c.ParentSpan().Context())) + loopSpan := c.tracer.StartSpan(c.proxy.tracing.initialOperationName, opentracing.ChildOf(c.parentSpan.Context())) defer loopSpan.Finish() err := c.proxy.do(c, loopSpan) if c.response != nil && c.response.Body != nil { From 9570e4fe8e9e08e72d6f2989dacc8e4850a9b970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 26 Apr 2024 16:05:48 +0200 Subject: [PATCH 0767/1047] add span logs for redis spans with error (#3045) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- ratelimit/redis.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ratelimit/redis.go b/ratelimit/redis.go index 7ac45c0dd3..7ff9f6802e 100644 --- a/ratelimit/redis.go +++ b/ratelimit/redis.go @@ -122,8 +122,9 @@ func (c *clusterLimitRedis) Allow(ctx context.Context, clearText string) bool { failed := err != nil if failed { allow = !c.failClosed - setError(span) - c.logError("Failed to determine if operation is allowed: %v", err) + msgFmt := "Failed to determine if operation is allowed: %v" + setError(span, fmt.Sprintf(msgFmt, err)) + c.logError(msgFmt, err) } if span != nil { span.SetTag("allowed", allow) @@ -207,9 +208,10 @@ func (c *clusterLimitRedis) Delta(clearText string) time.Duration { return d } -func setError(span opentracing.Span) { +func setError(span opentracing.Span, msg string) { if span != nil { ext.Error.Set(span, true) + span.LogKV("log", msg) } } @@ -233,7 +235,7 @@ func (c *clusterLimitRedis) oldest(ctx context.Context, clearText string) (time. res, err := c.ringClient.ZRangeByScoreWithScoresFirst(ctx, key, 0.0, float64(now.UnixNano()), 0, 1) if err != nil { - setError(span) + setError(span, fmt.Sprintf("Failed to execute ZRangeByScoreWithScoresFirst: %v", err)) return time.Time{}, err } @@ -243,13 +245,14 @@ func (c *clusterLimitRedis) oldest(ctx context.Context, clearText string) (time. s, ok := res.(string) if !ok { - setError(span) - return time.Time{}, errors.New("failed to evaluate redis data") + msg := "failed to evaluate redis data" + setError(span, msg) + return time.Time{}, errors.New(msg) } oldest, err := strconv.ParseInt(s, 10, 64) if err != nil { - setError(span) + setError(span, fmt.Sprintf("failed to convert value to int64: %v", err)) return time.Time{}, fmt.Errorf("failed to convert value to int64: %w", err) } From f6bf0336bafcaab3a7e71447d6925fdaf54d5c2a Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Fri, 26 Apr 2024 19:00:43 +0200 Subject: [PATCH 0768/1047] Create a minimum failure ratio for enabling PHC (#3046) It is hardcoded to 5% now, but we plan to make it configurable. Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- proxy/healthy_endpoints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index 8e172993bd..fbdfcbd773 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -22,7 +22,7 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout filtered := make([]routing.LBEndpoint, 0, len(endpoints)) for _, e := range endpoints { dropProbability := e.Metrics.HealthCheckDropProbability() - if p < dropProbability { + if dropProbability > 0.05 && p < dropProbability { ctx.Logger().Infof("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", e.Host, p, dropProbability) metrics.IncCounter("passive-health-check.endpoints.dropped") From cd27ca242826eb6eef08321bdd8515ca1353db66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:43:32 +0200 Subject: [PATCH 0769/1047] build(deps): bump actions/upload-artifact from 4.3.2 to 4.3.3 (#3047) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/1746f4ab65b179e0ea60a494b83293b640dd5bba...65462800fd760344b1a7b4382951275a0abb4808) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 5f1ea00bef..a85f751cb2 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: SARIF file path: results.sarif From 7ebbfb3857b14e755748d9c175d0567b7dcf2be5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:44:05 +0200 Subject: [PATCH 0770/1047] build(deps): bump actions/checkout from 4.1.3 to 4.1.4 (#3048) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.3 to 4.1.4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/1d96c772d19495a3b5c517cd2bc0cb401ea0529f...0ad4b8fadaa221de15dcec353f45205ec38ea70b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index be2d930a21..b745fb91a2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 21857eabbd..ff37e82e13 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ff90d3d8c3..ceed97b2a5 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 6ecede95d6..005f63a9a4 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index a85f751cb2..bdaa8830bf 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v3.1.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 62667e13b2..bf13776ef0 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From b3e3b81e8cec97270050b1a9fe8d5b3a74cc4dde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:44:35 +0200 Subject: [PATCH 0771/1047] build(deps): bump github.com/instana/go-sensor from 1.62.0 to 1.62.1 (#3049) Bumps [github.com/instana/go-sensor](https://github.com/instana/go-sensor) from 1.62.0 to 1.62.1. - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/RELEASE.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.62.0...v1.62.1) --- updated-dependencies: - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9fad714076..9927277b60 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 - github.com/instana/go-sensor v1.62.0 + github.com/instana/go-sensor v1.62.1 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.59 github.com/oklog/ulid v1.3.1 diff --git a/go.sum b/go.sum index 21f05420df..37838b8360 100644 --- a/go.sum +++ b/go.sum @@ -255,8 +255,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.62.0 h1:SVHeDpkv8QGtJjGp351nmQpHtsigtuonAp1JZl2dHac= -github.com/instana/go-sensor v1.62.0/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= +github.com/instana/go-sensor v1.62.1 h1:WkZ3Foej8UtvuJlmnPO9PDyRbEHMVVn3Dmzt9GQpLd8= +github.com/instana/go-sensor v1.62.1/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= From a446073dab32bb7a3654212941148ae89d669a7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:45:15 +0200 Subject: [PATCH 0772/1047] build(deps): bump github.com/open-policy-agent/opa-envoy-plugin (#3050) Bumps [github.com/open-policy-agent/opa-envoy-plugin](https://github.com/open-policy-agent/opa-envoy-plugin) from 0.63.0-envoy to 0.64.1-envoy. - [Release notes](https://github.com/open-policy-agent/opa-envoy-plugin/releases) - [Changelog](https://github.com/open-policy-agent/opa-envoy-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa-envoy-plugin/compare/v0.63.0-envoy...v0.64.1-envoy) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa-envoy-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 22 +++++++++++----------- go.sum | 44 ++++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 9927277b60..337ac5533b 100644 --- a/go.mod +++ b/go.mod @@ -27,8 +27,8 @@ require ( github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.59 github.com/oklog/ulid v1.3.1 - github.com/open-policy-agent/opa v0.63.0 - github.com/open-policy-agent/opa-envoy-plugin v0.63.0-envoy + github.com/open-policy-agent/opa v0.64.1 + github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.19.0 @@ -61,7 +61,7 @@ require ( ) require ( - cloud.google.com/go/compute v1.23.3 // indirect + cloud.google.com/go/compute v1.24.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect dario.cat/mergo v1.0.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect @@ -78,7 +78,7 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect - github.com/containerd/containerd v1.7.14 // indirect + github.com/containerd/containerd v1.7.15 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -137,7 +137,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect @@ -166,15 +166,15 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.16.0 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.19.0 // indirect + golang.org/x/tools v0.20.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect - google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/grpc v1.62.1 // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/grpc v1.63.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.3.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 37838b8360..0e6ea11508 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -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/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= @@ -78,8 +78,8 @@ github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/P github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA= -github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg= +github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= +github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -338,10 +338,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/open-policy-agent/opa v0.63.0 h1:ztNNste1v8kH0/vJMJNquE45lRvqwrM5mY9Ctr9xIXw= -github.com/open-policy-agent/opa v0.63.0/go.mod h1:9VQPqEfoB2N//AToTxzZ1pVTVPUoF2Mhd64szzjWPpU= -github.com/open-policy-agent/opa-envoy-plugin v0.63.0-envoy h1:1RO1GG9dA0NBZPBKxBDBMLfGgCG0maxy6v6saC25+hs= -github.com/open-policy-agent/opa-envoy-plugin v0.63.0-envoy/go.mod h1:qtjZJlkuBxBnnypjWQ+zpd+OM5URWNmJQLI3u7FU1C4= +github.com/open-policy-agent/opa v0.64.1 h1:n8IJTYlFWzqiOYx+JiawbErVxiqAyXohovcZxYbskxQ= +github.com/open-policy-agent/opa v0.64.1/go.mod h1:j4VeLorVpKipnkQ2TDjWshEuV3cvP/rHzQhYaraUXZY= +github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy h1:YCH3+S/tbivQS7lZDsdNbZkHYp8lpFcUIAYzAkLwWKo= +github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy/go.mod h1:oesxvjmmIO3p0nPmy/xzv9oEsL4B+Ofdmd5vnHNZzAA= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -377,8 +377,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= @@ -542,8 +542,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -628,8 +628,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= 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= @@ -648,12 +648,12 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -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/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +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-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/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -661,8 +661,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= 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= From 678fc5a604483a527e6b69645f497675e020328c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 29 Apr 2024 12:56:33 +0200 Subject: [PATCH 0773/1047] filters: update godoc for deprecated FilterContext OpenTracing fields (#3052) The ParentSpan is not a proxy span but either ingress span of "filters" span when enabled. Follow up on #3044 Signed-off-by: Alexander Yastrebov --- filters/filters.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/filters/filters.go b/filters/filters.go index d3e060e826..7090a0de47 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -110,14 +110,14 @@ type FilterContext interface { // Allow filters to add Tags, Baggage to the trace or set the ComponentName. // // Deprecated: OpenTracing is deprecated, see https://github.com/zalando/skipper/issues/2104. - // Use opentracing.SpanFromContext(ctx.Request().Context()).Tracer() to get the proxy span Tracer. + // Use opentracing.SpanFromContext(ctx.Request().Context()).Tracer() to get the Tracer. Tracer() opentracing.Tracer // Allow filters to create their own spans // // Deprecated: OpenTracing is deprecated, see https://github.com/zalando/skipper/issues/2104. - // Filter spans should be children of the proxy span, - // use opentracing.SpanFromContext(ctx.Request().Context()) to get the proxy span. + // Filter spans should be children of the request span, + // use opentracing.SpanFromContext(ctx.Request().Context()) to get it. ParentSpan() opentracing.Span // Returns a clone of the FilterContext including a brand new request object. From 68cce2ae4b2607376ea247f85cf06e50380df89a Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Tue, 30 Apr 2024 19:32:22 +0200 Subject: [PATCH 0774/1047] Add -min-drop-probability cmdline option for the PHC (#3053) Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- docs/operation/operation.md | 13 ++++---- metricsinit_test.go | 1 + proxy/healthy_endpoints.go | 2 +- proxy/healthy_endpoints_test.go | 1 + proxy/proxy.go | 19 +++++++++++- proxy/proxy_test.go | 55 +++++++++++++++++++++++++++++++++ routing/endpointregistry.go | 11 ++++--- skipper.go | 1 + 8 files changed, 91 insertions(+), 12 deletions(-) diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 8969916091..8be9bc3c31 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -901,15 +901,15 @@ Passive Health Check(PHC). PHC works the following way: the entire uptime is divided in chunks of `period`, per every period Skipper calculates the total amount of requests and amount of requests failed per every endpoint. While next period is going on, the Skipper takes a look at previous period and if the amount of requests in the previous period is more than `min-requests` -for the given endpoints then Skipper will send reduced (the more `max-drop-probability` -and failed requests ratio in previous period are, the stronger reduction is) -amount of requests compared to amount sent without PHC. +and failed requests ratio is more than `min-drop-probability` for the given endpoints +then Skipper will send reduced (the more `max-drop-probability` and failed requests ratio +in previous period are, the stronger reduction is) amount of requests compared to amount sent without PHC. Having this, we expect less requests to fail because a lot of them would be sent to endpoints that seem to be healthy instead. To enable this feature, you need to provide `-passive-health-check` option having all forementioned parameters -(`period`, `min-requests`, `max-drop-probability`) defined, -for instance: `-passive-health-check=period=1s,min-requests=10,max-drop-probability=0.9`. +(`period`, `min-requests`, `min-drop-probability`, `max-drop-probability`) defined, +for instance: `-passive-health-check=period=1s,min-requests=10,min-drop-probability=0.05,max-drop-probability=0.9`. You need to define all parameters on your side, there are no defaults, and skipper will not run if PHC params are passed only partially. @@ -918,7 +918,8 @@ However, Skipper will run without this feature, if no `-passive-health-check` is The parameters of `-passive-health-check` option are: + `period=` - the duration of stats reset period + `min-requests=` - the minimum number of requests per `period` per backend endpoint required to activate PHC for this endpoint -+ `max-drop-probabilty=` - the maximum possible probability of unhealthy endpoint being not considered ++ `min-drop-probabilty=<0.0 <= p <= 1.0>` - the minimum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request. The same value is in fact used as minimal failed requests ratio for PHC to be enabled for this endpoint ++ `max-drop-probabilty=<0.0 <= p <= 1.0>` - the maximum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request ### Metrics diff --git a/metricsinit_test.go b/metricsinit_test.go index ff9740254e..c48172486f 100644 --- a/metricsinit_test.go +++ b/metricsinit_test.go @@ -57,6 +57,7 @@ func TestInitOrderAndDefault(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "0.9", + "min-drop-probability": "0.05", }, } diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index fbdfcbd773..8e172993bd 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -22,7 +22,7 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout filtered := make([]routing.LBEndpoint, 0, len(endpoints)) for _, e := range endpoints { dropProbability := e.Metrics.HealthCheckDropProbability() - if dropProbability > 0.05 && p < dropProbability { + if p < dropProbability { ctx.Logger().Infof("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", e.Host, p, dropProbability) metrics.IncCounter("passive-health-check.endpoints.dropped") diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index 0ce0682879..ad5a35da11 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -24,6 +24,7 @@ func defaultEndpointRegistry() *routing.EndpointRegistry { StatsResetPeriod: period, MinRequests: 10, MaxHealthCheckDropProbability: 1.0, + MinHealthCheckDropProbability: 0.01, }) } diff --git a/proxy/proxy.go b/proxy/proxy.go index dc61d23d14..2de0161d01 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -154,6 +154,11 @@ type PassiveHealthCheck struct { // potentially opt out the endpoint from the list of healthy endpoints MinRequests int64 + // The minimal ratio of failed requests in a single period to potentially opt out the endpoint + // from the list of healthy endpoints. This ratio is equal to the minimal non-zero probability of + // dropping endpoint out from load balancing for every specific request + MinDropProbability float64 + // The maximum probability of unhealthy endpoint to be dropped out from load balancing for every specific request MaxDropProbability float64 } @@ -186,6 +191,15 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e return false, nil, fmt.Errorf("passive health check: invalid minRequests value: %s", value) } result.MinRequests = int64(minRequests) + case "min-drop-probability": + minDropProbability, err := strconv.ParseFloat(value, 64) + if err != nil { + return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) + } + if minDropProbability < 0 || minDropProbability > 1 { + return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) + } + result.MinDropProbability = minDropProbability case "max-drop-probability": maxDropProbability, err := strconv.ParseFloat(value, 64) if err != nil { @@ -202,9 +216,12 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e keysInitialized[key] = struct{}{} } - if len(keysInitialized) != 3 { + if len(keysInitialized) != 4 { return false, nil, fmt.Errorf("passive health check: missing required parameters") } + if result.MinDropProbability >= result.MaxDropProbability { + return false, nil, fmt.Errorf("passive health check: minDropProbability should be less than maxDropProbability") + } return true, result, nil } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 0ba1fce3eb..ea3cc79463 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -2299,6 +2299,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "somethingInvalid", "min-requests": "10", "max-drop-probability": "0.9", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2309,12 +2310,14 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "0.9", + "min-drop-probability": "0.05", }, expectedEnabled: true, expectedParams: &PassiveHealthCheck{ Period: 1 * time.Minute, MinRequests: 10, MaxDropProbability: 0.9, + MinDropProbability: 0.05, }, expectedError: nil, }, @@ -2323,6 +2326,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "-1m", "min-requests": "10", "max-drop-probability": "0.9", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2333,6 +2337,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "somethingInvalid", "max-drop-probability": "0.9", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2343,6 +2348,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "-10", "max-drop-probability": "0.9", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2353,6 +2359,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "somethingInvalid", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2363,6 +2370,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "-0.1", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2373,6 +2381,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "3.1415", + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, @@ -2383,6 +2392,51 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "0.9", + "min-drop-probability": "somethingInvalid", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf("passive health check: invalid minDropProbability value: somethingInvalid"), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "-0.1", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf("passive health check: invalid minDropProbability value: -0.1"), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "3.1415", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf("passive health check: invalid minDropProbability value: 3.1415"), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.05", + "min-drop-probability": "0.9", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf("passive health check: minDropProbability should be less than maxDropProbability"), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", "non-existing": "non-existing", }, expectedEnabled: false, @@ -2394,6 +2448,7 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", /* forgot max-drop-probability */ + "min-drop-probability": "0.05", }, expectedEnabled: false, expectedParams: nil, diff --git a/routing/endpointregistry.go b/routing/endpointregistry.go index 539f7c8fc4..4609d7bc96 100644 --- a/routing/endpointregistry.go +++ b/routing/endpointregistry.go @@ -98,6 +98,7 @@ type EndpointRegistry struct { lastSeenTimeout time.Duration statsResetPeriod time.Duration minRequests int64 + minHealthCheckDropProbability float64 maxHealthCheckDropProbability float64 quit chan struct{} @@ -113,6 +114,7 @@ type RegistryOptions struct { PassiveHealthCheckEnabled bool StatsResetPeriod time.Duration MinRequests int64 + MinHealthCheckDropProbability float64 MaxHealthCheckDropProbability float64 } @@ -164,17 +166,17 @@ func (r *EndpointRegistry) updateStats() { e.totalFailedRoundTrips[nextSlot].Store(0) e.totalRequests[nextSlot].Store(0) + newDropProbability := 0.0 failed := e.totalFailedRoundTrips[curSlot].Load() requests := e.totalRequests[curSlot].Load() if requests > r.minRequests { failedRoundTripsRatio := float64(failed) / float64(requests) - if failedRoundTripsRatio > 0.0 { + if failedRoundTripsRatio > r.minHealthCheckDropProbability { log.Infof("Passive health check: marking %q as unhealthy due to failed round trips ratio: %0.2f", key, failedRoundTripsRatio) + newDropProbability = min(failedRoundTripsRatio, r.maxHealthCheckDropProbability) } - e.healthCheckDropProbability.Store(min(failedRoundTripsRatio, r.maxHealthCheckDropProbability)) - } else { - e.healthCheckDropProbability.Store(0.0) } + e.healthCheckDropProbability.Store(newDropProbability) e.curSlot.Store(nextSlot) return true @@ -201,6 +203,7 @@ func NewEndpointRegistry(o RegistryOptions) *EndpointRegistry { lastSeenTimeout: o.LastSeenTimeout, statsResetPeriod: o.StatsResetPeriod, minRequests: o.MinRequests, + minHealthCheckDropProbability: o.MinHealthCheckDropProbability, maxHealthCheckDropProbability: o.MaxHealthCheckDropProbability, quit: make(chan struct{}), diff --git a/skipper.go b/skipper.go index f8b6d30130..4bcf0336f1 100644 --- a/skipper.go +++ b/skipper.go @@ -1945,6 +1945,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { PassiveHealthCheckEnabled: passiveHealthCheckEnabled, StatsResetPeriod: passiveHealthCheck.Period, MinRequests: passiveHealthCheck.MinRequests, + MinHealthCheckDropProbability: passiveHealthCheck.MinDropProbability, MaxHealthCheckDropProbability: passiveHealthCheck.MaxDropProbability, }) ro := routing.Options{ From d0d558edcfc041ba6cc344d0e539fa6f3fec3b31 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 30 Apr 2024 19:44:16 +0200 Subject: [PATCH 0775/1047] filters/auth: extend jwtMetrics opt-out options (#3055) Allow opt-out via state bag key. The usecase is to opt-out metrics collection for routes that use `oauthTokeninfo*` and `oauthGrant` filters. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 7 ++++- filters/auth/jwt_metrics.go | 10 +++++++ filters/auth/jwt_metrics_test.go | 46 +++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index bc15fe1c94..457dc55b1c 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1585,9 +1585,14 @@ Examples: ``` jwtMetrics("{issuers: ['https://example.com', 'https://example.org']}") -// opt-out +// opt-out by annotation annotate("oauth.disabled", "this endpoint is public") -> jwtMetrics("{issuers: ['https://example.com', 'https://example.org'], optOutAnnotations: [oauth.disabled]}") + +// opt-out by state bag: +// oauthTokeninfo* and oauthGrant filters store token info in the state bag using "tokeninfo" key. +oauthTokeninfoAnyKV("foo", "bar") -> +jwtMetrics("{issuers: ['https://example.com', 'https://example.org'], optOutStateBag: [tokeninfo]}") ``` diff --git a/filters/auth/jwt_metrics.go b/filters/auth/jwt_metrics.go index 48a66226bf..b4e92cddd9 100644 --- a/filters/auth/jwt_metrics.go +++ b/filters/auth/jwt_metrics.go @@ -18,6 +18,7 @@ type ( jwtMetricsFilter struct { Issuers []string `json:"issuers,omitempty"` OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` + OptOutStateBag []string `json:"optOutStateBag,omitempty"` } ) @@ -57,6 +58,15 @@ func (f *jwtMetricsFilter) Response(ctx filters.FilterContext) { } } + if len(f.OptOutStateBag) > 0 { + sb := ctx.StateBag() + for _, key := range f.OptOutStateBag { + if _, ok := sb[key]; ok { + return // opt-out + } + } + } + response := ctx.Response() if response.StatusCode >= 400 && response.StatusCode < 500 { diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index 5b2486ae98..373ec9a01f 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/http/httptest" "net/url" "testing" @@ -20,6 +21,15 @@ import ( ) func TestJwtMetrics(t *testing.T) { + testAuthServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Authorization") != "Bearer foobarbaz" { + w.WriteHeader(http.StatusUnauthorized) + } else { + w.Write([]byte(`{"foo": "bar"}`)) + } + })) + defer testAuthServer.Close() + for _, tc := range []struct { name string filters string @@ -148,7 +158,7 @@ func TestJwtMetrics(t *testing.T) { }, }, { - name: "no metrics when opted-out", + name: "no metrics when opted-out by annotation", filters: ` annotate("oauth.disabled", "this endpoint is public") -> jwtMetrics("{issuers: [foo, bar], optOutAnnotations: [oauth.disabled, jwtMetrics.disabled]}") @@ -179,6 +189,38 @@ func TestJwtMetrics(t *testing.T) { "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, }, }, + { + name: "no metrics when opted-out by state bag", + // oauthTokeninfoAnyKV stores token info in the state bag using the key "tokeninfo" + filters: ` + oauthTokeninfoAnyKV("foo", "bar") -> + jwtMetrics("{issuers: [foo, bar], optOutStateBag: [tokeninfo]}") + `, + request: &http.Request{Method: "GET", Host: "foo.test", + Header: http.Header{"Authorization": []string{ + "Bearer foobarbaz", + }}, + }, + status: http.StatusOK, + expected: map[string]int64{}, + }, + { + name: "counts invalid-token when state bag does not match", + // oauthTokeninfoAnyKV stores token info in the state bag using the key "tokeninfo" + filters: ` + oauthTokeninfoAnyKV("foo", "bar") -> + jwtMetrics("{issuers: [foo, bar], optOutStateBag: [does.not.match]}") + `, + request: &http.Request{Method: "GET", Host: "foo.test", + Header: http.Header{"Authorization": []string{ + "Bearer foobarbaz", + }}, + }, + status: http.StatusOK, + expected: map[string]int64{ + "jwtMetrics.custom.GET.foo_test.200.invalid-token": 1, + }, + }, } { t.Run(tc.name, func(t *testing.T) { m := &metricstest.MockMetrics{} @@ -186,6 +228,7 @@ func TestJwtMetrics(t *testing.T) { fr := builtin.MakeRegistry() fr.Register(auth.NewJwtMetrics()) + fr.Register(auth.NewOAuthTokeninfoAnyKVWithOptions(auth.TokeninfoOptions{URL: testAuthServer.URL})) p := proxytest.Config{ RoutingOptions: routing.Options{ FilterRegistry: fr, @@ -204,6 +247,7 @@ func TestJwtMetrics(t *testing.T) { resp, err := p.Client().Do(tc.request) require.NoError(t, err) resp.Body.Close() + require.Equal(t, tc.status, resp.StatusCode) m.WithCounters(func(counters map[string]int64) { // add incoming counter to simplify comparison From b4b82cdd2927a0968b03fa14ecdeb4f63b1e0675 Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Thu, 2 May 2024 14:53:10 +0200 Subject: [PATCH 0776/1047] Fix PHC documentation rendering (#3057) Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- docs/operation/operation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 8be9bc3c31..3c4b74bc79 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -916,6 +916,7 @@ You need to define all parameters on your side, there are no defaults, and skipp However, Skipper will run without this feature, if no `-passive-health-check` is provided at all. The parameters of `-passive-health-check` option are: + + `period=` - the duration of stats reset period + `min-requests=` - the minimum number of requests per `period` per backend endpoint required to activate PHC for this endpoint + `min-drop-probabilty=<0.0 <= p <= 1.0>` - the minimum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request. The same value is in fact used as minimal failed requests ratio for PHC to be enabled for this endpoint From ff285476c2223218dc0888c740d70215f0279604 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 11:31:27 +0200 Subject: [PATCH 0777/1047] build(deps): bump actions/setup-go from 5.0.0 to 5.0.1 (#3064) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/0c52d547c9bc32b1aa3301fd7a9cb496313a4491...cdcb36043654635271a94b9a6d1392de5bb323a7) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b745fb91a2..06b6ee8d13 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: fetch-depth: 2 - name: Setup Go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: go-version: '^1.21' check-latest: true diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ceed97b2a5..c6f842f77d 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -25,7 +25,7 @@ jobs: - name: Checkout uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.21' diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 005f63a9a4..cbd9fb8e3f 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,7 +13,7 @@ jobs: contents: read steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index bf13776ef0..47cd6aa8ea 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" From d090d8334fde7db195732daabfeaabcfa6127c9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 11:31:49 +0200 Subject: [PATCH 0778/1047] build(deps): bump golang.org/x/oauth2 from 0.19.0 to 0.20.0 (#3063) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.19.0 to 0.20.0. - [Commits](https://github.com/golang/oauth2/compare/v0.19.0...v0.20.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 5 ++--- go.sum | 10 ++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 337ac5533b..f4e1e94ef2 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab golang.org/x/crypto v0.22.0 golang.org/x/net v0.24.0 - golang.org/x/oauth2 v0.19.0 + golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.7.0 golang.org/x/term v0.19.0 golang.org/x/time v0.5.0 @@ -61,8 +61,7 @@ require ( ) require ( - cloud.google.com/go/compute v1.24.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect diff --git a/go.sum b/go.sum index 0e6ea11508..9af63076c7 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -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/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +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= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -562,8 +560,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 19b849ff848c1cb71548e9f0e80e5da1305d078c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 11:31:55 +0200 Subject: [PATCH 0779/1047] build(deps): bump google.golang.org/protobuf from 1.33.0 to 1.34.0 (#3062) Bumps google.golang.org/protobuf from 1.33.0 to 1.34.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f4e1e94ef2..c22a804dc5 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( golang.org/x/sync v0.7.0 golang.org/x/term v0.19.0 golang.org/x/time v0.5.0 - google.golang.org/protobuf v1.33.0 + google.golang.org/protobuf v1.34.0 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf diff --git a/go.sum b/go.sum index 9af63076c7..208f130c42 100644 --- a/go.sum +++ b/go.sum @@ -670,8 +670,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -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.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= +google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From 631a46dda082aaa2d4796f72dbc8143b031e1c47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 12:11:32 +0200 Subject: [PATCH 0780/1047] build(deps): bump github.com/sony/gobreaker from 0.5.0 to 1.0.0 (#3061) Bumps [github.com/sony/gobreaker](https://github.com/sony/gobreaker) from 0.5.0 to 1.0.0. - [Commits](https://github.com/sony/gobreaker/compare/v0.5.0...v1.0.0) --- updated-dependencies: - dependency-name: github.com/sony/gobreaker dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c22a804dc5..c527117a4d 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/redis/go-redis/v9 v9.5.1 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 - github.com/sony/gobreaker v0.5.0 + github.com/sony/gobreaker v1.0.0 github.com/stretchr/testify v1.9.0 github.com/szuecs/rate-limit-buffer v0.9.0 github.com/testcontainers/testcontainers-go v0.30.0 diff --git a/go.sum b/go.sum index 208f130c42..df5ad4d060 100644 --- a/go.sum +++ b/go.sum @@ -412,8 +412,8 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= -github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= +github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= From ac9e8527186cea4efd9b16a44a76da76ee8d856c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 12:21:53 +0200 Subject: [PATCH 0781/1047] build(deps): bump golang.org/x/term from 0.19.0 to 0.20.0 (#3060) Bumps [golang.org/x/term](https://github.com/golang/term) from 0.19.0 to 0.20.0. - [Commits](https://github.com/golang/term/compare/v0.19.0...v0.20.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c527117a4d..bb65ea411f 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( golang.org/x/net v0.24.0 golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.7.0 - golang.org/x/term v0.19.0 + golang.org/x/term v0.20.0 golang.org/x/time v0.5.0 google.golang.org/protobuf v1.34.0 gopkg.in/go-jose/go-jose.v2 v2.6.3 @@ -166,7 +166,7 @@ require ( go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.19.0 // indirect + golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.20.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect diff --git a/go.sum b/go.sum index df5ad4d060..18775eb6e7 100644 --- a/go.sum +++ b/go.sum @@ -599,10 +599,10 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From f15ffba7864c0a6f3cacb0c790401257f70b6207 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 7 May 2024 12:13:00 +0200 Subject: [PATCH 0782/1047] docs: clarify jwtMetrics opt-out logic (#3066) Follow up on #3055 Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 457dc55b1c..d22546b95e 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1560,7 +1560,7 @@ jwtValidation("https://login.microsoftonline.com/{tenantId}/v2.0") > This filter is experimental and may change in the future, please see tests for example usage. -The filter parses (but does not validate) JWT token from `Authorization` request header on response path if status is not 4xx +The filter parses (but does not validate) JWT token from `Authorization` request header on response path and increments the following counters: * `missing-token`: request does not have `Authorization` header @@ -1577,6 +1577,8 @@ jwtMetrics.custom.GET.example_org.200.invalid-token and therefore requires approximately `count(HTTP methods) * count(Hosts) * count(Statuses) * 8` bytes of additional memory. +The filter does nothing if response status is 4xx or route is opt-out via annotation or state bag value. + The filter requires single string argument that is parsed as YAML. For convenience use [flow style format](https://yaml.org/spec/1.2.2/#chapter-7-flow-style-productions). From 75c404b378bc41814e7182a3bad2466e77da3ca9 Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Tue, 7 May 2024 15:23:16 +0200 Subject: [PATCH 0783/1047] Make min-drop-probability of PHC optional (#3067) The default value (0.0) makes some sense and is equivalent to how PHC was working before. Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- docs/operation/operation.md | 14 +++++++++----- proxy/proxy.go | 35 +++++++++++++++++++++-------------- proxy/proxy_test.go | 18 +++++++++++++++++- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 3c4b74bc79..2052387e9f 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -907,13 +907,17 @@ in previous period are, the stronger reduction is) amount of requests compared t Having this, we expect less requests to fail because a lot of them would be sent to endpoints that seem to be healthy instead. -To enable this feature, you need to provide `-passive-health-check` option having all forementioned parameters -(`period`, `min-requests`, `min-drop-probability`, `max-drop-probability`) defined, -for instance: `-passive-health-check=period=1s,min-requests=10,min-drop-probability=0.05,max-drop-probability=0.9`. +To enable this feature, you need to provide `-passive-health-check` option having forementioned parameters +(`period`, `min-requests`, `min-drop-probability`, `max-drop-probability`) defined. +`period`, `min-requests`, `max-drop-probability` are required parameters, it is not possible for PHC to be enabled without +them explicitly defined by user. `min-drop-probability` is implicitly defined as `0.0` if not explicitly set by user. +Valid examples of `-passive-health-check` are: -You need to define all parameters on your side, there are no defaults, and skipper will not run if PHC params are passed only partially. ++ `-passive-health-check=period=1s,min-requests=10,min-drop-probability=0.05,max-drop-probability=0.9` ++ `-passive-health-check=period=1s,min-requests=10,max-drop-probability=0.9` -However, Skipper will run without this feature, if no `-passive-health-check` is provided at all. +If `-passive-health-check` option is provided, but some required parameters are not defined, Skipper will not start. +Skipper will run without this feature, if no `-passive-health-check` is provided at all. The parameters of `-passive-health-check` option are: diff --git a/proxy/proxy.go b/proxy/proxy.go index 2de0161d01..cc5061954a 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -21,6 +21,7 @@ import ( "time" "unicode/utf8" + "golang.org/x/exp/maps" "golang.org/x/time/rate" ot "github.com/opentracing/opentracing-go" @@ -169,10 +170,16 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e } result := &PassiveHealthCheck{} - keysInitialized := make(map[string]struct{}) + requiredParams := map[string]struct{}{ + "period": {}, + "max-drop-probability": {}, + "min-requests": {}, + } for key, value := range o { + delete(requiredParams, key) switch key { + /* required parameters */ case "period": period, err := time.ParseDuration(value) if err != nil { @@ -191,15 +198,6 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e return false, nil, fmt.Errorf("passive health check: invalid minRequests value: %s", value) } result.MinRequests = int64(minRequests) - case "min-drop-probability": - minDropProbability, err := strconv.ParseFloat(value, 64) - if err != nil { - return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) - } - if minDropProbability < 0 || minDropProbability > 1 { - return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) - } - result.MinDropProbability = minDropProbability case "max-drop-probability": maxDropProbability, err := strconv.ParseFloat(value, 64) if err != nil { @@ -209,15 +207,24 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e return false, nil, fmt.Errorf("passive health check: invalid maxDropProbability value: %s", value) } result.MaxDropProbability = maxDropProbability + + /* optional parameters */ + case "min-drop-probability": + minDropProbability, err := strconv.ParseFloat(value, 64) + if err != nil { + return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) + } + if minDropProbability < 0 || minDropProbability > 1 { + return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) + } + result.MinDropProbability = minDropProbability default: return false, nil, fmt.Errorf("passive health check: invalid parameter: key=%s,value=%s", key, value) } - - keysInitialized[key] = struct{}{} } - if len(keysInitialized) != 4 { - return false, nil, fmt.Errorf("passive health check: missing required parameters") + if len(requiredParams) != 0 { + return false, nil, fmt.Errorf("passive health check: missing required parameters %+v", maps.Keys(requiredParams)) } if result.MinDropProbability >= result.MaxDropProbability { return false, nil, fmt.Errorf("passive health check: minDropProbability should be less than maxDropProbability") diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index ea3cc79463..51780a4f5d 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -2452,7 +2452,23 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, expectedEnabled: false, expectedParams: nil, - expectedError: fmt.Errorf("passive health check: missing required parameters"), + expectedError: fmt.Errorf("passive health check: missing required parameters [max-drop-probability]"), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + /* using default max-drop-probability */ + }, + expectedEnabled: true, + expectedParams: &PassiveHealthCheck{ + Period: 1 * time.Minute, + MinRequests: 10, + MaxDropProbability: 0.9, + MinDropProbability: 0.0, + }, + expectedError: nil, }, } { t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { From d563b7c8824cd4a78f3dda34e27f4b9d427c9892 Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Tue, 7 May 2024 16:52:31 +0200 Subject: [PATCH 0784/1047] phc: test multiple unhealthy endpoints (#3058) use vegeta with all algorithms except `consistentHash` as it's a bit tricky to set different headers for attacks Signed-off-by: Mustafa Abdelrahman --- proxy/healthy_endpoints_test.go | 201 ++++++++++++++++++++++++-------- 1 file changed, 151 insertions(+), 50 deletions(-) diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index ad5a35da11..1fa6694761 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -2,6 +2,8 @@ package proxy import ( "fmt" + "io" + "math" "net/http" "net/http/httptest" "testing" @@ -10,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zalando/skipper/metrics/metricstest" + zhttptest "github.com/zalando/skipper/net/httptest" "github.com/zalando/skipper/routing" ) @@ -22,8 +25,8 @@ func defaultEndpointRegistry() *routing.EndpointRegistry { return routing.NewEndpointRegistry(routing.RegistryOptions{ PassiveHealthCheckEnabled: true, StatsResetPeriod: period, - MinRequests: 10, - MaxHealthCheckDropProbability: 1.0, + MinRequests: 2, + MaxHealthCheckDropProbability: 0.95, MinHealthCheckDropProbability: 0.01, }) } @@ -49,8 +52,18 @@ func sendGetRequests(t *testing.T, ps *httptest.Server) (failed int) { return } +func fireVegeta(t *testing.T, ps *httptest.Server, freq int, per time.Duration, timeout time.Duration) *zhttptest.VegetaAttacker { + t.Helper() + va := zhttptest.NewVegetaAttacker(ps.URL, freq, per, timeout) + va.Attack(io.Discard, 5*time.Second, t.Name()) + return va +} + func setupProxy(t *testing.T, doc string) (*metricstest.MockMetrics, *httptest.Server) { - endpointRegistry := defaultEndpointRegistry() + return setupProxyWithCustomEndpointRegisty(t, doc, defaultEndpointRegistry()) +} + +func setupProxyWithCustomEndpointRegisty(t *testing.T, doc string, endpointRegistry *routing.EndpointRegistry) (*metricstest.MockMetrics, *httptest.Server) { m := &metricstest.MockMetrics{} tp, err := newTestProxyWithParams(doc, Params{ @@ -68,20 +81,33 @@ func setupProxy(t *testing.T, doc string) (*metricstest.MockMetrics, *httptest.S return m, ps } -func TestPHCWithoutRequests(t *testing.T) { - services := []*httptest.Server{} - for i := 0; i < 3; i++ { +func setupServices(t *testing.T, healthy, unhealthy int) []string { + services := []string{} + for i := 0; i < healthy; i++ { service := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) - services = append(services, service) - defer service.Close() + services = append(services, service.URL) + t.Cleanup(service.Close) } + for i := 0; i < unhealthy; i++ { + service := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(100 * time.Millisecond) + w.WriteHeader(http.StatusOK) + })) + services = append(services, service.URL) + t.Cleanup(service.Close) + } + return services +} + +func TestPHCWithoutRequests(t *testing.T) { + services := setupServices(t, 3, 0) for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { _, ps := setupProxy(t, fmt.Sprintf(`* -> <%s, "%s", "%s", "%s">`, - algorithm, services[0].URL, services[1].URL, services[2].URL)) + algorithm, services[0], services[1], services[2])) rsp := sendGetRequest(t, ps, 0) assert.Equal(t, http.StatusOK, rsp.StatusCode) rsp.Body.Close() @@ -93,7 +119,7 @@ func TestPHCWithoutRequests(t *testing.T) { t.Run("consistent hash with balance factor", func(t *testing.T) { _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashBalanceFactor(1.25) -> `, - services[0].URL, services[1].URL, services[2].URL)) + services[0], services[1], services[2])) rsp := sendGetRequest(t, ps, 0) assert.Equal(t, http.StatusOK, rsp.StatusCode) rsp.Body.Close() @@ -104,13 +130,10 @@ func TestPHCWithoutRequests(t *testing.T) { } func TestPHCForSingleHealthyEndpoint(t *testing.T) { - service := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - defer service.Close() + service := setupServices(t, 1, 0)[0] endpointRegistry := defaultEndpointRegistry() - doc := fmt.Sprintf(`* -> "%s"`, service.URL) + doc := fmt.Sprintf(`* -> "%s"`, service) tp, err := newTestProxyWithParams(doc, Params{ EnablePassiveHealthCheck: true, EndpointRegistry: endpointRegistry, @@ -128,63 +151,141 @@ func TestPHCForSingleHealthyEndpoint(t *testing.T) { } func TestPHCForMultipleHealthyEndpoints(t *testing.T) { - services := []*httptest.Server{} - for i := 0; i < 3; i++ { - service := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - services = append(services, service) - defer service.Close() - } + services := setupServices(t, 3, 0) for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, - algorithm, services[0].URL, services[1].URL, services[2].URL)) - failedReqs := sendGetRequests(t, ps) - assert.Equal(t, 0, failedReqs) + algorithm, services[0], services[1], services[2])) + va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) + count200, ok := va.CountStatus(200) + assert.True(t, ok) + assert.InDelta(t, count200, 15000, 50) // 3000*5s, the delta is for CI + assert.Equal(t, count200, int(va.TotalRequests())) }) } t.Run("consistent hash with balance factor", func(t *testing.T) { _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, - services[0].URL, services[1].URL, services[2].URL)) - failedReqs := sendGetRequests(t, ps) - assert.Equal(t, 0, failedReqs) + services[0], services[1], services[2])) + va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) + count200, ok := va.CountStatus(200) + assert.True(t, ok) + assert.Equal(t, count200, int(va.TotalRequests())) }) } func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { - services := []*httptest.Server{} - for i := 0; i < 3; i++ { - serviceNum := i - service := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if serviceNum == 0 { - // emulating unhealthy endpoint - time.Sleep(100 * time.Millisecond) - } - w.WriteHeader(http.StatusOK) - })) - services = append(services, service) - defer service.Close() - } - - for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { + services := setupServices(t, 2, 1) + for _, algorithm := range []string{"random", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, - algorithm, services[0].URL, services[1].URL, services[2].URL)) - failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) + algorithm, services[0], services[1], services[2])) + + va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) + + count200, ok := va.CountStatus(200) + assert.True(t, ok) + + count504, ok := va.CountStatus(504) + assert.True(t, ok) + + failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) + t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) + + assert.InDelta(t, float64(count504), failedRequests, 5) + assert.InDelta(t, 0, float64(failedRequests), 0.1*float64(va.TotalRequests())) mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error + assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(va.TotalRequests())) // allow 30% error }) }) } + t.Run("consistentHash", func(t *testing.T) { + endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ + PassiveHealthCheckEnabled: true, + StatsResetPeriod: 1 * time.Second, + MinRequests: 1, // with 2 test case fails on github actions + MaxHealthCheckDropProbability: 0.95, + MinHealthCheckDropProbability: 0.01, + }) + mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> `, + services[0], services[1], services[2]), endpointRegistry) + failedReqs := sendGetRequests(t, ps) + assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error + }) + }) + t.Run("consistent hash with balance factor", func(t *testing.T) { - _, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, - services[0].URL, services[1].URL, services[2].URL)) + mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, + services[0], services[1], services[2])) failedReqs := sendGetRequests(t, ps) assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error + }) + }) +} + +func TestPHCForMultipleHealthyAndMultipleUnhealthyEndpoints(t *testing.T) { + services := setupServices(t, 2, 2) + for _, algorithm := range []string{"random", "roundRobin", "powerOfRandomNChoices"} { + t.Run(algorithm, func(t *testing.T) { + mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s", "%s">`, + algorithm, services[0], services[1], services[2], services[3])) + + va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) + + count200, ok := va.CountStatus(200) + assert.True(t, ok) + + count504, ok := va.CountStatus(504) + assert.True(t, ok) + + failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) + t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) + + assert.InDelta(t, float64(count504), failedRequests, 5) + assert.InDelta(t, 0, float64(failedRequests), 0.3*float64(va.TotalRequests())) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, 2*float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(va.TotalRequests())) + }) + }) + } + + t.Run("consistentHash", func(t *testing.T) { + endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ + PassiveHealthCheckEnabled: true, + StatsResetPeriod: 1 * time.Second, + MinRequests: 1, // with 2 test case fails on github actions and -race + MaxHealthCheckDropProbability: 0.95, + MinHealthCheckDropProbability: 0.01, + }) + mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> `, + services[0], services[1], services[2], services[3]), endpointRegistry) + failedReqs := sendGetRequests(t, ps) + assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, 2*float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(nRequests)) + }) + }) + + t.Run("consistent hash with balance factor", func(t *testing.T) { + endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ + PassiveHealthCheckEnabled: true, + StatsResetPeriod: 1 * time.Second, + MinRequests: 1, // with 2 test case fails on github actions and -race + MaxHealthCheckDropProbability: 0.95, + MinHealthCheckDropProbability: 0.01, + }) + mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, + services[0], services[1], services[2], services[3]), endpointRegistry) + failedReqs := sendGetRequests(t, ps) + assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, 2*float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(nRequests)) + }) }) } From 26fefee131040a8865556599accc50c20a2c8ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 7 May 2024 18:34:29 +0200 Subject: [PATCH 0785/1047] doc: remove video tutorial as it was not followed up and is heavily outdated (#3068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- docs/tutorials/video-howto-build.md | 18 ------------------ mkdocs.yml | 1 - 2 files changed, 19 deletions(-) delete mode 100644 docs/tutorials/video-howto-build.md diff --git a/docs/tutorials/video-howto-build.md b/docs/tutorials/video-howto-build.md deleted file mode 100644 index e01097eecf..0000000000 --- a/docs/tutorials/video-howto-build.md +++ /dev/null @@ -1,18 +0,0 @@ -## How to build Skipper - -We expect you to have [Go](https://golang.org/dl) and [glide](https://github.com/Masterminds/glide) installed. - -### How to build Skipper without plugins - - - - -### How to build Skipper with plugins - -__TODO__ - -## How to run Skipper - -We expect you to have already built Skipper. - -__TODO__ diff --git a/mkdocs.yml b/mkdocs.yml index 3d9e519697..466135bc80 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,7 +11,6 @@ nav: - Plugins: reference/plugins.md - Architecture: reference/architecture.md - Development: reference/development.md - - Video - How to build: tutorials/video-howto-build.md - Data Clients: - Eskip File: data-clients/eskip-file.md - Remote Eskip: data-clients/eskip-remote.md From f97919ef06c7cfbac6a942cbac0d1599e0b633b7 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 8 May 2024 10:58:07 +0200 Subject: [PATCH 0786/1047] config: add yaml flag (#3069) YAML flag parses (preferably flow-style) YAML value from the command line or unmarshals object yaml value from the yaml config file. Example value: ``` bin/skipper -foo-flag='{foo: hello, bar: [world, "1"], baz: 2, qux: {baz: 3}}' ``` and equivalent branch in config yaml: ```yaml foo-flag: foo: hello bar: - world - "1" baz: 2 qux: baz: 3 ``` This will be useful for #2104 It is also a better alternative to manual parsing of micro-syntax like e.g. implemented in #2888 Signed-off-by: Alexander Yastrebov --- config/yamlflag.go | 42 ++++++++++++++++ config/yamlflag_test.go | 108 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 config/yamlflag.go create mode 100644 config/yamlflag_test.go diff --git a/config/yamlflag.go b/config/yamlflag.go new file mode 100644 index 0000000000..6d321e9e8b --- /dev/null +++ b/config/yamlflag.go @@ -0,0 +1,42 @@ +package config + +import ( + "fmt" + + "gopkg.in/yaml.v2" +) + +type yamlFlag[T any] struct { + Ptr **T + value string // only for Set +} + +func newYamlFlag[T any](ptr **T) *yamlFlag[T] { + return &yamlFlag[T]{Ptr: ptr} +} + +func (yf *yamlFlag[T]) Set(value string) error { + var opts T + if err := yaml.Unmarshal([]byte(value), &opts); err != nil { + return fmt.Errorf("failed to parse yaml: %w", err) + } + *yf.Ptr = &opts + yf.value = value + return nil +} + +func (yf *yamlFlag[T]) UnmarshalYAML(unmarshal func(interface{}) error) error { + var opts T + if err := unmarshal(&opts); err != nil { + return err + } + *yf.Ptr = &opts + return nil +} + +func (yf *yamlFlag[T]) String() string { + if yf == nil { + return "" + } + return yf.value +} diff --git a/config/yamlflag_test.go b/config/yamlflag_test.go new file mode 100644 index 0000000000..416af515d1 --- /dev/null +++ b/config/yamlflag_test.go @@ -0,0 +1,108 @@ +package config + +import ( + "testing" + + "gopkg.in/yaml.v2" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type yamlFlagTestConfig struct { + Foo string + Bar []string + Baz int + Qux *yamlFlagTestConfig +} + +func TestYamlFlag(t *testing.T) { + t.Run("set", func(t *testing.T) { + cfg := struct { + Field *yamlFlagTestConfig + }{} + + f := newYamlFlag(&cfg.Field) + v := `{foo: hello, bar: [world, "1"], baz: 2, qux: {baz: 3}}` + err := f.Set(v) + + require.NoError(t, err) + assert.Equal(t, "hello", cfg.Field.Foo) + assert.Equal(t, []string{"world", "1"}, cfg.Field.Bar) + assert.Equal(t, 2, cfg.Field.Baz) + assert.Equal(t, 3, cfg.Field.Qux.Baz) + + assert.Equal(t, v, f.String()) + }) + + t.Run("set empty", func(t *testing.T) { + cfg := struct { + Field *yamlFlagTestConfig + }{} + + f := newYamlFlag(&cfg.Field) + err := f.Set("") + + require.NoError(t, err) + assert.Equal(t, &yamlFlagTestConfig{}, cfg.Field) + assert.Equal(t, "", f.String()) + }) + + t.Run("set empty object", func(t *testing.T) { + cfg := struct { + Field *yamlFlagTestConfig + }{} + + f := newYamlFlag(&cfg.Field) + err := f.Set("{}") + + require.NoError(t, err) + assert.Equal(t, &yamlFlagTestConfig{}, cfg.Field) + assert.Equal(t, "{}", f.String()) + }) + + t.Run("set invalid yaml", func(t *testing.T) { + cfg := struct { + Field *yamlFlagTestConfig + }{} + + f := newYamlFlag(&cfg.Field) + v := `This is not a valid YAML` + err := f.Set(v) + + assert.Error(t, err) + }) + + t.Run("unmarshal YAML", func(t *testing.T) { + cfg := struct { + Field *yamlFlagTestConfig + }{} + + err := yaml.Unmarshal([]byte(` +field: + foo: hello + bar: + - world + - "1" + baz: 2 + qux: + baz: 3 +`), &cfg) + + require.NoError(t, err) + assert.Equal(t, "hello", cfg.Field.Foo) + assert.Equal(t, []string{"world", "1"}, cfg.Field.Bar) + assert.Equal(t, 2, cfg.Field.Baz) + assert.Equal(t, 3, cfg.Field.Qux.Baz) + }) + + t.Run("unmarshal invalid YAML", func(t *testing.T) { + cfg := struct { + Field *yamlFlagTestConfig + }{} + + err := yaml.Unmarshal([]byte(`This is not a valid YAML`), &cfg) + + assert.Error(t, err) + }) +} From b778f2baac071cb0a3c1ca15d1617cdf0ead48a7 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 8 May 2024 10:59:55 +0200 Subject: [PATCH 0787/1047] eskip: remove flowid dependency (#3065) eskip package is generally useful without proxy or filters and therefore ideally should have minimal number of dependencies. This change deprecates GenerateIfNeeded which is only used by etcd dataclient and reimplements it to remove dependency on github.com/zalando/skipper/filters/flowid See 35ba6cc76dee89a57cc7869e79824bd9fc56e750 Signed-off-by: Alexander Yastrebov --- eskip/eskip.go | 31 +++++++++++++++---------------- etcd/etcd.go | 1 + 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/eskip/eskip.go b/eskip/eskip.go index e68ab5d22c..229789b873 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -5,13 +5,13 @@ package eskip import ( "errors" "fmt" + "math/rand" "net/url" "regexp" "strings" "sync" log "github.com/sirupsen/logrus" - "github.com/zalando/skipper/filters/flowid" ) const duplicateHeaderPredicateErrorFmt = "duplicate header predicate: %s" @@ -712,29 +712,28 @@ func ParsePredicates(p string) ([]*Predicate, error) { return ps, nil } -const randomIdLength = 16 - -var routeIdRx = regexp.MustCompile(`\W`) +const ( + randomIdLength = 16 + // does not contain underscore to produce compatible output with previously used flow id generator + alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +) // generate weak random id for a route if // it doesn't have one. +// +// Deprecated: do not use, generate valid route id that matches [a-zA-Z_] yourself. func GenerateIfNeeded(existingId string) string { if existingId != "" { return existingId } - // using this to avoid adding a new dependency. - g, err := flowid.NewStandardGenerator(randomIdLength) - if err != nil { - return existingId - } - id, err := g.Generate() - if err != nil { - return existingId + var sb strings.Builder + sb.WriteString("route") + + for i := 0; i < randomIdLength; i++ { + ai := rand.Intn(len(alphabet)) + sb.WriteByte(alphabet[ai]) } - // replace characters that are not allowed - // for eskip route ids. - id = routeIdRx.ReplaceAllString(id, "x") - return "route" + id + return sb.String() } diff --git a/etcd/etcd.go b/etcd/etcd.go index 3dda1216ed..50843f103a 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -493,6 +493,7 @@ func (c *Client) Delete(id string) error { func (c *Client) UpsertAll(routes []*eskip.Route) error { for _, r := range routes { + //lint:ignore SA1019 due to backward compatibility r.Id = eskip.GenerateIfNeeded(r.Id) err := c.Upsert(r) if err != nil { From 0f1e776a846fc964cad20a9b3d8f7ea88aeb318d Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Wed, 8 May 2024 19:29:40 +0200 Subject: [PATCH 0788/1047] phc: add new metric to counter number of unique requests mitigated (#3059) Signed-off-by: Mustafa Abdelrahman --- docs/operation/operation.md | 1 + proxy/healthy_endpoints.go | 5 +++++ proxy/healthy_endpoints_test.go | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 2052387e9f..37c6211e1d 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -932,6 +932,7 @@ while choosing the endpoint for the given request A set of metrics will be exposed to track passive health check: * `passive-health-check.endpoints.dropped`: Number of all endpoints dropped before load balancing a request, so after N requests and M endpoints are being dropped this counter would be N*M. +* `passive-health-check.requests.passed`: Number of unique requests where PHC was able to avoid sending them to unhealthy endpoints. ## Memory consumption diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index 8e172993bd..00e1ef5db8 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -34,5 +34,10 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout if len(filtered) == 0 { return endpoints } + + if len(filtered) < len(endpoints) { + metrics.IncCounter("passive-health-check.requests.passed") + } + return filtered } diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index 1fa6694761..991792a0bc 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -197,6 +197,7 @@ func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { assert.InDelta(t, 0, float64(failedRequests), 0.1*float64(va.TotalRequests())) mockMetrics.WithCounters(func(counters map[string]int64) { assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(va.TotalRequests())) // allow 30% error + assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(va.TotalRequests())) // allow 30% error }) }) } @@ -215,6 +216,7 @@ func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) mockMetrics.WithCounters(func(counters map[string]int64) { assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error }) }) @@ -225,6 +227,7 @@ func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) mockMetrics.WithCounters(func(counters map[string]int64) { assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error }) }) } @@ -251,6 +254,7 @@ func TestPHCForMultipleHealthyAndMultipleUnhealthyEndpoints(t *testing.T) { assert.InDelta(t, 0, float64(failedRequests), 0.3*float64(va.TotalRequests())) mockMetrics.WithCounters(func(counters map[string]int64) { assert.InDelta(t, 2*float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(va.TotalRequests())) + assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(va.TotalRequests())) // allow 30% error }) }) } @@ -269,6 +273,7 @@ func TestPHCForMultipleHealthyAndMultipleUnhealthyEndpoints(t *testing.T) { assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) mockMetrics.WithCounters(func(counters map[string]int64) { assert.InDelta(t, 2*float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(nRequests)) + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error }) }) @@ -286,6 +291,7 @@ func TestPHCForMultipleHealthyAndMultipleUnhealthyEndpoints(t *testing.T) { assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) mockMetrics.WithCounters(func(counters map[string]int64) { assert.InDelta(t, 2*float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(nRequests)) + assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error }) }) } From d959cafc351be032bbf88e6b9a7360eeea4311bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 10:25:59 +0200 Subject: [PATCH 0789/1047] build(deps): bump coverallsapp/github-action from 2.2.3 to 2.3.0 (#3080) Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.2.3 to 2.3.0. - [Release notes](https://github.com/coverallsapp/github-action/releases) - [Commits](https://github.com/coverallsapp/github-action/compare/3dfc5567390f6fa9267c0ee9c251e4c8c3f18949...643bc377ffa44ace6394b2b5d0d3950076de9f63) --- updated-dependencies: - dependency-name: coverallsapp/github-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index cbd9fb8e3f..16d52981ee 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -31,7 +31,7 @@ jobs: - name: Convert coverage to lcov uses: jandelgado/gcov2lcov-action@c680c0f7c7442485f1749eb2a13e54a686e76eb5 - name: Coveralls - uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 + uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage.lcov From fe40e58f26eb9dcbf1471c7d515c8ee2e9a77f55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 10:26:29 +0200 Subject: [PATCH 0790/1047] build(deps): bump ossf/scorecard-action from 2.3.1 to 2.3.3 (#3079) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.3.1 to 2.3.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/0864cf19026789058feabb7e87baa5f140aac736...dc50aa9510b46c811795eb24b2f1ba02a914e534) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index bdaa8830bf..3e73abc4aa 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -39,7 +39,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 with: results_file: results.sarif results_format: sarif From a21e6eb72483de00aca4e0f2555488119bc3d75d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 10:26:53 +0200 Subject: [PATCH 0791/1047] build(deps): bump actions/checkout from 4.1.4 to 4.1.5 (#3078) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.4 to 4.1.5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/0ad4b8fadaa221de15dcec353f45205ec38ea70b...44c2b7a8a4ea60a981eaca3cf939b5f4305c123b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 06b6ee8d13..72d366a1fd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index ff37e82e13..09fe4521e3 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index c6f842f77d..31f3b46a2b 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 16d52981ee..e850235ab0 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 3e73abc4aa..bb253f9bf3 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v3.1.0 + uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 47cd6aa8ea..35c8ce17b3 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From c6e4abb20b80db97b9e0f745cc6d3cb9aa02cf54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 10:27:23 +0200 Subject: [PATCH 0792/1047] build(deps): bump github.com/prometheus/client_golang (#3077) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.19.0 to 1.19.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.19.0...v1.19.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index bb65ea411f..f1534f9f3e 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/client_golang v1.19.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/redis/go-redis/v9 v9.5.1 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 @@ -48,6 +48,7 @@ require ( github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab golang.org/x/crypto v0.22.0 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.24.0 golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.7.0 @@ -164,7 +165,6 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index 18775eb6e7..404000b046 100644 --- a/go.sum +++ b/go.sum @@ -369,8 +369,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= From de36c85ee198f638feef04e767c9bdf634019fce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 10:27:49 +0200 Subject: [PATCH 0793/1047] build(deps): bump google.golang.org/protobuf from 1.34.0 to 1.34.1 (#3076) Bumps google.golang.org/protobuf from 1.34.0 to 1.34.1. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f1534f9f3e..ae4dfe59a9 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( golang.org/x/sync v0.7.0 golang.org/x/term v0.20.0 golang.org/x/time v0.5.0 - google.golang.org/protobuf v1.34.0 + google.golang.org/protobuf v1.34.1 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf diff --git a/go.sum b/go.sum index 404000b046..f14920d2d4 100644 --- a/go.sum +++ b/go.sum @@ -670,8 +670,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From 9121becc607404f35f9d5e2dd8d3e3ae1d733765 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 11:09:09 +0200 Subject: [PATCH 0794/1047] build(deps): bump golang.org/x/net from 0.24.0 to 0.25.0 (#3074) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.24.0 to 0.25.0. - [Commits](https://github.com/golang/net/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index ae4dfe59a9..bb32a04ba0 100644 --- a/go.mod +++ b/go.mod @@ -47,9 +47,9 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.23.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.24.0 + golang.org/x/net v0.25.0 golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.7.0 golang.org/x/term v0.20.0 @@ -167,7 +167,7 @@ require ( go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.20.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect diff --git a/go.sum b/go.sum index f14920d2d4..7fcf31b89f 100644 --- a/go.sum +++ b/go.sum @@ -518,8 +518,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -557,8 +557,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -606,8 +606,8 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 00c33332ed059ccbe55a878e05035a1298f5b6dc Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Mon, 13 May 2024 14:49:12 +0200 Subject: [PATCH 0795/1047] phc: refactor testcases (#3071) Signed-off-by: Mustafa Abdelrahman --- proxy/healthy_endpoints_test.go | 242 +++++++++++++------------------- 1 file changed, 94 insertions(+), 148 deletions(-) diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index 991792a0bc..01cea436b0 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -6,6 +6,7 @@ import ( "math" "net/http" "net/http/httptest" + "strings" "testing" "time" @@ -81,7 +82,7 @@ func setupProxyWithCustomEndpointRegisty(t *testing.T, doc string, endpointRegis return m, ps } -func setupServices(t *testing.T, healthy, unhealthy int) []string { +func setupServices(t *testing.T, healthy, unhealthy int) string { services := []string{} for i := 0; i < healthy; i++ { service := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -98,16 +99,16 @@ func setupServices(t *testing.T, healthy, unhealthy int) []string { services = append(services, service.URL) t.Cleanup(service.Close) } - return services + return fmt.Sprint(`"`, strings.Join(services, `", "`), `"`) } func TestPHCWithoutRequests(t *testing.T) { - services := setupServices(t, 3, 0) + servicesString := setupServices(t, 3, 0) for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { t.Run(algorithm, func(t *testing.T) { - _, ps := setupProxy(t, fmt.Sprintf(`* -> <%s, "%s", "%s", "%s">`, - algorithm, services[0], services[1], services[2])) + _, ps := setupProxy(t, fmt.Sprintf(`* -> <%s, %s>`, + algorithm, servicesString)) rsp := sendGetRequest(t, ps, 0) assert.Equal(t, http.StatusOK, rsp.StatusCode) rsp.Body.Close() @@ -118,8 +119,8 @@ func TestPHCWithoutRequests(t *testing.T) { } t.Run("consistent hash with balance factor", func(t *testing.T) { - _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashBalanceFactor(1.25) -> `, - services[0], services[1], services[2])) + _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashBalanceFactor(1.25) -> `, + servicesString)) rsp := sendGetRequest(t, ps, 0) assert.Equal(t, http.StatusOK, rsp.StatusCode) rsp.Body.Close() @@ -130,10 +131,10 @@ func TestPHCWithoutRequests(t *testing.T) { } func TestPHCForSingleHealthyEndpoint(t *testing.T) { - service := setupServices(t, 1, 0)[0] + servicesString := setupServices(t, 1, 0) endpointRegistry := defaultEndpointRegistry() - doc := fmt.Sprintf(`* -> "%s"`, service) + doc := fmt.Sprintf(`* -> %s`, servicesString) tp, err := newTestProxyWithParams(doc, Params{ EnablePassiveHealthCheck: true, EndpointRegistry: endpointRegistry, @@ -150,148 +151,93 @@ func TestPHCForSingleHealthyEndpoint(t *testing.T) { assert.Equal(t, 0, failedReqs) } -func TestPHCForMultipleHealthyEndpoints(t *testing.T) { - services := setupServices(t, 3, 0) - - for _, algorithm := range []string{"random", "consistentHash", "roundRobin", "powerOfRandomNChoices"} { - t.Run(algorithm, func(t *testing.T) { - _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, - algorithm, services[0], services[1], services[2])) - va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) - count200, ok := va.CountStatus(200) - assert.True(t, ok) - assert.InDelta(t, count200, 15000, 50) // 3000*5s, the delta is for CI - assert.Equal(t, count200, int(va.TotalRequests())) - }) - } - - t.Run("consistent hash with balance factor", func(t *testing.T) { - _, ps := setupProxy(t, fmt.Sprintf(`* -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, - services[0], services[1], services[2])) - va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) - count200, ok := va.CountStatus(200) - assert.True(t, ok) - assert.Equal(t, count200, int(va.TotalRequests())) - }) -} - -func TestPHCForMultipleHealthyAndOneUnhealthyEndpoints(t *testing.T) { - services := setupServices(t, 2, 1) - for _, algorithm := range []string{"random", "roundRobin", "powerOfRandomNChoices"} { - t.Run(algorithm, func(t *testing.T) { - mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s">`, - algorithm, services[0], services[1], services[2])) - - va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) - - count200, ok := va.CountStatus(200) - assert.True(t, ok) - - count504, ok := va.CountStatus(504) - assert.True(t, ok) - - failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) - t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) - - assert.InDelta(t, float64(count504), failedRequests, 5) - assert.InDelta(t, 0, float64(failedRequests), 0.1*float64(va.TotalRequests())) - mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(va.TotalRequests())) // allow 30% error - assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(va.TotalRequests())) // allow 30% error +func TestPHC(t *testing.T) { + rpsFactor := 0.0 + for _, tt := range []struct { + name string + healthy, unhealthy int + }{ + {"single healthy", 1, 0}, + {"multiple healthy", 2, 0}, + {"multiple healthy and one unhealthy", 2, 1}, + {"multiple healthy and multiple unhealthy", 2, 2}, + } { + if (tt.healthy != 0 && tt.unhealthy == 0) || (tt.healthy == 0 && tt.unhealthy != 0) { + t.Logf("test %s has no mitigations", tt.name) + rpsFactor = 0.0 + } else { + t.Logf("test %s has mitigations", tt.name) + rpsFactor = 1.0 + } + t.Run(tt.name, func(t *testing.T) { + servicesString := setupServices(t, tt.healthy, tt.unhealthy) + for _, algorithm := range []string{"random", "roundRobin", "powerOfRandomNChoices"} { + t.Run(algorithm, func(t *testing.T) { + mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("20ms") -> <%s, %s>`, + algorithm, servicesString)) + va := fireVegeta(t, ps, 5000, 1*time.Second, 5*time.Second) + + count200, ok := va.CountStatus(200) + assert.True(t, ok) + + count504, ok := va.CountStatus(504) + assert.Condition(t, func() bool { + if tt.unhealthy == 0 && (ok || count504 == 0) { + return true + } else if tt.unhealthy > 0 && ok { + return true + } else { + return false + } + }) + + failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) + t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) + + assert.InDelta(t, float64(count504), failedRequests, 5) + assert.InDelta(t, 0, float64(failedRequests), 0.3*float64(va.TotalRequests())) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(tt.unhealthy)*float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(tt.unhealthy)*float64(va.TotalRequests())) + assert.InDelta(t, float64(va.TotalRequests())*rpsFactor, float64(counters["passive-health-check.requests.passed"]), 0.3*float64(va.TotalRequests())) // allow 30% error + }) + }) + } + + t.Run("consistentHash", func(t *testing.T) { + consistantHashCustomEndpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ + PassiveHealthCheckEnabled: true, + StatsResetPeriod: 1 * time.Second, + MinRequests: 1, // with 2 test case fails on github actions + MaxHealthCheckDropProbability: 0.95, + MinHealthCheckDropProbability: 0.01, + }) + mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> `, + servicesString), consistantHashCustomEndpointRegistry) + failedReqs := sendGetRequests(t, ps) + assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(tt.unhealthy*nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(tt.unhealthy)*float64(nRequests)) + assert.InDelta(t, float64(nRequests)*rpsFactor, float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error + }) }) - }) - } - t.Run("consistentHash", func(t *testing.T) { - endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ - PassiveHealthCheckEnabled: true, - StatsResetPeriod: 1 * time.Second, - MinRequests: 1, // with 2 test case fails on github actions - MaxHealthCheckDropProbability: 0.95, - MinHealthCheckDropProbability: 0.01, - }) - mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> `, - services[0], services[1], services[2]), endpointRegistry) - failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) - mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error - }) - }) - - t.Run("consistent hash with balance factor", func(t *testing.T) { - mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, - services[0], services[1], services[2])) - failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0, failedReqs, 0.1*float64(nRequests)) - mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(nRequests)) // allow 30% error - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error - }) - }) -} - -func TestPHCForMultipleHealthyAndMultipleUnhealthyEndpoints(t *testing.T) { - services := setupServices(t, 2, 2) - for _, algorithm := range []string{"random", "roundRobin", "powerOfRandomNChoices"} { - t.Run(algorithm, func(t *testing.T) { - mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> <%s, "%s", "%s", "%s", "%s">`, - algorithm, services[0], services[1], services[2], services[3])) - - va := fireVegeta(t, ps, 3000, 1*time.Second, 5*time.Second) - - count200, ok := va.CountStatus(200) - assert.True(t, ok) - - count504, ok := va.CountStatus(504) - assert.True(t, ok) - - failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) - t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) - - assert.InDelta(t, float64(count504), failedRequests, 5) - assert.InDelta(t, 0, float64(failedRequests), 0.3*float64(va.TotalRequests())) - mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, 2*float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(va.TotalRequests())) - assert.InDelta(t, float64(va.TotalRequests()), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(va.TotalRequests())) // allow 30% error + t.Run("consistent hash with balance factor", func(t *testing.T) { + consistantHashCustomEndpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ + PassiveHealthCheckEnabled: true, + StatsResetPeriod: 1 * time.Second, + MinRequests: 1, // with 2 test case fails on github actions + MaxHealthCheckDropProbability: 0.95, + MinHealthCheckDropProbability: 0.01, + }) + mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, + servicesString), consistantHashCustomEndpointRegistry) + failedReqs := sendGetRequests(t, ps) + assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(tt.unhealthy*nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(tt.unhealthy)*float64(nRequests)) + assert.InDelta(t, float64(nRequests)*rpsFactor, float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error + }) }) }) } - - t.Run("consistentHash", func(t *testing.T) { - endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ - PassiveHealthCheckEnabled: true, - StatsResetPeriod: 1 * time.Second, - MinRequests: 1, // with 2 test case fails on github actions and -race - MaxHealthCheckDropProbability: 0.95, - MinHealthCheckDropProbability: 0.01, - }) - mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> `, - services[0], services[1], services[2], services[3]), endpointRegistry) - failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) - mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, 2*float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(nRequests)) - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error - }) - }) - - t.Run("consistent hash with balance factor", func(t *testing.T) { - endpointRegistry := routing.NewEndpointRegistry(routing.RegistryOptions{ - PassiveHealthCheckEnabled: true, - StatsResetPeriod: 1 * time.Second, - MinRequests: 1, // with 2 test case fails on github actions and -race - MaxHealthCheckDropProbability: 0.95, - MinHealthCheckDropProbability: 0.01, - }) - mockMetrics, ps := setupProxyWithCustomEndpointRegisty(t, fmt.Sprintf(`* -> backendTimeout("5ms") -> consistentHashKey("${request.header.ConsistentHashKey}") -> consistentHashBalanceFactor(1.25) -> `, - services[0], services[1], services[2], services[3]), endpointRegistry) - failedReqs := sendGetRequests(t, ps) - assert.InDelta(t, 0, failedReqs, 0.2*float64(nRequests)) - mockMetrics.WithCounters(func(counters map[string]int64) { - assert.InDelta(t, 2*float64(nRequests), float64(counters["passive-health-check.endpoints.dropped"]), 0.6*float64(nRequests)) - assert.InDelta(t, float64(nRequests), float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error - }) - }) } From 260041525467aaf83e741554d59f73824f6e9590 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 18:57:05 +0200 Subject: [PATCH 0796/1047] build(deps): bump github.com/testcontainers/testcontainers-go (#3075) Bumps [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) from 0.30.0 to 0.31.0. - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bb32a04ba0..d5dfe3d739 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/sony/gobreaker v1.0.0 github.com/stretchr/testify v1.9.0 github.com/szuecs/rate-limit-buffer v0.9.0 - github.com/testcontainers/testcontainers-go v0.30.0 + github.com/testcontainers/testcontainers-go v0.31.0 github.com/tidwall/gjson v1.17.1 github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible diff --git a/go.sum b/go.sum index 7fcf31b89f..570cefb2ed 100644 --- a/go.sum +++ b/go.sum @@ -447,8 +447,8 @@ github.com/szuecs/rate-limit-buffer v0.9.0 h1:65fBCVsaJFh0E1G5C6/sInEPlYR6dXtF9J github.com/szuecs/rate-limit-buffer v0.9.0/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/testcontainers/testcontainers-go v0.30.0 h1:jmn/XS22q4YRrcMwWg0pAwlClzs/abopbsBzrepyc4E= -github.com/testcontainers/testcontainers-go v0.30.0/go.mod h1:K+kHNGiM5zjklKjgTtcrEetF3uhWbMUyqAQoyoh8Pf0= +github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U= +github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= From c804aeafbad7f49a1e9a80d9d7196e9e285ca6cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 10:32:32 +0200 Subject: [PATCH 0797/1047] build(deps): bump actions/checkout from 4.1.5 to 4.1.6 (#3082) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.5 to 4.1.6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/44c2b7a8a4ea60a981eaca3cf939b5f4305c123b...a5ac7e51b41094c92402da3b24376905380afc29) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 72d366a1fd..b30453a138 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 09fe4521e3..a46fef7c01 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 31f3b46a2b..e273b20b19 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index e850235ab0..a97cf11531 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index bb253f9bf3..17388ad6df 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v3.1.0 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 35c8ce17b3..fbc054364e 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From 0964a48f038fa306b244bda8a99eaf59991a0b06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 11:16:40 +0200 Subject: [PATCH 0798/1047] build(deps): bump alpine from `c5b1261` to `77726ef` in /packaging (#3083) Bumps alpine from `c5b1261` to `77726ef`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index c0ca0c3ae9..9427671362 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b +FROM --platform=linux/arm64 alpine@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index 7f51812df0..1a58f37642 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b +FROM --platform=linux/arm/v7 alpine@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 07de3081af50d1f330a7f383f5af5407762d97f0 Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Thu, 23 May 2024 13:46:55 +0200 Subject: [PATCH 0799/1047] Added max-unhealthy-endpoints-ratio cmdline parameter for PHC (#3081) Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- docs/operation/operation.md | 12 +- proxy/healthy_endpoints.go | 12 +- proxy/healthy_endpoints_test.go | 102 ++++++++++++++++- proxy/proxy.go | 23 +++- proxy/proxy_test.go | 197 ++++++++++++++++++++++---------- 5 files changed, 270 insertions(+), 76 deletions(-) diff --git a/docs/operation/operation.md b/docs/operation/operation.md index 37c6211e1d..c7ee3e9c5a 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -904,15 +904,20 @@ the Skipper takes a look at previous period and if the amount of requests in the and failed requests ratio is more than `min-drop-probability` for the given endpoints then Skipper will send reduced (the more `max-drop-probability` and failed requests ratio in previous period are, the stronger reduction is) amount of requests compared to amount sent without PHC. +If the ratio of unhealthy endpoints is more than `max-unhealthy-endpoints-ratio` then PHC becomes fail-open. This effectively means +if there are too many unhealthy endpoints PHC does not try to mitigate them any more and requests are sent like there is no PHC at all. Having this, we expect less requests to fail because a lot of them would be sent to endpoints that seem to be healthy instead. To enable this feature, you need to provide `-passive-health-check` option having forementioned parameters -(`period`, `min-requests`, `min-drop-probability`, `max-drop-probability`) defined. +(`period`, `min-requests`, `min-drop-probability`, `max-drop-probability`, `max-unhealthy-endpoints-ratio`) defined. `period`, `min-requests`, `max-drop-probability` are required parameters, it is not possible for PHC to be enabled without them explicitly defined by user. `min-drop-probability` is implicitly defined as `0.0` if not explicitly set by user. +`max-unhealthy-endpoints-ratio` is defined as `1.0` if not explicitly set by user. Valid examples of `-passive-health-check` are: ++ `-passive-health-check=period=1s,min-requests=10,min-drop-probability=0.05,max-drop-probability=0.9,max-unhealthy-endpoints-ratio=0.3` ++ `-passive-health-check=period=1s,min-requests=10,max-drop-probability=0.9,max-unhealthy-endpoints-ratio=0.3` + `-passive-health-check=period=1s,min-requests=10,min-drop-probability=0.05,max-drop-probability=0.9` + `-passive-health-check=period=1s,min-requests=10,max-drop-probability=0.9` @@ -923,9 +928,10 @@ The parameters of `-passive-health-check` option are: + `period=` - the duration of stats reset period + `min-requests=` - the minimum number of requests per `period` per backend endpoint required to activate PHC for this endpoint -+ `min-drop-probabilty=<0.0 <= p <= 1.0>` - the minimum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request. The same value is in fact used as minimal failed requests ratio for PHC to be enabled for this endpoint -+ `max-drop-probabilty=<0.0 <= p <= 1.0>` - the maximum possible probability of unhealthy endpoint being not considered ++ `min-drop-probabilty=[0.0 <= p < max-drop-probability)` - the minimum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request. The same value is in fact used as minimal failed requests ratio for PHC to be enabled for this endpoint ++ `max-drop-probabilty=(min-drop-probability < p <= 1.0]` - the maximum possible probability of unhealthy endpoint being not considered while choosing the endpoint for the given request ++ `max-unhealthy-endpoints-ratio=[0.0 <= r <= 1.0]` - the maximum ratio of unhealthy endpoints for PHC to try to mitigate ongoing requests ### Metrics diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index 00e1ef5db8..d4818065d6 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -8,8 +8,9 @@ import ( ) type healthyEndpoints struct { - rnd *rand.Rand - endpointRegistry *routing.EndpointRegistry + rnd *rand.Rand + endpointRegistry *routing.EndpointRegistry + maxUnhealthyEndpointsRatio float64 } func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []routing.LBEndpoint, metrics metrics.Metrics) []routing.LBEndpoint { @@ -19,6 +20,8 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout p := h.rnd.Float64() + unhealthyEndpointsCount := 0 + maxUnhealthyEndpointsCount := float64(len(endpoints)) * h.maxUnhealthyEndpointsRatio filtered := make([]routing.LBEndpoint, 0, len(endpoints)) for _, e := range endpoints { dropProbability := e.Metrics.HealthCheckDropProbability() @@ -26,9 +29,14 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout ctx.Logger().Infof("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", e.Host, p, dropProbability) metrics.IncCounter("passive-health-check.endpoints.dropped") + unhealthyEndpointsCount++ } else { filtered = append(filtered, e) } + + if float64(unhealthyEndpointsCount) > maxUnhealthyEndpointsCount { + return endpoints + } } if len(filtered) == 0 { diff --git a/proxy/healthy_endpoints_test.go b/proxy/healthy_endpoints_test.go index 01cea436b0..0e75e65cda 100644 --- a/proxy/healthy_endpoints_test.go +++ b/proxy/healthy_endpoints_test.go @@ -61,17 +61,36 @@ func fireVegeta(t *testing.T, ps *httptest.Server, freq int, per time.Duration, } func setupProxy(t *testing.T, doc string) (*metricstest.MockMetrics, *httptest.Server) { - return setupProxyWithCustomEndpointRegisty(t, doc, defaultEndpointRegistry()) + m := &metricstest.MockMetrics{} + endpointRegistry := defaultEndpointRegistry() + proxyParams := Params{ + EnablePassiveHealthCheck: true, + EndpointRegistry: endpointRegistry, + Metrics: m, + PassiveHealthCheck: &PassiveHealthCheck{ + MaxUnhealthyEndpointsRatio: 1.0, + }, + } + + return m, setupProxyWithCustomProxyParams(t, doc, proxyParams) } func setupProxyWithCustomEndpointRegisty(t *testing.T, doc string, endpointRegistry *routing.EndpointRegistry) (*metricstest.MockMetrics, *httptest.Server) { m := &metricstest.MockMetrics{} - - tp, err := newTestProxyWithParams(doc, Params{ + proxyParams := Params{ EnablePassiveHealthCheck: true, EndpointRegistry: endpointRegistry, Metrics: m, - }) + PassiveHealthCheck: &PassiveHealthCheck{ + MaxUnhealthyEndpointsRatio: 1.0, + }, + } + + return m, setupProxyWithCustomProxyParams(t, doc, proxyParams) +} + +func setupProxyWithCustomProxyParams(t *testing.T, doc string, proxyParams Params) *httptest.Server { + tp, err := newTestProxyWithParams(doc, proxyParams) require.NoError(t, err) ps := httptest.NewServer(tp.proxy) @@ -79,7 +98,7 @@ func setupProxyWithCustomEndpointRegisty(t *testing.T, doc string, endpointRegis t.Cleanup(tp.close) t.Cleanup(ps.Close) - return m, ps + return ps } func setupServices(t *testing.T, healthy, unhealthy int) string { @@ -138,6 +157,9 @@ func TestPHCForSingleHealthyEndpoint(t *testing.T) { tp, err := newTestProxyWithParams(doc, Params{ EnablePassiveHealthCheck: true, EndpointRegistry: endpointRegistry, + PassiveHealthCheck: &PassiveHealthCheck{ + MaxUnhealthyEndpointsRatio: 1.0, + }, }) if err != nil { t.Fatal(err) @@ -241,3 +263,73 @@ func TestPHC(t *testing.T) { }) } } + +func TestPHCNoHealthyEndpoints(t *testing.T) { + const ( + healthy = 0 + unhealthy = 4 + ) + + servicesString := setupServices(t, healthy, unhealthy) + mockMetrics, ps := setupProxy(t, fmt.Sprintf(`* -> backendTimeout("20ms") -> `, + servicesString)) + va := fireVegeta(t, ps, 5000, 1*time.Second, 5*time.Second) + + count200, ok := va.CountStatus(200) + assert.False(t, ok) + + count504, ok := va.CountStatus(504) + assert.True(t, ok) + + failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) + t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) + + assert.InDelta(t, float64(count504), failedRequests, 5) + assert.InDelta(t, float64(va.TotalRequests()), float64(failedRequests), 0.1*float64(va.TotalRequests())) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, float64(unhealthy)*float64(va.TotalRequests()), float64(counters["passive-health-check.endpoints.dropped"]), 0.3*float64(unhealthy)*float64(va.TotalRequests())) + assert.InDelta(t, 0.0, float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error + }) +} + +func TestPHCMaxUnhealthyEndpointsRatioParam(t *testing.T) { + const ( + healthy = 0 + unhealthy = 4 + maxUnhealthyEndpointsRatio = 0.49 // slightly less than 0.5 to avoid equality and looking up the third endpoint + ) + + servicesString := setupServices(t, healthy, unhealthy) + mockMetrics := &metricstest.MockMetrics{} + endpointRegistry := defaultEndpointRegistry() + proxyParams := Params{ + EnablePassiveHealthCheck: true, + EndpointRegistry: endpointRegistry, + Metrics: mockMetrics, + PassiveHealthCheck: &PassiveHealthCheck{ + MaxUnhealthyEndpointsRatio: maxUnhealthyEndpointsRatio, + }, + } + ps := setupProxyWithCustomProxyParams(t, fmt.Sprintf(`* -> backendTimeout("20ms") -> `, + servicesString), proxyParams) + va := fireVegeta(t, ps, 5000, 1*time.Second, 5*time.Second) + + count200, ok := va.CountStatus(200) + assert.False(t, ok) + + count504, ok := va.CountStatus(504) + assert.True(t, ok) + + failedRequests := math.Abs(float64(va.TotalRequests())) - float64(count200) + t.Logf("total requests: %d, count200: %d, count504: %d, failedRequests: %f", va.TotalRequests(), count200, count504, failedRequests) + + assert.InDelta(t, float64(count504), failedRequests, 5) + assert.InDelta(t, float64(va.TotalRequests()), float64(failedRequests), 0.1*float64(va.TotalRequests())) + mockMetrics.WithCounters(func(counters map[string]int64) { + assert.InDelta(t, maxUnhealthyEndpointsRatio*float64(unhealthy)*float64(va.TotalRequests()), + float64(counters["passive-health-check.endpoints.dropped"]), + 0.3*maxUnhealthyEndpointsRatio*float64(unhealthy)*float64(va.TotalRequests()), + ) + assert.InDelta(t, 0.0, float64(counters["passive-health-check.requests.passed"]), 0.3*float64(nRequests)) // allow 30% error + }) +} diff --git a/proxy/proxy.go b/proxy/proxy.go index cc5061954a..5553a2fdd5 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -162,6 +162,10 @@ type PassiveHealthCheck struct { // The maximum probability of unhealthy endpoint to be dropped out from load balancing for every specific request MaxDropProbability float64 + + // MaxUnhealthyEndpointsRatio is the maximum ratio of unhealthy endpoints in the list of all endpoints PHC will check + // in case of all endpoints are unhealthy + MaxUnhealthyEndpointsRatio float64 } func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, error) { @@ -169,7 +173,10 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e return false, &PassiveHealthCheck{}, nil } - result := &PassiveHealthCheck{} + result := &PassiveHealthCheck{ + MinDropProbability: 0.0, + MaxUnhealthyEndpointsRatio: 1.0, + } requiredParams := map[string]struct{}{ "period": {}, "max-drop-probability": {}, @@ -218,6 +225,15 @@ func InitPassiveHealthChecker(o map[string]string) (bool, *PassiveHealthCheck, e return false, nil, fmt.Errorf("passive health check: invalid minDropProbability value: %s", value) } result.MinDropProbability = minDropProbability + case "max-unhealthy-endpoints-ratio": + maxUnhealthyEndpointsRatio, err := strconv.ParseFloat(value, 64) + if err != nil { + return false, nil, fmt.Errorf("passive health check: invalid maxUnhealthyEndpointsRatio value: %q", value) + } + if maxUnhealthyEndpointsRatio < 0 || maxUnhealthyEndpointsRatio > 1 { + return false, nil, fmt.Errorf("passive health check: invalid maxUnhealthyEndpointsRatio value: %q", value) + } + result.MaxUnhealthyEndpointsRatio = maxUnhealthyEndpointsRatio default: return false, nil, fmt.Errorf("passive health check: invalid parameter: key=%s,value=%s", key, value) } @@ -824,8 +840,9 @@ func WithParams(p Params) *Proxy { var healthyEndpointsChooser *healthyEndpoints if p.EnablePassiveHealthCheck { healthyEndpointsChooser = &healthyEndpoints{ - rnd: rand.New(loadbalancer.NewLockedSource()), - endpointRegistry: p.EndpointRegistry, + rnd: rand.New(loadbalancer.NewLockedSource()), + endpointRegistry: p.EndpointRegistry, + maxUnhealthyEndpointsRatio: p.PassiveHealthCheck.MaxUnhealthyEndpointsRatio, } } return &Proxy{ diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 51780a4f5d..b9c080b188 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -2296,10 +2296,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "somethingInvalid", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "0.05", + "period": "somethingInvalid", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2307,26 +2308,29 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "0.05", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: true, expectedParams: &PassiveHealthCheck{ - Period: 1 * time.Minute, - MinRequests: 10, - MaxDropProbability: 0.9, - MinDropProbability: 0.05, + Period: 1 * time.Minute, + MinRequests: 10, + MaxDropProbability: 0.9, + MinDropProbability: 0.05, + MaxUnhealthyEndpointsRatio: 0.3, }, expectedError: nil, }, { inputArg: map[string]string{ - "period": "-1m", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "0.05", + "period": "-1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2334,10 +2338,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "somethingInvalid", - "max-drop-probability": "0.9", - "min-drop-probability": "0.05", + "period": "1m", + "min-requests": "somethingInvalid", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2345,10 +2350,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "-10", - "max-drop-probability": "0.9", - "min-drop-probability": "0.05", + "period": "1m", + "min-requests": "-10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2356,10 +2362,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "somethingInvalid", - "min-drop-probability": "0.05", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "somethingInvalid", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2367,10 +2374,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "-0.1", - "min-drop-probability": "0.05", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "-0.1", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2378,10 +2386,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "3.1415", - "min-drop-probability": "0.05", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "3.1415", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2389,10 +2398,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "somethingInvalid", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "somethingInvalid", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2400,10 +2410,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "-0.1", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "-0.1", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2411,10 +2422,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "3.1415", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "3.1415", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2422,10 +2434,11 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "0.05", - "min-drop-probability": "0.9", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.05", + "min-drop-probability": "0.9", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2433,11 +2446,48 @@ func TestInitPassiveHealthChecker(t *testing.T) { }, { inputArg: map[string]string{ - "period": "1m", - "min-requests": "10", - "max-drop-probability": "0.9", - "min-drop-probability": "0.05", - "non-existing": "non-existing", + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "-0.1", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf(`passive health check: invalid maxUnhealthyEndpointsRatio value: "-0.1"`), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "3.1415", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf(`passive health check: invalid maxUnhealthyEndpointsRatio value: "3.1415"`), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "somethingInvalid", + }, + expectedEnabled: false, + expectedParams: nil, + expectedError: fmt.Errorf(`passive health check: invalid maxUnhealthyEndpointsRatio value: "somethingInvalid"`), + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", + "non-existing": "non-existing", }, expectedEnabled: false, expectedParams: nil, @@ -2448,7 +2498,8 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", /* forgot max-drop-probability */ - "min-drop-probability": "0.05", + "min-drop-probability": "0.05", + "max-unhealthy-endpoints-ratio": "0.3", }, expectedEnabled: false, expectedParams: nil, @@ -2459,14 +2510,34 @@ func TestInitPassiveHealthChecker(t *testing.T) { "period": "1m", "min-requests": "10", "max-drop-probability": "0.9", - /* using default max-drop-probability */ + /* using default min-drop-probability */ + "max-unhealthy-endpoints-ratio": "0.3", + }, + expectedEnabled: true, + expectedParams: &PassiveHealthCheck{ + Period: 1 * time.Minute, + MinRequests: 10, + MaxDropProbability: 0.9, + MinDropProbability: 0.0, + MaxUnhealthyEndpointsRatio: 0.3, + }, + expectedError: nil, + }, + { + inputArg: map[string]string{ + "period": "1m", + "min-requests": "10", + "max-drop-probability": "0.9", + "min-drop-probability": "0.05", + /* using default max-unhealthy-endpoints-ratio */ }, expectedEnabled: true, expectedParams: &PassiveHealthCheck{ - Period: 1 * time.Minute, - MinRequests: 10, - MaxDropProbability: 0.9, - MinDropProbability: 0.0, + Period: 1 * time.Minute, + MinRequests: 10, + MaxDropProbability: 0.9, + MinDropProbability: 0.05, + MaxUnhealthyEndpointsRatio: 1.0, }, expectedError: nil, }, From 34b188cab70fe01bed88b6a9a16db7a34559a70d Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 29 May 2024 20:05:05 +0200 Subject: [PATCH 0800/1047] filters/auth: add tracing tag for jwtMetrics (#3085) Add tracing tag for requests with problematic JWT tokens. Signed-off-by: Alexander Yastrebov --- filters/auth/jwt_metrics.go | 22 ++++++++++++------- filters/auth/jwt_metrics_test.go | 36 +++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/filters/auth/jwt_metrics.go b/filters/auth/jwt_metrics.go index b4e92cddd9..d0db481cf9 100644 --- a/filters/auth/jwt_metrics.go +++ b/filters/auth/jwt_metrics.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/ghodss/yaml" + "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/annotate" "github.com/zalando/skipper/jwt" @@ -75,33 +76,40 @@ func (f *jwtMetricsFilter) Response(ctx filters.FilterContext) { request := ctx.Request() - metrics := ctx.Metrics() - metricsPrefix := fmt.Sprintf("%s.%s.%d.", request.Method, escapeMetricKeySegment(request.Host), response.StatusCode) + count := func(metric string) { + prefix := fmt.Sprintf("%s.%s.%d.", request.Method, escapeMetricKeySegment(request.Host), response.StatusCode) + + ctx.Metrics().IncCounter(prefix + metric) + + if span := opentracing.SpanFromContext(ctx.Request().Context()); span != nil { + span.SetTag("jwt", metric) + } + } ahead := request.Header.Get("Authorization") if ahead == "" { - metrics.IncCounter(metricsPrefix + "missing-token") + count("missing-token") return } tv := strings.TrimPrefix(ahead, "Bearer ") if tv == ahead { - metrics.IncCounter(metricsPrefix + "invalid-token-type") + count("invalid-token-type") return } if len(f.Issuers) > 0 { token, err := jwt.Parse(tv) if err != nil { - metrics.IncCounter(metricsPrefix + "invalid-token") + count("invalid-token") return } // https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1 if issuer, ok := token.Claims["iss"].(string); !ok { - metrics.IncCounter(metricsPrefix + "missing-issuer") + count("missing-issuer") } else if !slices.Contains(f.Issuers, issuer) { - metrics.IncCounter(metricsPrefix + "invalid-issuer") + count("invalid-issuer") } } } diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index 373ec9a01f..e31425fb9a 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -8,7 +8,9 @@ import ( "net/http/httptest" "net/url" "testing" + "time" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zalando/skipper/eskip" @@ -31,11 +33,12 @@ func TestJwtMetrics(t *testing.T) { defer testAuthServer.Close() for _, tc := range []struct { - name string - filters string - request *http.Request - status int - expected map[string]int64 + name string + filters string + request *http.Request + status int + expected map[string]int64 + expectedTag string }{ { name: "ignores 401 response", @@ -66,6 +69,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, }, + expectedTag: "missing-token", }, { name: "invalid-token-type", @@ -77,6 +81,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.invalid-token-type": 1, }, + expectedTag: "invalid-token-type", }, { name: "invalid-token", @@ -88,6 +93,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.invalid-token": 1, }, + expectedTag: "invalid-token", }, { name: "missing-issuer", @@ -101,6 +107,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.missing-issuer": 1, }, + expectedTag: "missing-issuer", }, { name: "invalid-issuer", @@ -114,6 +121,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.invalid-issuer": 1, }, + expectedTag: "invalid-issuer", }, { name: "no invalid-issuer for empty issuers", @@ -156,6 +164,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, }, + expectedTag: "missing-token", }, { name: "no metrics when opted-out by annotation", @@ -188,6 +197,7 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.missing-token": 1, }, + expectedTag: "missing-token", }, { name: "no metrics when opted-out by state bag", @@ -220,11 +230,13 @@ func TestJwtMetrics(t *testing.T) { expected: map[string]int64{ "jwtMetrics.custom.GET.foo_test.200.invalid-token": 1, }, + expectedTag: "invalid-token", }, } { t.Run(tc.name, func(t *testing.T) { m := &metricstest.MockMetrics{} defer m.Close() + tracer := mocktracer.New() fr := builtin.MakeRegistry() fr.Register(auth.NewJwtMetrics()) @@ -236,6 +248,10 @@ func TestJwtMetrics(t *testing.T) { Routes: eskip.MustParse(fmt.Sprintf(`* -> %s -> status(%d) -> `, tc.filters, tc.status)), ProxyParams: proxy.Params{ Metrics: m, + OpenTracing: &proxy.OpenTracingParams{ + Tracer: tracer, + DisableFilterSpans: true, + }, }, }.Create() defer p.Close() @@ -249,6 +265,16 @@ func TestJwtMetrics(t *testing.T) { resp.Body.Close() require.Equal(t, tc.status, resp.StatusCode) + // wait for the span to be finished + require.Eventually(t, func() bool { return len(tracer.FinishedSpans()) == 1 }, time.Second, 100*time.Millisecond) + + span := tracer.FinishedSpans()[0] + if tc.expectedTag == "" { + assert.Nil(t, span.Tag("jwt")) + } else { + assert.Equal(t, tc.expectedTag, span.Tag("jwt")) + } + m.WithCounters(func(counters map[string]int64) { // add incoming counter to simplify comparison tc.expected["incoming.HTTP/1.1"] = 1 From 8c3c2412d0702272520930ec09a5068397ef0b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 30 May 2024 14:24:36 +0200 Subject: [PATCH 0801/1047] doc: fix supported service types (#3088) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- docs/kubernetes/ingress-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kubernetes/ingress-usage.md b/docs/kubernetes/ingress-usage.md index e21fc33193..1b35e3cdae 100644 --- a/docs/kubernetes/ingress-usage.md +++ b/docs/kubernetes/ingress-usage.md @@ -35,7 +35,7 @@ Service type | supported | workaround --- | --- | --- ClusterIP | yes | --- NodePort | yes | --- -ExternalName | no, [related issue](https://github.com/zalando/skipper/issues/549) | [use deployment with routestring](../data-clients/route-string.md#proxy-to-a-given-url) +ExternalName | yes | --- LoadBalancer | no | it should not, because Kubernetes cloud-controller-manager will maintain it From ff7d8a06bc34a53623e1c0127ddc4d6f96c692a5 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 31 May 2024 12:50:18 +0200 Subject: [PATCH 0802/1047] config: support multiple default filters flag values (#3084) This is useful for templated configurations that conditionally add various default filters. Signed-off-by: Alexander Yastrebov --- config/defaultfilterflags.go | 27 +++-- config/defaultfilterflags_test.go | 165 +++++++++++++++++++++--------- 2 files changed, 138 insertions(+), 54 deletions(-) diff --git a/config/defaultfilterflags.go b/config/defaultfilterflags.go index 6be5ea6d28..162c92f6f7 100644 --- a/config/defaultfilterflags.go +++ b/config/defaultfilterflags.go @@ -2,16 +2,18 @@ package config import ( "fmt" + "strings" "github.com/zalando/skipper/eskip" ) type defaultFiltersFlags struct { + values []string filters []*eskip.Filter } func (dpf defaultFiltersFlags) String() string { - return "" + return strings.Join(dpf.values, " -> ") } func (dpf *defaultFiltersFlags) Set(value string) error { @@ -20,14 +22,27 @@ func (dpf *defaultFiltersFlags) Set(value string) error { return fmt.Errorf("failed to parse default filters: %w", err) } - dpf.filters = fs + dpf.values = append(dpf.values, value) + dpf.filters = append(dpf.filters, fs...) + return nil } func (dpf *defaultFiltersFlags) UnmarshalYAML(unmarshal func(interface{}) error) error { - var value string - if err := unmarshal(&value); err != nil { - return err + values := make([]string, 1) + if err := unmarshal(&values); err != nil { + // Try to unmarshal as string for backwards compatibility. + // UnmarshalYAML allows calling unmarshal more than once. + if err := unmarshal(&values[0]); err != nil { + return err + } + } + dpf.values = nil + dpf.filters = nil + for _, v := range values { + if err := dpf.Set(v); err != nil { + return err + } } - return dpf.Set(value) + return nil } diff --git a/config/defaultfilterflags_test.go b/config/defaultfilterflags_test.go index 716bdbf8b1..f1bc55004a 100644 --- a/config/defaultfilterflags_test.go +++ b/config/defaultfilterflags_test.go @@ -5,85 +5,154 @@ import ( "gopkg.in/yaml.v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/zalando/skipper/eskip" ) -func Test_defaultFiltersFlags_String(t *testing.T) { +func TestDefaultFiltersFlagsSet(t *testing.T) { + oneFilter := eskip.MustParseFilters(`tee("https://www.zalando.de/")`) + manyFilters := eskip.MustParseFilters(`ratelimit(5, "10s") -> tee("https://www.zalando.de/")`) + manyMoreFilters := eskip.MustParseFilters(`ratelimit(5, "10s") -> tee("https://www.zalando.de/") -> inlineContent("OK\n")`) + tests := []struct { - name string - filters []*eskip.Filter - want string + name string + args []string + yaml string + want []*eskip.Filter + wantString string }{ { - name: "test string", - filters: []*eskip.Filter{}, - want: "", + name: "test no filter", + args: nil, + want: nil, + wantString: "", + }, + { + name: "test empty filter", + args: []string{""}, + yaml: "", + want: nil, + wantString: "", + }, + { + name: "test one filter", + args: []string{`tee("https://www.zalando.de/")`}, + yaml: `field: tee("https://www.zalando.de/")`, + want: oneFilter, + wantString: `tee("https://www.zalando.de/")`, + }, + { + name: "test many filters in one value", + args: []string{`ratelimit(5, "10s") -> tee("https://www.zalando.de/")`}, + yaml: `field: ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, + want: manyFilters, + wantString: `ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, + }, + { + name: "test multiple values", + args: []string{`ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, `inlineContent("OK\n")`}, + yaml: ` +field: + - ratelimit(5, "10s") -> tee("https://www.zalando.de/") + - inlineContent("OK\n") +`, + want: manyMoreFilters, + wantString: `ratelimit(5, "10s") -> tee("https://www.zalando.de/") -> inlineContent("OK\n")`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - dpf := &defaultFiltersFlags{ - filters: tt.filters, + cfg := struct { + Field *defaultFiltersFlags + }{ + Field: &defaultFiltersFlags{}, } - if got := dpf.String(); got != tt.want { - t.Errorf("defaultFiltersFlags.String() = %v, want %v", got, tt.want) + + var err error + for _, arg := range tt.args { + err = cfg.Field.Set(arg) + if err != nil { + break + } + } + require.NoError(t, err) + + assert.Equal(t, tt.wantString, cfg.Field.String()) + assert.Equal(t, tt.want, cfg.Field.filters) + + if tt.yaml != "" { + err := yaml.Unmarshal([]byte(tt.yaml), &cfg) + require.NoError(t, err) + + assert.Equal(t, tt.want, cfg.Field.filters) } }) } } -func Test_defaultFiltersFlags_Set(t *testing.T) { - oneFilter, _ := eskip.ParseFilters(`tee("https://www.zalando.de/")`) - manyFilters, _ := eskip.ParseFilters(`ratelimit(5, "10s") -> tee("https://www.zalando.de/")`) +func TestDefaultFiltersFlagsInvalidFlag(t *testing.T) { tests := []struct { - name string - args string - want []*eskip.Filter - wantErr bool + name string + args []string }{ { - name: "test no filter", - args: "", - want: nil, - wantErr: false, + name: "test single filter", + args: []string{"invalid-filter"}, }, { - name: "test error filter", - args: "invalid-filter", - wantErr: true, + name: "test multiple filters", + args: []string{`status(204)`, "invalid-filter"}, }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := struct { + Field *defaultFiltersFlags + }{ + Field: &defaultFiltersFlags{}, + } + + var err error + for _, arg := range tt.args { + err = cfg.Field.Set(arg) + if err != nil { + break + } + } + assert.Error(t, err) + }) + } +} + +func TestDefaultFiltersFlagsInvalidYaml(t *testing.T) { + tests := []struct { + name string + yaml string + }{ { - name: "test one filter", - args: `tee("https://www.zalando.de/")`, - want: oneFilter, - wantErr: false, + name: "test one value", + yaml: `field: invalid-filter`, }, { - name: "test many filters", - args: `ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, - want: manyFilters, - wantErr: false, + name: "test multiple values", + yaml: ` +field: + - ratelimit(5, "10s") -> tee("https://www.zalando.de/") + - invalid-filter + `, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - dpf := &defaultFiltersFlags{} - if err := dpf.Set(tt.args); (err != nil) != tt.wantErr { - t.Errorf("defaultFiltersFlags.Set() error = %v, wantErr %v", err, tt.wantErr) + cfg := struct { + Field *defaultFiltersFlags + }{ + Field: &defaultFiltersFlags{}, } - if !tt.wantErr { - if len(tt.want) != len(dpf.filters) { - t.Errorf("defaultFiltersFlags size mismatch got %d want %d", len(dpf.filters), len(tt.want)) - } - if err := yaml.Unmarshal([]byte(tt.args), dpf); err != nil { - t.Errorf("defaultFiltersFlags.UnmarshalYAML() error = %v, wantErr %v", err, tt.wantErr) - } - - if len(tt.want) != len(dpf.filters) { - t.Errorf("defaultFiltersFlags from yaml size mismatch got %d want %d", len(dpf.filters), len(tt.want)) - } - } + err := yaml.Unmarshal([]byte(tt.yaml), &cfg) + require.Error(t, err) }) } } From 3dc217089de89a0e672c6339db20d45795de0485 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:24:55 +0200 Subject: [PATCH 0803/1047] build(deps): bump docker/login-action from 3.1.0 to 3.2.0 (#3090) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/e92390c5fb421da1463c202d546fed0ec5c39f20...0d4c9c5ea7693da7b068278f7b52bda2a190a446) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index e273b20b19..9d4e1c7099 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -32,7 +32,7 @@ jobs: check-latest: true - name: Login to Github Container Registry - uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -54,7 +54,7 @@ jobs: - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' - uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 with: registry: ghcr.io username: ${{ github.actor }} From 3453b243604aa95ee7a8bc7185844f929adbec90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:27:37 +0200 Subject: [PATCH 0804/1047] build(deps): bump github.com/redis/go-redis/v9 from 9.5.1 to 9.5.2 (#3092) Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.5.1 to 9.5.2. - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.5.1...v9.5.2) --- updated-dependencies: - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d5dfe3d739..ed71b5d342 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.19.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.5.1 + github.com/redis/go-redis/v9 v9.5.2 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 diff --git a/go.sum b/go.sum index 570cefb2ed..d1e9f5db09 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= -github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.5.2 h1:L0L3fcSNReTRGyZ6AqAEN0K56wYeYAwapBIhkvh0f3E= +github.com/redis/go-redis/v9 v9.5.2/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= From 7e21afe649e7a613ccc3a970b6bac76db2e83833 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:56:58 +0200 Subject: [PATCH 0805/1047] build(deps): bump github.com/open-policy-agent/opa-envoy-plugin (#3093) Bumps [github.com/open-policy-agent/opa-envoy-plugin](https://github.com/open-policy-agent/opa-envoy-plugin) from 0.64.1-envoy to 0.65.0-envoy. - [Release notes](https://github.com/open-policy-agent/opa-envoy-plugin/releases) - [Changelog](https://github.com/open-policy-agent/opa-envoy-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa-envoy-plugin/compare/v0.64.1-envoy...v0.65.0-envoy) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa-envoy-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 23 +++++++++++------------ go.sum | 46 ++++++++++++++++++++++------------------------ 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/go.mod b/go.mod index ed71b5d342..5f2680b7df 100644 --- a/go.mod +++ b/go.mod @@ -27,8 +27,8 @@ require ( github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.59 github.com/oklog/ulid v1.3.1 - github.com/open-policy-agent/opa v0.64.1 - github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy + github.com/open-policy-agent/opa v0.65.0 + github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.19.1 @@ -67,8 +67,8 @@ require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Microsoft/hcsshim v0.11.5 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect @@ -77,8 +77,8 @@ require ( github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect - github.com/containerd/containerd v1.7.15 // indirect + github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 // indirect + github.com/containerd/containerd v1.7.17 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -94,7 +94,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/gobwas/glob v0.2.3 // indirect @@ -168,12 +168,11 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.20.0 // indirect + golang.org/x/tools v0.21.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect - google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.63.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/grpc v1.64.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.3.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index d1e9f5db09..d7dee0bac0 100644 --- a/go.sum +++ b/go.sum @@ -17,10 +17,10 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38= +github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -72,12 +72,12 @@ github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 h1:cJ1E8ZwZLfercT github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199/go.mod h1:jC+zrjHA5CaxJzn+tojIoIOzSp/6BlkRWXnMlxNkB+g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -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 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= +github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= -github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= +github.com/containerd/containerd v1.7.17 h1:KjNnn0+tAVQHAoaWRjmdak9WlvnFR/8rU1CHHy8Rm2A= +github.com/containerd/containerd v1.7.17/go.mod h1:vK+hhT4TIv2uejlcDlbVIc8+h/BqtKLIyNrtCZol8lI= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -159,8 +159,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= @@ -336,10 +336,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/open-policy-agent/opa v0.64.1 h1:n8IJTYlFWzqiOYx+JiawbErVxiqAyXohovcZxYbskxQ= -github.com/open-policy-agent/opa v0.64.1/go.mod h1:j4VeLorVpKipnkQ2TDjWshEuV3cvP/rHzQhYaraUXZY= -github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy h1:YCH3+S/tbivQS7lZDsdNbZkHYp8lpFcUIAYzAkLwWKo= -github.com/open-policy-agent/opa-envoy-plugin v0.64.1-envoy/go.mod h1:oesxvjmmIO3p0nPmy/xzv9oEsL4B+Ofdmd5vnHNZzAA= +github.com/open-policy-agent/opa v0.65.0 h1:wnEU0pEk80YjFi3yoDbFTMluyNssgPI4VJNJetD9a4U= +github.com/open-policy-agent/opa v0.65.0/go.mod h1:CNoLL44LuCH1Yot/zoeZXRKFylQtCJV+oGFiP2TeeEc= +github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy h1:HN/mBZeRpcNUmcOVZddxW0lhtQTagiLTPxf+mszXI1s= +github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy/go.mod h1:K7QVzQXxhv0S0PkDwazG/NZN6Jtaq0jQqfW0fftjPBU= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -626,8 +626,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= @@ -646,12 +646,10 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -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-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/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -659,8 +657,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= 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= From 338c99b29192f3ce020571a3b36d68948b38069d Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 3 Jun 2024 13:06:27 +0200 Subject: [PATCH 0806/1047] metrics: add start label for prometheus counters (#3089) Add start label to each counter with the value of counter creation timestamp as unix nanoseconds. This enables OpenTelemetry cumulative temporality, see https://opentelemetry.io/docs/specs/otel/metrics/data-model/#temporality Example: ``` ~$ curl -s localhost:9911/metrics | grep host_count # HELP skipper_serve_host_count Total number of requests of serving a host. # TYPE skipper_serve_host_count counter skipper_serve_host_count{code="200",host="bar_test",method="GET",start="1717066533598500794"} 1 skipper_serve_host_count{code="200",host="foo_test",method="GET",start="1717066538031805059"} 2 ``` Fixes #3087 Signed-off-by: Alexander Yastrebov --- config/config.go | 3 ++ metrics/metrics.go | 4 +++ metrics/prometheus.go | 29 +++++++++++++++++- metrics/prometheus_test.go | 62 ++++++++++++++++++++++++++++++++++++++ skipper.go | 5 +++ 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 12b27336ab..206f4de36f 100644 --- a/config/config.go +++ b/config/config.go @@ -70,6 +70,7 @@ type Config struct { // logging, metrics, profiling, tracing: EnablePrometheusMetrics bool `yaml:"enable-prometheus-metrics"` + EnablePrometheusStartLabel bool `yaml:"enable-prometheus-start-label"` OpenTracing string `yaml:"opentracing"` OpenTracingInitialSpan string `yaml:"opentracing-initial-span"` OpenTracingExcludedProxyTags string `yaml:"opentracing-excluded-proxy-tags"` @@ -378,6 +379,7 @@ func NewConfig() *Config { flag.IntVar(&cfg.BlockProfileRate, "block-profile-rate", 0, "block profile sample rate, see runtime.SetBlockProfileRate") flag.IntVar(&cfg.MutexProfileFraction, "mutex-profile-fraction", 0, "mutex profile fraction rate, see runtime.SetMutexProfileFraction") flag.IntVar(&cfg.MemProfileRate, "memory-profile-rate", 0, "memory profile rate, see runtime.SetMemProfileRate, keeps default 512 kB") + flag.BoolVar(&cfg.EnablePrometheusStartLabel, "enable-prometheus-start-label", false, "adds start label to each prometheus counter with the value of counter creation timestamp as unix nanoseconds") flag.BoolVar(&cfg.DebugGcMetrics, "debug-gc-metrics", false, "enables reporting of the Go garbage collector statistics exported in debug.GCStats") flag.BoolVar(&cfg.RuntimeMetrics, "runtime-metrics", true, "enables reporting of the Go runtime statistics exported in runtime and specifically runtime.MemStats") flag.BoolVar(&cfg.ServeRouteMetrics, "serve-route-metrics", false, "enables reporting total serve time metrics for each route") @@ -734,6 +736,7 @@ func (c *Config) ToOptions() skipper.Options { // logging, metrics, profiling, tracing: EnablePrometheusMetrics: c.EnablePrometheusMetrics, + EnablePrometheusStartLabel: c.EnablePrometheusStartLabel, OpenTracing: strings.Split(c.OpenTracing, " "), OpenTracingInitialSpan: c.OpenTracingInitialSpan, OpenTracingExcludedProxyTags: strings.Split(c.OpenTracingExcludedProxyTags, ","), diff --git a/metrics/metrics.go b/metrics/metrics.go index 88ebc49b1e..616706c352 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -194,6 +194,10 @@ type Options struct { // library. // A new registry is created if this option is nil. PrometheusRegistry *prometheus.Registry + + // EnablePrometheusStartLabel adds start label to each prometheus counter with the value of counter creation + // timestamp as unix nanoseconds. + EnablePrometheusStartLabel bool } var ( diff --git a/metrics/prometheus.go b/metrics/prometheus.go index d2f969df92..e9af8b3c1e 100644 --- a/metrics/prometheus.go +++ b/metrics/prometheus.go @@ -9,6 +9,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" + dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/proto" ) const ( @@ -295,7 +297,11 @@ func (p *Prometheus) registerMetrics() { } func (p *Prometheus) CreateHandler() http.Handler { - return promhttp.HandlerFor(p.registry, promhttp.HandlerOpts{}) + var gatherer prometheus.Gatherer = p.registry + if p.opts.EnablePrometheusStartLabel { + gatherer = withStartLabelGatherer{p.registry} + } + return promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}) } func (p *Prometheus) getHandler() http.Handler { @@ -457,3 +463,24 @@ func (p *Prometheus) IncErrorsStreaming(routeID string) { } func (p *Prometheus) Close() {} + +// withStartLabelGatherer adds a "start" label to all counters with +// the value of counter creation timestamp as unix nanoseconds. +type withStartLabelGatherer struct { + *prometheus.Registry +} + +func (g withStartLabelGatherer) Gather() ([]*dto.MetricFamily, error) { + metricFamilies, err := g.Registry.Gather() + for _, metricFamily := range metricFamilies { + if metricFamily.GetType() == dto.MetricType_COUNTER { + for _, metric := range metricFamily.Metric { + metric.Label = append(metric.Label, &dto.LabelPair{ + Name: proto.String("start"), + Value: proto.String(fmt.Sprintf("%d", metric.Counter.CreatedTimestamp.AsTime().UnixNano())), + }) + } + } + } + return metricFamilies, err +} diff --git a/metrics/prometheus_test.go b/metrics/prometheus_test.go index 6e5640c22d..0aa6574c9e 100644 --- a/metrics/prometheus_test.go +++ b/metrics/prometheus_test.go @@ -4,10 +4,14 @@ import ( "io" "net/http" "net/http/httptest" + "regexp" + "strconv" "strings" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/zalando/skipper/metrics" ) @@ -1053,3 +1057,61 @@ func TestPrometheusMetrics(t *testing.T) { }) } } + +func TestPrometheusMetricsStartTimestamp(t *testing.T) { + pm := metrics.NewPrometheus(metrics.Options{ + EnablePrometheusStartLabel: true, + EnableServeHostCounter: true, + }) + path := "/awesome-metrics" + + mux := http.NewServeMux() + pm.RegisterHandler(path, mux) + + before := time.Now() + + pm.MeasureServe("route1", "foo.test", "GET", 200, time.Now().Add(-15*time.Millisecond)) + pm.MeasureServe("route1", "bar.test", "POST", 201, time.Now().Add(-15*time.Millisecond)) + pm.MeasureServe("route1", "bar.test", "POST", 201, time.Now().Add(-15*time.Millisecond)) + pm.IncRoutingFailures() + pm.IncRoutingFailures() + pm.IncRoutingFailures() + + after := time.Now() + + req := httptest.NewRequest("GET", path, nil) + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + resp := w.Result() + require.Equal(t, http.StatusOK, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + t.Logf("Metrics response:\n%s", body) + + // Prometheus client does not allow to mock counter creation timestamps, + // see https://github.com/prometheus/client_golang/issues/1354 + // + // checkMetric tests that timestamp is within [before, after] range. + checkMetric := func(pattern string) { + t.Helper() + + re := regexp.MustCompile(pattern) + + matches := re.FindSubmatch(body) + require.NotNil(t, matches, "Metrics response does not match: %s", pattern) + require.Len(t, matches, 2) + + ts, err := strconv.ParseInt(string(matches[1]), 10, 64) + require.NoError(t, err) + + assert.GreaterOrEqual(t, ts, before.UnixNano()) + assert.LessOrEqual(t, ts, after.UnixNano()) + } + + checkMetric(`skipper_serve_host_count{code="200",host="foo_test",method="GET",start="(\d+)"} 1`) + checkMetric(`skipper_serve_host_count{code="201",host="bar_test",method="POST",start="(\d+)"} 2`) + checkMetric(`skipper_route_error_total{start="(\d+)"} 3`) +} diff --git a/skipper.go b/skipper.go index 4bcf0336f1..619ad0f8a4 100644 --- a/skipper.go +++ b/skipper.go @@ -716,6 +716,10 @@ type Options struct { // use the MetricsFlavours option. EnablePrometheusMetrics bool + // EnablePrometheusStartLabel adds start label to each prometheus counter with the value of counter creation + // timestamp as unix nanoseconds. + EnablePrometheusStartLabel bool + // An instance of a Prometheus registry. It allows registering and serving custom metrics when skipper is used as a // library. // A new registry is created if this option is nil. @@ -1514,6 +1518,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { HistogramBuckets: o.HistogramMetricBuckets, DisableCompatibilityDefaults: o.DisableMetricsCompatibilityDefaults, PrometheusRegistry: o.PrometheusRegistry, + EnablePrometheusStartLabel: o.EnablePrometheusStartLabel, } mtr := o.MetricsBackend From 5822d16346ec1f2f88fbef8450d2e4088c6c6882 Mon Sep 17 00:00:00 2001 From: Magnus Jungsbluth Date: Mon, 3 Jun 2024 13:54:51 +0200 Subject: [PATCH 0807/1047] Reduce errors in decision logs that are operational (#3086) Signed-off-by: Magnus Jungsbluth --- filters/openpolicyagent/evaluation.go | 6 +++ .../internal/envoy/skipperadapter.go | 12 +++++- .../opaauthorizerequest.go | 6 ++- .../opaauthorizerequest_test.go | 14 +++++++ .../opaserveresponse/opaserveresponse.go | 7 +++- .../opaserveresponse/opaserveresponse_test.go | 37 +++++++++++-------- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go index f01b1be948..03f56b450a 100644 --- a/filters/openpolicyagent/evaluation.go +++ b/filters/openpolicyagent/evaluation.go @@ -11,6 +11,7 @@ import ( "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/server" + "github.com/open-policy-agent/opa/topdown" "github.com/opentracing/opentracing-go" ) @@ -36,6 +37,11 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. var input map[string]interface{} defer func() { stopeval() + if topdown.IsCancel(err) { + // If the evaluation was canceled, we don't want to log the decision. + return + } + err := opa.logDecision(ctx, input, result, err) if err != nil { opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to log decision to control plane.") diff --git a/filters/openpolicyagent/internal/envoy/skipperadapter.go b/filters/openpolicyagent/internal/envoy/skipperadapter.go index 9f8f0acae9..6d8cf771bd 100644 --- a/filters/openpolicyagent/internal/envoy/skipperadapter.go +++ b/filters/openpolicyagent/internal/envoy/skipperadapter.go @@ -1,14 +1,16 @@ package envoy import ( + "fmt" "net/http" "strings" + "unicode/utf8" ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" ) -func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metadata, contextExtensions map[string]string, rawBody []byte) *ext_authz_v3.CheckRequest { +func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metadata, contextExtensions map[string]string, rawBody []byte) (*ext_authz_v3.CheckRequest, error) { headers := make(map[string]string, len(req.Header)) for h, vv := range req.Header { @@ -17,7 +19,7 @@ func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metada headers[strings.ToLower(h)] = strings.Join(vv, ", ") } - return &ext_authz_v3.CheckRequest{ + ereq := &ext_authz_v3.CheckRequest{ Attributes: &ext_authz_v3.AttributeContext{ Request: &ext_authz_v3.AttributeContext_Request{ Http: &ext_authz_v3.AttributeContext_HttpRequest{ @@ -32,4 +34,10 @@ func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metada MetadataContext: metadata, }, } + + if !utf8.ValidString(ereq.Attributes.Request.Http.Path) { + return nil, fmt.Errorf("invalid utf8 in path: %q", ereq.Attributes.Request.Http.Path) + } + + return ereq, nil } diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go index 3b9cf38107..c3ff73cd70 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go @@ -121,7 +121,11 @@ func (f *opaAuthorizeRequestFilter) Request(fc filters.FilterContext) { req.Body = body } - authzreq := envoy.AdaptToExtAuthRequest(req, f.opa.InstanceConfig().GetEnvoyMetadata(), f.envoyContextExtensions, rawBodyBytes) + authzreq, err := envoy.AdaptToExtAuthRequest(req, f.opa.InstanceConfig().GetEnvoyMetadata(), f.envoyContextExtensions, rawBodyBytes) + if err != nil { + f.opa.HandleEvaluationError(fc, span, nil, err, !f.opa.EnvoyPluginConfig().DryRun, http.StatusBadRequest) + return + } start := time.Now() result, err := f.opa.Eval(ctx, authzreq) diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index f0850dec79..1e48c63c8f 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -255,6 +255,20 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Invalid UTF-8 in Path", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow/%c0%ae%c0%ae", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusBadRequest, + expectedBody: "", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, } { t.Run(ti.msg, func(t *testing.T) { t.Logf("Running test for %v", ti) diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse.go index 76e8b34501..64ad14d1f9 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse.go @@ -2,6 +2,7 @@ package opaserveresponse import ( "io" + "net/http" "time" "github.com/zalando/skipper/filters" @@ -114,7 +115,11 @@ func (f *opaServeResponseFilter) Request(fc filters.FilterContext) { req.Body = body } - authzreq := envoy.AdaptToExtAuthRequest(fc.Request(), f.opa.InstanceConfig().GetEnvoyMetadata(), f.envoyContextExtensions, rawBodyBytes) + authzreq, err := envoy.AdaptToExtAuthRequest(fc.Request(), f.opa.InstanceConfig().GetEnvoyMetadata(), f.envoyContextExtensions, rawBodyBytes) + if err != nil { + f.opa.HandleEvaluationError(fc, span, nil, err, !f.opa.EnvoyPluginConfig().DryRun, http.StatusBadRequest) + return + } start := time.Now() result, err := f.opa.Eval(ctx, authzreq) diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go index 0951cdf3c7..2746c9e8c7 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -4,8 +4,6 @@ import ( "fmt" "io" "net/http" - "net/url" - "path" "strings" "testing" @@ -38,7 +36,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow", - requestPath: "allow", + requestPath: "/allow", expectedStatus: http.StatusInternalServerError, expectedBody: "", expectedHeaders: make(http.Header), @@ -48,7 +46,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow", - requestPath: "forbidden", + requestPath: "/forbidden", expectedStatus: http.StatusForbidden, expectedHeaders: make(http.Header), }, @@ -57,7 +55,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/invalid_path", - requestPath: "allow", + requestPath: "/allow", expectedStatus: http.StatusInternalServerError, expectedBody: "", expectedHeaders: make(http.Header), @@ -67,7 +65,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_object", - requestPath: "allow/structured", + requestPath: "/allow/structured", expectedStatus: http.StatusOK, expectedBody: "Welcome from policy!", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, @@ -77,7 +75,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_object", - requestPath: "allow/production", + requestPath: "/allow/production", expectedStatus: http.StatusOK, expectedBody: "Welcome to production evaluation!", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, @@ -87,7 +85,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_object", - requestPath: "allow/test", + requestPath: "/allow/test", expectedStatus: http.StatusForbidden, expectedBody: "Unauthorized Request", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"no"}}, @@ -97,7 +95,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_object_structured_body", - requestPath: "allow/structured", + requestPath: "/allow/structured", expectedStatus: http.StatusInternalServerError, expectedBody: "", expectedHeaders: map[string][]string{}, @@ -107,7 +105,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponse", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_object_contextextensions", - requestPath: "allow/structured", + requestPath: "/allow/structured", contextExtensions: "hello: world", expectedStatus: http.StatusOK, expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, @@ -118,13 +116,23 @@ func TestAuthorizeRequestFilter(t *testing.T) { filterName: "opaServeResponseWithReqBody", bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_object_req_body", - requestPath: "allow/allow_object_req_body", + requestPath: "/allow/allow_object_req_body", requestHeaders: map[string][]string{"content-type": {"application/json"}}, body: `{"hello":"world"}`, expectedStatus: http.StatusOK, expectedBody: `{"hello":"world"}`, expectedHeaders: map[string][]string{}, }, + { + msg: "Invalid UTF-8 in Path", + filterName: "opaServeResponse", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow/%c0%ae%c0%ae", + expectedStatus: http.StatusBadRequest, + expectedBody: "", + expectedHeaders: make(http.Header), + }, } { t.Run(ti.msg, func(t *testing.T) { t.Logf("Running test for %v", ti) @@ -257,11 +265,8 @@ func TestAuthorizeRequestFilter(t *testing.T) { r := eskip.MustParse(fmt.Sprintf(`* -> %s("%s", "%s") -> `, ti.filterName, ti.bundleName, ti.contextExtensions)) proxy := proxytest.New(fr, r...) - reqURL, err := url.Parse(proxy.URL) - assert.NoErrorf(t, err, "Failed to parse url %s: %v", proxy.URL, err) - reqURL.Path = path.Join(reqURL.Path, ti.requestPath) - req, err := http.NewRequest("GET", reqURL.String(), strings.NewReader(ti.body)) + req, err := http.NewRequest("GET", proxy.URL+ti.requestPath, strings.NewReader(ti.body)) assert.NoError(t, err) for name, values := range ti.requestHeaders { for _, value := range values { @@ -284,7 +289,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { defer rsp.Body.Close() body, err := io.ReadAll(rsp.Body) assert.NoError(t, err) - assert.Equal(t, ti.expectedBody, string(body), "HTTP Headers do not match") + assert.Equal(t, ti.expectedBody, string(body), "HTTP Body does not match") }) } } From c8ac9cb9be881489cfec3886d6f06363a16caa3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Mon, 3 Jun 2024 14:45:50 +0200 Subject: [PATCH 0808/1047] doc: review and update ingress usage docs (#3094) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc: review and update ingress usage docs Signed-off-by: Sandor Szücs --- docs/kubernetes/ingress-usage.md | 118 ++++++++++++++++++++----------- docs/reference/filters.md | 2 +- 2 files changed, 76 insertions(+), 44 deletions(-) diff --git a/docs/kubernetes/ingress-usage.md b/docs/kubernetes/ingress-usage.md index 1b35e3cdae..d8d96fea7b 100644 --- a/docs/kubernetes/ingress-usage.md +++ b/docs/kubernetes/ingress-usage.md @@ -4,21 +4,21 @@ This documentation is meant for people deploying to Kubernetes Clusters and describes to use Ingress and low level and high level features Skipper provides. -[RouteGroups](routegroups.md), a relatively new feature, also -support each of these features, with an alternative format that -supports them in a more native way. The documentation contains a -section with -[mapping](routegroups.md#mapping-from-ingress-to-routegroups) -Ingress to RouteGroups. +[RouteGroups](routegroups.md), Skipper Kubernetes native routing +object that supports all Skipper features. If you need to create more +than one route to your application, RouteGroups should be the default +choice, instead of ingress. The documentation contains a section with +[mapping](routegroups.md#mapping-from-ingress-to-routegroups) Ingress +to RouteGroups. ## Skipper Ingress Annotations Annotation | example data | usage --- | --- | --- -zalando.org/backend-weights | `{"my-app-1": 80, "my-app-2": 20}` | blue-green deployments +zalando.org/backend-weights | `{"my-app-1": 80, "my-app-2": 20}` | blue-green deployments, see also [StackSet](https://github.com/zalando-incubator/stackset-controller) for more high-level integration zalando.org/skipper-filter | `consecutiveBreaker(15)` | arbitrary filters zalando.org/skipper-predicate | `QueryParam("version", "^alpha$")` | arbitrary predicates -zalando.org/skipper-routes | `Method("OPTIONS") -> status(200) -> ` | extra custom routes +zalando.org/skipper-routes | `Method("OPTIONS") -> status(200) -> ` | extra custom routes, please consider using [RouteGroups](routegroups.md) instead zalando.org/ratelimit | `ratelimit(50, "1m")` | deprecated, use zalando.org/skipper-filter instead zalando.org/skipper-ingress-redirect | `"true"` | change the default HTTPS redirect behavior for specific ingresses (true/false) zalando.org/skipper-ingress-redirect-code | `301` | change the default HTTPS redirect code for specific ingresses @@ -30,6 +30,7 @@ zalando.org/skipper-ingress-path-mode | `path-prefix` | (*deprecated*) please us Ingress backend definitions are services, which have different [service types](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types). +ClusterIP should be the default for all backend applications that expose via ingress. Service type | supported | workaround --- | --- | --- @@ -106,7 +107,7 @@ spec: If multiple ingresses define the same host and the same predicates, traffic routing may become non-deterministic. Consider the following two ingresses which have the same hostname and therefore -overlap. In skipper the routing of this is currently undefined as skipper +overlap. In Skipper the routing of this is currently undefined as skipper doesn't pick one over the other, but just creates routes (possible overlapping) for each of the ingresses. @@ -164,18 +165,19 @@ Ingress paths can be interpreted in five different modes: 1. `pathType: Prefix` results in [PathSubtree predicate](../reference/predicates.md#pathsubtree)) 2. `pathType: Exact` results in [Path predicate](../reference/predicates.md#path)) 3. `pathType: ImplementationSpecific` - 1. based on the kubernetes ingress specification + 1. based on the Kubernetes ingress specification 2. as plain regular expression 3. as a path prefix (same as `pathType: Prefix` and results in [PathSubtree](../reference/predicates.md#pathsubtree)) -The default is 3.1 the kubernetes ingress mode. It can be changed by a startup option -to any of the other modes, and the individual ingress rules can also override the -default behavior with the zalando.org/skipper-ingress-path-mode annotation. You can +The default is 3.1 the Kubernetes ingress mode. It can be changed by startup option `-kubernetes-path-mode` +to any of the other modes. The individual ingress rules can also override the +default behavior with the `zalando.org/skipper-ingress-path-mode` annotation. You can also set for each path rule a different Kubernetes `pathType` like `Prefix` and `Exact`. E.g.: - zalando.org/skipper-ingress-path-mode: path-prefix + zalando.org/skipper-ingress-path-mode: path-prefix # Skipper specific + pathType: Prefix # ingress v1 ### Kubernetes ingress specification base path @@ -188,7 +190,7 @@ path. ### Plain regular expression When the path mode is set to `path-regexp`, the ingress path is interpreted similar -to the default kubernetes ingress specification way, but is not prepended by the `^` +to the default Kubernetes ingress specification way, but is not prepended by the `^` control character. ### Path prefix @@ -241,7 +243,7 @@ Custom routes is a way of extending the default routes configured for an ingress resource. Sometimes you just want to return a header, redirect or even static -html content. You can return from skipper without doing a proxy call +html content. You can return from Skipper without doing a proxy call to a backend, if you end your filter chain with ``. The use of `` recommends the use in combination with `status()` filter, to not respond with the default http code, which defaults to 404. To @@ -253,7 +255,7 @@ priority. Custom routes specified in ingress will always add the `Host()` [predicate](../reference/predicates.md#host) to match the host header specified in the ingress `rules:`. If there is a `path:` definition in your -ingress, then it will be based on the skipper command line parameter +ingress, then it will be based on the Skipper command line parameter `-kubernetes-path-mode` set one of these predicates: - [Path()](../reference/predicates.md#path) @@ -351,7 +353,7 @@ directly with a HTTP status code 200: ``` zalando.org/skipper-routes: | - Path("/") -> setResponseHeader("X", "bar") -> inlineContent("hello") -> status(200) -> + PathRegexp("/") -> setResponseHeader("X", "bar") -> inlineContent("hello") -> status(200) -> ``` Keep in mind that you need a valid backend definition to backends @@ -399,8 +401,8 @@ Host(/^app-default[.]example[.]org$/) && Method("OPTIONS") -> ### Multiple routes -You can also set multiple routes, but you have to set the names of the -route as defined in eskip: +You can also set multiple routes, but you have to set the IDs +(`routename1`, `routename2`) of the route as defined in eskip: ``` zalando.org/skipper-routes: | @@ -449,7 +451,7 @@ A possible solution is to use skipper's [RouteGroups](routegroups.md). ## Filters - Basic HTTP manipulations -HTTP manipulations are done by using skipper filters. Changes can be +HTTP manipulations are done by using Skipper filters. Changes can be done in the request path, meaning request to your backend or in the response path to the client, which made the request. @@ -548,31 +550,26 @@ by the filters mentioned above. ### Diagnosis - Throttling Bandwidth - Latency -For diagnosis purpose there are filters that enable you to throttle +For diagnosis purpose there are more than 20 filters that enable you to throttle the bandwidth or add latency. For the full list of filters see our -[diag filter godoc page](https://godoc.org/github.com/zalando/skipper/filters/diag). +[diagnostics filters](../reference/filters.md#diagnostics). +Examples: bandwidth(30) // incoming in kb/s backendBandwidth(30) // outgoing in kb/s backendLatency(120) // in ms - -Filter documentation: - -- [latency](../reference/filters.md#latency) -- [bandwidth](../reference/filters.md#bandwidth) -- [chunks](../reference/filters.md#chunks) -- [backendlatency](../reference/filters.md#backendlatency) -- [backendChunks](../reference/filters.md#backendchunks) -- [randomcontent](../reference/filters.md#randomcontent) + normalRequestLatency("10ms", "5ms") // normal distribution for request latency as time duration string + logHeader("request") // log all request headers + logBody("response", 1024) // log up to 1024 Bytes of the response body ### Flow Id to trace request flows -To trace request flows skipper can generate a unique Flow Id for every +To trace request flows Skipper can generate a unique Flow Id for every HTTP request that it receives. You can then find the trace of the request in all your access logs. Skipper sets the X-Flow-Id header to a unique value. Read more about this in our [flowid filter](../reference/filters.md#flowid) -and [godoc](https://godoc.org/github.com/zalando/skipper/filters/flowid). +and [godoc](https://pkg.go.dev/github.com/zalando/skipper/filters/flowid). flowId("reuse") @@ -648,6 +645,33 @@ spec: pathType: ImplementationSpecific ``` +#### Admission Control + +The [admissionControl](../reference/filters.md#admissioncontrol) +filter is a dynamic circuit breaker that works based on HTTP error +codes observed by backends. It will dynamically adjust the shedding of +load to the maximum throughput possible. + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + zalando.org/skipper-filter: admissionControl("myapp", "active", "1s", 5, 10, 0.95, 0.9, 0.5) + name: app +spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + service: + name: app-svc + port: + number: 80 + pathType: ImplementationSpecific +``` + ### Ratelimits @@ -665,15 +689,15 @@ side as described above. Ratelimits are enforced per route. -More details you will find in [ratelimit -package](https://godoc.org/github.com/zalando/skipper/filters/ratelimit) -and in our [ratelimit tutorial](../tutorials/ratelimit.md). +More details you will find in [rate limit +filters](../reference/filters.md#rate-limit) section and in our +[ratelimit tutorial](../tutorials/ratelimit.md). #### Client Ratelimits The example shows 20 calls per hour per client, based on X-Forwarded-For header or IP in case there is no X-Forwarded-For header -set, are allowed to each skipper instance for the given ingress. +set, are allowed to each Skipper instance for the given ingress. ```yaml apiVersion: networking.k8s.io/v1 @@ -705,7 +729,7 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: - zalando.org/skipper-filter: clientRatelimit(20, "1h", "auth") + zalando.org/skipper-filter: clientRatelimit(20, "1h", "authorization") name: app spec: rules: @@ -849,6 +873,7 @@ If you want to test a new replacement of a production service with production load, you can copy incoming requests to your new endpoint and ignore the responses from your new backend. This can be done by the [tee()](../reference/filters.md#tee) and [teenf()](../reference/filters.md#teenf) filters. +See also our [shadow traffic tutorial](../tutorials/shadow-traffic.md). ```yaml apiVersion: networking.k8s.io/v1 @@ -936,16 +961,23 @@ spec: pathType: ImplementationSpecific ``` -### IP Whitelisting +### IP Allow Listing + +This ingress route will only allow traffic from networks 1.2.3.0/24 +and 195.168.0.0/17 Before you use this in production please understand +your deployment and check the difference between the following +options: -This ingress route will only allow traffic from networks 1.2.3.0/24 and 195.168.0.0/17 +- [ClientIP](../reference/predicates.md#clientip) +- [Source](../reference/predicates.md#source) +- [SourceFromLast](../reference/predicates.md#sourcefromlast) ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: - zalando.org/skipper-predicate: Source("1.2.3.0/24", "195.168.0.0/17") + zalando.org/skipper-predicate: ClientIP("1.2.3.0/24", "195.168.0.0/17") name: app spec: rules: @@ -1113,7 +1145,7 @@ For more advanced blue-green deployments, check out our [stackset-controller](ht ## Chaining Filters and Predicates -You can set multiple filters in a chain similar to the [eskip format](https://godoc.org/github.com/zalando/skipper/eskip). +You can set multiple filters in a chain similar to the [eskip format](https://pkg.go.dev/github.com/zalando/skipper/eskip). ```yaml apiVersion: networking.k8s.io/v1 diff --git a/docs/reference/filters.md b/docs/reference/filters.md index d22546b95e..bc858894cc 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -3263,7 +3263,7 @@ tracingTag("http.flow_id", "${request.header.X-Flow-Id}") ### tracingTagFromResponse -This filter works just like [tracingTag](#tracingTag), but is applied after the request was processed. In particular, [template placeholders](#template-placeholders) referencing the response can be used in the parameters. +This filter works just like [tracingTag](#tracingtag), but is applied after the request was processed. In particular, [template placeholders](#template-placeholders) referencing the response can be used in the parameters. ### tracingSpanName From cf9258f130c74739337eb3637cfb4ff5bc58aa59 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 4 Jun 2024 18:48:09 +0200 Subject: [PATCH 0809/1047] build: trim path (#3095) Add -trimpath flag to remove filesystem paths and make stacktraces shorter, see https://pkg.go.dev/cmd/go Signed-off-by: Alexander Yastrebov --- packaging/Makefile | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packaging/Makefile b/packaging/Makefile index e140665128..91e30e423a 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -11,6 +11,7 @@ CGO_ENABLED ?= 0 GOOS ?= linux GOARCH ?= amd64 GOARM ?= +BUILD_FLAGS ?= -v -trimpath COMMIT_HASH = $(shell git rev-parse --short HEAD) default: docker-build @@ -20,16 +21,16 @@ skipper: GOARCH=$(GOARCH) \ $(GOARM) \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o skipper ../cmd/skipper/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o skipper ../cmd/skipper/*.go eskip: - GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build -o eskip ../cmd/eskip/*.go + GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build $(BUILD_FLAGS) -o eskip ../cmd/eskip/*.go webhook: - GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build -o webhook ../cmd/webhook/*.go + GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build $(BUILD_FLAGS) -o webhook ../cmd/webhook/*.go routesrv: - GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build -o routesrv ../cmd/routesrv/*.go + GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOARM) CGO_ENABLED=$(CGO_ENABLED) go build $(BUILD_FLAGS) -o routesrv ../cmd/routesrv/*.go clean: rm -rf $(BINARIES) build/ @@ -81,38 +82,38 @@ build/linux/amd64/%: GOOS=linux \ GOARCH=amd64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go build/linux/arm64/%: GOOS=linux \ GOARCH=arm64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/arm64/$(notdir $@) ../cmd/$(notdir $@)/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/arm64/$(notdir $@) ../cmd/$(notdir $@)/*.go build/linux/arm/v7/%: GOOS=linux \ GOARCH=arm \ GOARM=7 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/arm/v7/$(notdir $@) ../cmd/$(notdir $@)/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/linux/arm/v7/$(notdir $@) ../cmd/$(notdir $@)/*.go build/darwin/amd64/%: GOOS=darwin \ GOARCH=amd64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/darwin/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/darwin/amd64/$(notdir $@) ../cmd/$(notdir $@)/*.go build/darwin/arm64/%: GOOS=darwin \ GOARCH=arm64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/darwin/arm64/$(notdir $@) ../cmd/$(notdir $@)/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/darwin/arm64/$(notdir $@) ../cmd/$(notdir $@)/*.go build/windows/amd64/%: GOOS=windows \ GOARCH=amd64 \ CGO_ENABLED=$(CGO_ENABLED) \ - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/windows/amd64/$(notdir $@).exe ../cmd/$(notdir $@)/*.go + go build $(BUILD_FLAGS) -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT_HASH)" -o build/windows/amd64/$(notdir $@).exe ../cmd/$(notdir $@)/*.go build.package: build.linux build.darwin build.windows # Pack linux binaries as tar From 5a1374041780e589406f732f03a76ce34b6d232a Mon Sep 17 00:00:00 2001 From: Magnus Jungsbluth Date: Wed, 5 Jun 2024 11:05:56 +0200 Subject: [PATCH 0810/1047] OPA: Add decision outcome to span (#3096) Signed-off-by: Magnus Jungsbluth --- .../openpolicyagent/opaauthorizerequest/opaauthorizerequest.go | 1 + 1 file changed, 1 insertion(+) diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go index c3ff73cd70..ca6671d9c3 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go @@ -151,6 +151,7 @@ func (f *opaAuthorizeRequestFilter) Request(fc filters.FilterContext) { f.opa.HandleInvalidDecisionError(fc, span, result, err, !f.opa.EnvoyPluginConfig().DryRun) return } + span.SetTag("opa.decision.allowed", allowed) if !allowed { fc.Metrics().IncCounter(f.opa.MetricsKey("decision.deny")) f.opa.ServeResponse(fc, span, result) From b297d64a040145d07beed9ae0da648e39a4bfd97 Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Thu, 6 Jun 2024 15:08:48 +0200 Subject: [PATCH 0811/1047] Set log level of PHC dropping an endpoint to debug (#3098) This is needed not to log too many lines. Moreover, the information about requests amount could be seen in the metrics, the information about IPs being mitigated could be get from updateStats logs Signed-off-by: Roman Zavodskikh Co-authored-by: Roman Zavodskikh --- proxy/healthy_endpoints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index d4818065d6..70c0366361 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -26,7 +26,7 @@ func (h *healthyEndpoints) filterHealthyEndpoints(ctx *context, endpoints []rout for _, e := range endpoints { dropProbability := e.Metrics.HealthCheckDropProbability() if p < dropProbability { - ctx.Logger().Infof("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", + ctx.Logger().Debugf("Dropping endpoint %q due to passive health check: p=%0.2f, dropProbability=%0.2f", e.Host, p, dropProbability) metrics.IncCounter("passive-health-check.endpoints.dropped") unhealthyEndpointsCount++ From 0e6d576dabf4c499f3b32b4b531470b354a1fc8d Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 7 Jun 2024 14:28:31 +0200 Subject: [PATCH 0812/1047] docs: cleanup deprecated -lb-healthcheck-interval flag (#3099) Follow up on #2906 Signed-off-by: Alexander Yastrebov --- docs/kubernetes/deploy/daemonset/daemonset.yaml | 1 - docs/kubernetes/deploy/deployment/deployment.yaml | 1 - docs/tutorials/basics.md | 1 - docs/tutorials/operations.md | 1 - packaging/helm/skipper-aws/templates/skipper/deployment.yaml | 1 - 5 files changed, 5 deletions(-) diff --git a/docs/kubernetes/deploy/daemonset/daemonset.yaml b/docs/kubernetes/deploy/daemonset/daemonset.yaml index 5646b0942e..b0b9d20336 100644 --- a/docs/kubernetes/deploy/daemonset/daemonset.yaml +++ b/docs/kubernetes/deploy/daemonset/daemonset.yaml @@ -51,7 +51,6 @@ spec: - "-experimental-upgrade" - "-metrics-exp-decay-sample" - "-reverse-source-predicate" - - "-lb-healthcheck-interval=3s" - "-metrics-flavour=codahale,prometheus" - "-enable-connection-metrics" - "-max-audit-body=0" diff --git a/docs/kubernetes/deploy/deployment/deployment.yaml b/docs/kubernetes/deploy/deployment/deployment.yaml index f15db53466..311e81b77e 100644 --- a/docs/kubernetes/deploy/deployment/deployment.yaml +++ b/docs/kubernetes/deploy/deployment/deployment.yaml @@ -59,7 +59,6 @@ spec: - "-experimental-upgrade" - "-metrics-exp-decay-sample" - "-reverse-source-predicate" - - "-lb-healthcheck-interval=3s" - "-metrics-flavour=prometheus" - "-enable-connection-metrics" - "-max-audit-body=0" diff --git a/docs/tutorials/basics.md b/docs/tutorials/basics.md index fc925ef07d..94ca076a7b 100644 --- a/docs/tutorials/basics.md +++ b/docs/tutorials/basics.md @@ -352,7 +352,6 @@ address: ":8080" enable-ratelimits: true experimental-upgrade: true metrics-exp-decay-sample: true -lb-healthcheck-interval: "3s" metrics-flavour: ["codahale","prometheus"] enable-connection-metrics: true whitelisted-healthcheck-cidr: "172.20.0.0/16" diff --git a/docs/tutorials/operations.md b/docs/tutorials/operations.md index 79e5f85dd8..eab47ada9b 100644 --- a/docs/tutorials/operations.md +++ b/docs/tutorials/operations.md @@ -32,7 +32,6 @@ Kubernetes use cases: - "-proxy-preserve-host" - "-enable-ratelimits" - "-experimental-upgrade" - - "-lb-healthcheck-interval=3s" - "-metrics-flavour=prometheus" - "-metrics-exp-decay-sample" - "-serve-host-metrics" diff --git a/packaging/helm/skipper-aws/templates/skipper/deployment.yaml b/packaging/helm/skipper-aws/templates/skipper/deployment.yaml index 36e7bed55f..bf1d236f6e 100644 --- a/packaging/helm/skipper-aws/templates/skipper/deployment.yaml +++ b/packaging/helm/skipper-aws/templates/skipper/deployment.yaml @@ -67,7 +67,6 @@ spec: - "-experimental-upgrade" - "-metrics-exp-decay-sample" - "-reverse-source-predicate" - - "-lb-healthcheck-interval=3s" - "-metrics-flavour=prometheus" - "-enable-connection-metrics" - "-enable-route-lifo-metrics" From 89836f6bfa7d3091c61b7c922b2b939560d019a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:56:01 +0200 Subject: [PATCH 0813/1047] build(deps): bump amazonlinux from `5478f82` to `0d172f8` in /fuzz (#3105) Bumps amazonlinux from `5478f82` to `0d172f8`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 5c518e2c96..c0bc4948f1 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:5478f82c47e435ed988fa12a00b623ef3c920fadd669d40c596249cf81960c4c +FROM amazonlinux:2023@sha256:0d172f83b07323d8a8a08ea2242b7d64eb676f013eba269a158f229ce062a24f WORKDIR /workspace From 746d4621132f3a4626b4d861f913dcf2ef3651e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:57:17 +0200 Subject: [PATCH 0814/1047] build(deps): bump golang.org/x/net from 0.25.0 to 0.26.0 (#3100) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.25.0 to 0.26.0. - [Commits](https://github.com/golang/net/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 14 +++++++------- go.sum | 24 ++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 5f2680b7df..dbc492dba9 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.19.1 + github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/redis/go-redis/v9 v9.5.2 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 @@ -47,12 +48,12 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.25.0 + golang.org/x/net v0.26.0 golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.7.0 - golang.org/x/term v0.20.0 + golang.org/x/term v0.21.0 golang.org/x/time v0.5.0 google.golang.org/protobuf v1.34.1 gopkg.in/go-jose/go-jose.v2 v2.6.3 @@ -137,7 +138,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect @@ -166,9 +166,9 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect diff --git a/go.sum b/go.sum index d7dee0bac0..77965f2c02 100644 --- a/go.sum +++ b/go.sum @@ -518,8 +518,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -557,8 +557,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -599,15 +599,15 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -626,8 +626,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= From 522262c7cef6d39adfc31b60cc0ef4189ac0c88f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:15:38 +0200 Subject: [PATCH 0815/1047] build(deps): bump github.com/instana/go-sensor from 1.62.1 to 1.63.0 (#3104) Bumps [github.com/instana/go-sensor](https://github.com/instana/go-sensor) from 1.62.1 to 1.63.0. - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/RELEASE.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.62.1...v1.63.0) --- updated-dependencies: - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dbc492dba9..f396960048 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 - github.com/instana/go-sensor v1.62.1 + github.com/instana/go-sensor v1.63.0 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.59 github.com/oklog/ulid v1.3.1 diff --git a/go.sum b/go.sum index 77965f2c02..a7619cfd8a 100644 --- a/go.sum +++ b/go.sum @@ -253,8 +253,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.62.1 h1:WkZ3Foej8UtvuJlmnPO9PDyRbEHMVVn3Dmzt9GQpLd8= -github.com/instana/go-sensor v1.62.1/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= +github.com/instana/go-sensor v1.63.0 h1:6+oad7bXDIaZUoLGGSqGhPStKgwstA5rz9vg7EK1RuI= +github.com/instana/go-sensor v1.63.0/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= From 5b75b1a2134d4873b28b510e40ac801acb75f4ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:25:45 +0200 Subject: [PATCH 0816/1047] build(deps): bump golang.org/x/oauth2 from 0.20.0 to 0.21.0 (#3102) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.20.0 to 0.21.0. - [Commits](https://github.com/golang/oauth2/compare/v0.20.0...v0.21.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f396960048..a03b854b44 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.26.0 - golang.org/x/oauth2 v0.20.0 + golang.org/x/oauth2 v0.21.0 golang.org/x/sync v0.7.0 golang.org/x/term v0.21.0 golang.org/x/time v0.5.0 diff --git a/go.sum b/go.sum index a7619cfd8a..e2df3a8295 100644 --- a/go.sum +++ b/go.sum @@ -560,8 +560,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 8d239522d79c2abfe3d33ff12b420326c673c141 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:15:39 +0200 Subject: [PATCH 0817/1047] build(deps): bump docker/build-push-action from 5.3.0 to 5.4.0 (#3106) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/2cdde995de11925a030ce8070c3d77a52ffcf1c0...ca052bb54ab0790a636c9b5f226502c73d547a25) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 9d4e1c7099..049a9f2c14 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 37b72f47ccd007d3c93024fe37d598199e1aa78d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:15:57 +0200 Subject: [PATCH 0818/1047] build(deps): bump github.com/miekg/dns from 1.1.59 to 1.1.61 (#3109) Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.59 to 1.1.61. - [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release) - [Commits](https://github.com/miekg/dns/compare/v1.1.59...v1.1.61) --- updated-dependencies: - dependency-name: github.com/miekg/dns dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index a03b854b44..5f920ceba6 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/hashicorp/memberlist v0.5.1 github.com/instana/go-sensor v1.63.0 github.com/lightstep/lightstep-tracer-go v0.26.0 - github.com/miekg/dns v1.1.59 + github.com/miekg/dns v1.1.61 github.com/oklog/ulid v1.3.1 github.com/open-policy-agent/opa v0.65.0 github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy @@ -165,10 +165,10 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.22.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect diff --git a/go.sum b/go.sum index e2df3a8295..451b9e6816 100644 --- a/go.sum +++ b/go.sum @@ -295,8 +295,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -540,8 +540,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -626,8 +626,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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= From 38dde38608730e8b7016cd2e0a52f72c31e4c1df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 14:51:08 +0200 Subject: [PATCH 0819/1047] build(deps): bump github.com/redis/go-redis/v9 from 9.5.2 to 9.5.3 (#3110) Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.5.2 to 9.5.3. - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.5.2...v9.5.3) --- updated-dependencies: - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5f920ceba6..6fd7d6cce9 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.5.2 + github.com/redis/go-redis/v9 v9.5.3 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 diff --git a/go.sum b/go.sum index 451b9e6816..cf6f37ebf8 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.5.2 h1:L0L3fcSNReTRGyZ6AqAEN0K56wYeYAwapBIhkvh0f3E= -github.com/redis/go-redis/v9 v9.5.2/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= +github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= From 9bf6f022dd0d4eb833e6eddac0fef0b4bd47e1ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:04:09 +0200 Subject: [PATCH 0820/1047] build(deps): bump google.golang.org/protobuf from 1.34.1 to 1.34.2 (#3108) Bumps google.golang.org/protobuf from 1.34.1 to 1.34.2. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6fd7d6cce9..2d0680cd34 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( golang.org/x/sync v0.7.0 golang.org/x/term v0.21.0 golang.org/x/time v0.5.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf diff --git a/go.sum b/go.sum index cf6f37ebf8..ea63936d0c 100644 --- a/go.sum +++ b/go.sum @@ -668,8 +668,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From 47c3db15c36bfe7557456e9ac3b02108c20823ed Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 17 Jun 2024 16:31:27 +0200 Subject: [PATCH 0821/1047] routing: log route update id (#3112) Add and log route update counter to observe delay between receiving and applying routes. Signed-off-by: Alexander Yastrebov --- routing/datasource.go | 6 ++++-- routing/routing.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/routing/datasource.go b/routing/datasource.go index 36f8b7ce91..e18f1875c3 100644 --- a/routing/datasource.go +++ b/routing/datasource.go @@ -511,6 +511,7 @@ func processRouteDefs(o Options, fr filters.Registry, defs []*eskip.Route) (rout } type routeTable struct { + id int m *matcher once sync.Once routes []*Route // only used for closing @@ -543,10 +544,10 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} updatesRelay <-chan []*eskip.Route ) updatesRelay = updates - for { + for id := 1; ; id++ { select { case defs := <-updatesRelay: - o.Log.Info("route settings received") + o.Log.Infof("route settings received, id: %d", id) for i := range o.PreProcessors { defs = o.PreProcessors[i].Do(defs) @@ -582,6 +583,7 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} }) rt = &routeTable{ + id: id, m: m, routes: routes, validRoutes: validRoutes, diff --git a/routing/routing.go b/routing/routing.go index efdb746305..21c21d4772 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -353,7 +353,7 @@ func (r *Routing) startReceivingUpdates(o Options) { r.firstLoadSignaled = true } } - r.log.Info("route settings applied") + r.log.Infof("route settings applied, id: %d", rt.id) if r.metrics != nil { // existing codebases might not supply metrics instance r.metrics.UpdateGauge("routes.total", float64(len(rt.validRoutes))) r.metrics.UpdateGauge("routes.updated_timestamp", float64(rt.created.Unix())) From dbcb0fd6530b16e20b23b5d7228e8cb75bbae798 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:23:05 +0200 Subject: [PATCH 0822/1047] build(deps): bump actions/checkout from 4.1.6 to 4.1.7 (#3107) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.6 to 4.1.7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/a5ac7e51b41094c92402da3b24376905380afc29...692973e3d937129bcbf40652eb9f2f61becf3332) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b30453a138..06db6ef549 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index a46fef7c01..f27f86e5a1 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 049a9f2c14..12669ec19c 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index a97cf11531..6278fa73c2 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 17388ad6df..3922825659 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v3.1.0 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index fbc054364e..24138a0b6d 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From 9eee9a3b5d8e810f947cdd813629a517cfffc0e6 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 17 Jun 2024 18:05:53 +0200 Subject: [PATCH 0823/1047] routing: measure route update latency (#3113) * add route update latency metric - the time it takes to apply routes update after receival. * remove redundant setting of `created` location to UTC - its value is only exposed via Unix() which is always in UTC. Signed-off-by: Alexander Yastrebov --- routing/datasource.go | 4 +++- routing/routing.go | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/routing/datasource.go b/routing/datasource.go index e18f1875c3..c9be374df2 100644 --- a/routing/datasource.go +++ b/routing/datasource.go @@ -547,6 +547,8 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} for id := 1; ; id++ { select { case defs := <-updatesRelay: + start := time.Now() + o.Log.Infof("route settings received, id: %d", id) for i := range o.PreProcessors { @@ -588,7 +590,7 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} routes: routes, validRoutes: validRoutes, invalidRoutes: invalidRoutes, - created: time.Now().UTC(), + created: start, } updatesRelay = nil outRelay = out diff --git a/routing/routing.go b/routing/routing.go index 21c21d4772..068f1c98e1 100644 --- a/routing/routing.go +++ b/routing/routing.go @@ -269,7 +269,7 @@ func New(o Options) *Routing { initialMatcher, _ := newMatcher(nil, MatchingOptionsNone) rt := &routeTable{ m: initialMatcher, - created: time.Now().UTC(), + created: time.Now(), } r.routeTable.Store(rt) r.startReceivingUpdates(o) @@ -357,6 +357,7 @@ func (r *Routing) startReceivingUpdates(o Options) { if r.metrics != nil { // existing codebases might not supply metrics instance r.metrics.UpdateGauge("routes.total", float64(len(rt.validRoutes))) r.metrics.UpdateGauge("routes.updated_timestamp", float64(rt.created.Unix())) + r.metrics.MeasureSince("routes.update_latency", rt.created) } case <-r.quit: var rt *routeTable From 355f8355bdbe34de74fbdbbec104794c280d5377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Tue, 18 Jun 2024 13:07:06 +0200 Subject: [PATCH 0824/1047] doc: mention DCO and what to do in your commit (#3114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- CONTRIBUTING.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d48680f0f..46e441d223 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,7 @@ If you need help, please reach out to us: [community channels](https://github.co ## Report a bug + Reporting bugs is one of the best ways to contribute. Before creating a bug report, please check that an [issue](https://github.com/zalando/skipper/issues) reporting the same problem does not already exist. If there is an such an issue, you may add your information as a comment. To report a new bug you should open an issue that summarizes the bug and set the label to "bug". @@ -20,14 +21,18 @@ To report a new bug you should open an issue that summarizes the bug and set the If you want to provide a fix along with your bug report: That is great! In this case please send us a pull request as described in section [Contribute Code](#contribute-code). ## Suggest a feature + To request a new feature you should open an [issue](https://github.com/zalando/skipper/issues/new) and summarize the desired functionality and its use case. Set the issue label to "enhancement". ## Contribute code -This is a rough outline of what the workflow for code contributions looks like: + +This is a rough outline of what the workflow for code contributions +looks like: + - Check the list of open [issues](https://github.com/zalando/skipper/issues). Either assign an existing issue to yourself, or create a new one that you would like work on and discuss your ideas and use cases. - Fork the repository on GitHub - Create a topic branch, for example feature/foo fix/bar refactor/baz, from where you want to base your work. The base is usually master. -- Make commits of logical units. +- Make commits of logical units and use `git commit --sign-off` to comply with [DCO](https://developercertificate.org/). - Write good commit messages (see below). - Push your changes to a topic branch in your fork of the repository. - Submit a pull request to [zalando/skipper](https://github.com/zalando/skipper) @@ -40,9 +45,11 @@ This is a rough outline of what the workflow for code contributions looks like: Thanks for your contributions! ### Code style + Skipper is formatted with [gofmt](https://golang.org/cmd/gofmt/). Please run it on your code before making a pull request. The coding style suggested by the Golang community is the preferred one for the cases that are not covered by gofmt, see the [style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details. ### Commit messages + Your commit messages ideally can answer two questions: what changed and why. The subject line should feature the “what” and the body of the commit should describe the “why”. When creating a pull request, its comment should reference the corresponding issue id. From 7da6fc65e6d9ec2a050f1c25d5bcb86fea70d3c8 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 18 Jun 2024 18:12:56 +0200 Subject: [PATCH 0825/1047] routing: refactor update id logging (#3116) Increment update id only on update receival instead of each loop cycle which results in odd update id values due to second select case branch. Follow up on #3112 Signed-off-by: Alexander Yastrebov --- routing/datasource.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/routing/datasource.go b/routing/datasource.go index c9be374df2..11967d7945 100644 --- a/routing/datasource.go +++ b/routing/datasource.go @@ -542,14 +542,16 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} rt *routeTable outRelay chan<- *routeTable updatesRelay <-chan []*eskip.Route + updateId int ) updatesRelay = updates - for id := 1; ; id++ { + for { select { case defs := <-updatesRelay: + updateId++ start := time.Now() - o.Log.Infof("route settings received, id: %d", id) + o.Log.Infof("route settings received, id: %d", updateId) for i := range o.PreProcessors { defs = o.PreProcessors[i].Do(defs) @@ -585,7 +587,7 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} }) rt = &routeTable{ - id: id, + id: updateId, m: m, routes: routes, validRoutes: validRoutes, From ff44b6df8afe5ba6432ae8bc3e8181ac35d5f717 Mon Sep 17 00:00:00 2001 From: Magnus Jungsbluth Date: Wed, 19 Jun 2024 13:00:32 +0200 Subject: [PATCH 0826/1047] OPA: Add response status to control plane traces (#3118) Signed-off-by: Magnus Jungsbluth --- filters/openpolicyagent/tracing.go | 29 +++++++++++++++++-------- filters/openpolicyagent/tracing_test.go | 20 +++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/filters/openpolicyagent/tracing.go b/filters/openpolicyagent/tracing.go index 531e563913..420deb231f 100644 --- a/filters/openpolicyagent/tracing.go +++ b/filters/openpolicyagent/tracing.go @@ -89,21 +89,32 @@ func (tr *transport) RoundTrip(req *http.Request) (*http.Response, error) { span = tr.tracer.StartSpan(spanNameHttpOut, spanOpts...) } - if span != nil { - defer span.Finish() + if span == nil { + return tr.wrapped.RoundTrip(req) + } - setSpanTags(span, tr.bundleName, tr.manager) - req = req.WithContext(opentracing.ContextWithSpan(ctx, span)) + defer span.Finish() - carrier := opentracing.HTTPHeadersCarrier(req.Header) - span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, carrier) - } + setSpanTags(span, tr.bundleName, tr.manager) + req = req.WithContext(opentracing.ContextWithSpan(ctx, span)) + + carrier := opentracing.HTTPHeadersCarrier(req.Header) + span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, carrier) resp, err := tr.wrapped.RoundTrip(req) - if err != nil && span != nil { + + if err != nil { span.SetTag("error", true) span.LogKV("event", "error", "message", err.Error()) + return resp, err + } + + span.SetTag(proxy.HTTPStatusCodeTag, resp.StatusCode) + + if resp.StatusCode > 399 { + span.SetTag("error", true) + span.LogKV("event", "error", "message", resp.Status) } - return resp, err + return resp, nil } diff --git a/filters/openpolicyagent/tracing_test.go b/filters/openpolicyagent/tracing_test.go index dc5bd33370..92f295eea3 100644 --- a/filters/openpolicyagent/tracing_test.go +++ b/filters/openpolicyagent/tracing_test.go @@ -10,6 +10,7 @@ import ( "github.com/open-policy-agent/opa/plugins" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/tracing/tracingtest" ) @@ -43,6 +44,7 @@ func TestTracingFactory(t *testing.T) { }, tracer: nil, parentSpan: tracer.StartSpan("open-policy-agent"), + resp: &http.Response{StatusCode: http.StatusOK}, }, { name: "Sub-span created with parent span without tracer set", @@ -54,6 +56,7 @@ func TestTracingFactory(t *testing.T) { }, tracer: tracer, parentSpan: tracer.StartSpan("open-policy-agent"), + resp: &http.Response{StatusCode: http.StatusOK}, }, { name: "Sub-span created without parent span", @@ -64,6 +67,7 @@ func TestTracingFactory(t *testing.T) { URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, }, tracer: tracer, + resp: &http.Response{StatusCode: http.StatusOK}, }, { name: "Span contains error information", @@ -76,6 +80,17 @@ func TestTracingFactory(t *testing.T) { tracer: tracer, resperr: assert.AnError, }, + { + name: "Response has a 4xx response", + req: &http.Request{ + Header: map[string][]string{}, + Method: "GET", + Host: "example.com", + URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, + }, + tracer: tracer, + resp: &http.Response{StatusCode: http.StatusUnauthorized}, + }, } for _, tc := range testCases { @@ -105,6 +120,11 @@ func TestTracingFactory(t *testing.T) { if tc.resperr == nil { assert.NoError(t, err) + if tc.resp.StatusCode > 399 { + assert.Equal(t, true, createdSpan.Tags["error"], "Error tag was not set") + } + + assert.Equal(t, tc.resp.StatusCode, createdSpan.Tags[proxy.HTTPStatusCodeTag], "http status tag was not set") } else { assert.Equal(t, true, createdSpan.Tags["error"], "Error tag was not set") assert.Equal(t, tc.resperr, err, "Error was not propagated") From 9bf19e4e1160229568a0a129847d11cc6e2609c9 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 19 Jun 2024 13:47:47 +0200 Subject: [PATCH 0827/1047] routing: measure CreateFilter latency (#3115) Add `MeasureFilterCreate` method to `Metrics` interface similar to existing `MeasureFilterRequest` and `MeasureFilterResponse`. The alternative would be to use `MeasureSince` but then filter name would have to be embedded into metric key. Signed-off-by: Alexander Yastrebov --- metrics/all_kind.go | 22 ++++++++++ metrics/codahale.go | 5 +++ metrics/codahale_test.go | 1 + metrics/metrics.go | 1 + metrics/metricstest/metricsmock.go | 8 ++++ metrics/prometheus.go | 15 +++++++ routing/datasource.go | 25 +++++++---- routing/datasource_test.go | 66 ++++++++++++++++++++++++++++-- routing/host_test.go | 6 ++- routing/matcher_test.go | 4 +- 10 files changed, 137 insertions(+), 16 deletions(-) diff --git a/metrics/all_kind.go b/metrics/all_kind.go index 2eb2cebeb8..33a33eae95 100644 --- a/metrics/all_kind.go +++ b/metrics/all_kind.go @@ -23,71 +23,93 @@ func (a *All) MeasureSince(key string, start time.Time) { a.prometheus.MeasureSince(key, start) a.codaHale.MeasureSince(key, start) } + func (a *All) IncCounter(key string) { a.prometheus.IncCounter(key) a.codaHale.IncCounter(key) } + func (a *All) IncCounterBy(key string, value int64) { a.prometheus.IncCounterBy(key, value) a.codaHale.IncCounterBy(key, value) } + func (a *All) IncFloatCounterBy(key string, value float64) { a.prometheus.IncFloatCounterBy(key, value) a.codaHale.IncFloatCounterBy(key, value) } + func (a *All) UpdateGauge(key string, v float64) { a.prometheus.UpdateGauge(key, v) a.codaHale.UpdateGauge(key, v) } + func (a *All) MeasureRouteLookup(start time.Time) { a.prometheus.MeasureRouteLookup(start) a.codaHale.MeasureRouteLookup(start) } + +func (a *All) MeasureFilterCreate(filterName string, start time.Time) { + a.prometheus.MeasureFilterCreate(filterName, start) + a.codaHale.MeasureFilterCreate(filterName, start) +} + func (a *All) MeasureFilterRequest(filterName string, start time.Time) { a.prometheus.MeasureFilterRequest(filterName, start) a.codaHale.MeasureFilterRequest(filterName, start) } + func (a *All) MeasureAllFiltersRequest(routeId string, start time.Time) { a.prometheus.MeasureAllFiltersRequest(routeId, start) a.codaHale.MeasureAllFiltersRequest(routeId, start) } + func (a *All) MeasureBackend(routeId string, start time.Time) { a.prometheus.MeasureBackend(routeId, start) a.codaHale.MeasureBackend(routeId, start) } + func (a *All) MeasureBackendHost(routeBackendHost string, start time.Time) { a.prometheus.MeasureBackendHost(routeBackendHost, start) a.codaHale.MeasureBackendHost(routeBackendHost, start) } + func (a *All) MeasureFilterResponse(filterName string, start time.Time) { a.prometheus.MeasureFilterResponse(filterName, start) a.codaHale.MeasureFilterResponse(filterName, start) } + func (a *All) MeasureAllFiltersResponse(routeId string, start time.Time) { a.prometheus.MeasureAllFiltersResponse(routeId, start) a.codaHale.MeasureAllFiltersResponse(routeId, start) } + func (a *All) MeasureResponse(code int, method string, routeId string, start time.Time) { a.prometheus.MeasureResponse(code, method, routeId, start) a.codaHale.MeasureResponse(code, method, routeId, start) } + func (a *All) MeasureServe(routeId, host, method string, code int, start time.Time) { a.prometheus.MeasureServe(routeId, host, method, code, start) a.codaHale.MeasureServe(routeId, host, method, code, start) } + func (a *All) IncRoutingFailures() { a.prometheus.IncRoutingFailures() a.codaHale.IncRoutingFailures() } + func (a *All) IncErrorsBackend(routeId string) { a.prometheus.IncErrorsBackend(routeId) a.codaHale.IncErrorsBackend(routeId) } + func (a *All) MeasureBackend5xx(t time.Time) { a.prometheus.MeasureBackend5xx(t) a.codaHale.MeasureBackend5xx(t) } + func (a *All) IncErrorsStreaming(routeId string) { a.prometheus.IncErrorsStreaming(routeId) a.codaHale.IncErrorsStreaming(routeId) diff --git a/metrics/codahale.go b/metrics/codahale.go index 0b7fa8241d..cfcc897e22 100644 --- a/metrics/codahale.go +++ b/metrics/codahale.go @@ -14,6 +14,7 @@ import ( const ( KeyRouteLookup = "routelookup" KeyRouteFailure = "routefailure" + KeyFilterCreate = "filter.%s.create" KeyFilterRequest = "filter.%s.request" KeyFiltersRequest = "allfilters.request.%s" KeyAllFiltersRequestCombined = "allfilters.combined.request" @@ -131,6 +132,10 @@ func (c *CodaHale) MeasureRouteLookup(start time.Time) { c.measureSince(KeyRouteLookup, start) } +func (c *CodaHale) MeasureFilterCreate(filterName string, start time.Time) { + c.measureSince(fmt.Sprintf(KeyFilterCreate, filterName), start) +} + func (c *CodaHale) MeasureFilterRequest(filterName string, start time.Time) { c.measureSince(fmt.Sprintf(KeyFilterRequest, filterName), start) } diff --git a/metrics/codahale_test.go b/metrics/codahale_test.go index 7f7fd6e8a3..916b709c99 100644 --- a/metrics/codahale_test.go +++ b/metrics/codahale_test.go @@ -151,6 +151,7 @@ type proxyMetricTest struct { var proxyMetricsTests = []proxyMetricTest{ // T1 - Measure routing {KeyRouteLookup, func(m Metrics) { m.MeasureRouteLookup(time.Now()) }}, + {fmt.Sprintf(KeyFilterCreate, "afilter"), func(m Metrics) { m.MeasureFilterCreate("afilter", time.Now()) }}, // T2 - Measure filter request {fmt.Sprintf(KeyFilterRequest, "foo"), func(m Metrics) { m.MeasureFilterRequest("foo", time.Now()) }}, // T3 - Measure all filters request diff --git a/metrics/metrics.go b/metrics/metrics.go index 616706c352..c6b79c4da6 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -62,6 +62,7 @@ type Metrics interface { IncFloatCounterBy(key string, value float64) // Additional methods MeasureRouteLookup(start time.Time) + MeasureFilterCreate(filterName string, start time.Time) MeasureFilterRequest(filterName string, start time.Time) MeasureAllFiltersRequest(routeId string, start time.Time) MeasureBackend(routeId string, start time.Time) diff --git a/metrics/metricstest/metricsmock.go b/metrics/metricstest/metricsmock.go index a61b59b161..f08183ebbc 100644 --- a/metrics/metricstest/metricsmock.go +++ b/metrics/metricstest/metricsmock.go @@ -1,6 +1,7 @@ package metricstest import ( + "fmt" "net/http" "sync" "time" @@ -112,6 +113,13 @@ func (m *MockMetrics) MeasureRouteLookup(start time.Time) { // implement me } +func (m *MockMetrics) MeasureFilterCreate(filterName string, start time.Time) { + key := fmt.Sprintf("%sfilter.%s.create", m.Prefix, filterName) + m.WithMeasures(func(measures map[string][]time.Duration) { + measures[key] = append(m.measures[key], time.Since(start)) + }) +} + func (m *MockMetrics) MeasureFilterRequest(filterName string, start time.Time) { // implement me } diff --git a/metrics/prometheus.go b/metrics/prometheus.go index e9af8b3c1e..a013d0a259 100644 --- a/metrics/prometheus.go +++ b/metrics/prometheus.go @@ -30,6 +30,7 @@ type Prometheus struct { routeLookupM *prometheus.HistogramVec routeErrorsM *prometheus.CounterVec responseM *prometheus.HistogramVec + filterCreateM *prometheus.HistogramVec filterRequestM *prometheus.HistogramVec filterAllRequestM *prometheus.HistogramVec filterAllCombinedRequestM *prometheus.HistogramVec @@ -86,6 +87,14 @@ func NewPrometheus(opts Options) *Prometheus { Buckets: opts.HistogramBuckets, }, []string{"code", "method", "route"}) + filterCreate := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: promFilterSubsystem, + Name: "create_duration_seconds", + Help: "Duration in seconds of filter creation.", + Buckets: opts.HistogramBuckets, + }, []string{"filter"}) + filterRequest := prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, @@ -229,6 +238,7 @@ func NewPrometheus(opts Options) *Prometheus { routeLookupM: routeLookup, routeErrorsM: routeErrors, responseM: response, + filterCreateM: filterCreate, filterRequestM: filterRequest, filterAllRequestM: filterAllRequest, filterAllCombinedRequestM: filterAllCombinedRequest, @@ -352,6 +362,11 @@ func (p *Prometheus) MeasureRouteLookup(start time.Time) { p.routeLookupM.WithLabelValues().Observe(t) } +func (p *Prometheus) MeasureFilterCreate(filterName string, start time.Time) { + t := p.sinceS(start) + p.filterCreateM.WithLabelValues(filterName).Observe(t) +} + // MeasureFilterRequest satisfies Metrics interface. func (p *Prometheus) MeasureFilterRequest(filterName string, start time.Time) { t := p.sinceS(start) diff --git a/routing/datasource.go b/routing/datasource.go index 11967d7945..4f87c6edb8 100644 --- a/routing/datasource.go +++ b/routing/datasource.go @@ -208,8 +208,8 @@ func splitBackend(r *eskip.Route) (string, string, error) { // creates a filter instance based on its definition and its // specification in the filter registry. -func createFilter(fr filters.Registry, def *eskip.Filter, cpm map[string]PredicateSpec) (filters.Filter, error) { - spec, ok := fr[def.Name] +func createFilter(o *Options, def *eskip.Filter, cpm map[string]PredicateSpec) (filters.Filter, error) { + spec, ok := o.FilterRegistry[def.Name] if !ok { if isTreePredicate(def.Name) || def.Name == predicates.HostName || def.Name == predicates.PathRegexpName || def.Name == predicates.MethodName || def.Name == predicates.HeaderName || def.Name == predicates.HeaderRegexpName { return nil, fmt.Errorf("trying to use %q as filter, but it is only available as predicate", def.Name) @@ -222,7 +222,14 @@ func createFilter(fr filters.Registry, def *eskip.Filter, cpm map[string]Predica return nil, fmt.Errorf("filter %q not found", def.Name) } + start := time.Now() + f, err := spec.CreateFilter(def.Args) + + if o.Metrics != nil { // measure regardless of the error + o.Metrics.MeasureFilterCreate(def.Name, start) + } + if err != nil { return nil, fmt.Errorf("failed to create filter %q: %w", spec.Name(), err) } @@ -231,10 +238,10 @@ func createFilter(fr filters.Registry, def *eskip.Filter, cpm map[string]Predica // creates filter instances based on their definition // and the filter registry. -func createFilters(fr filters.Registry, defs []*eskip.Filter, cpm map[string]PredicateSpec) ([]*RouteFilter, error) { +func createFilters(o *Options, defs []*eskip.Filter, cpm map[string]PredicateSpec) ([]*RouteFilter, error) { fs := make([]*RouteFilter, 0, len(defs)) for i, def := range defs { - f, err := createFilter(fr, def, cpm) + f, err := createFilter(o, def, cpm) if err != nil { return nil, err } @@ -456,13 +463,13 @@ func processTreePredicates(r *Route, predicateList []*eskip.Predicate) error { } // processes a route definition for the routing table -func processRouteDef(cpm map[string]PredicateSpec, fr filters.Registry, def *eskip.Route) (*Route, error) { +func processRouteDef(o *Options, cpm map[string]PredicateSpec, def *eskip.Route) (*Route, error) { scheme, host, err := splitBackend(def) if err != nil { return nil, err } - fs, err := createFilters(fr, def.Filters, cpm) + fs, err := createFilters(o, def.Filters, cpm) if err != nil { return nil, err } @@ -496,10 +503,10 @@ func mapPredicates(cps []PredicateSpec) map[string]PredicateSpec { } // processes a set of route definitions for the routing table -func processRouteDefs(o Options, fr filters.Registry, defs []*eskip.Route) (routes []*Route, invalidDefs []*eskip.Route) { +func processRouteDefs(o *Options, defs []*eskip.Route) (routes []*Route, invalidDefs []*eskip.Route) { cpm := mapPredicates(o.Predicates) for _, def := range defs { - route, err := processRouteDef(cpm, fr, def) + route, err := processRouteDef(o, cpm, def) if err == nil { routes = append(routes, route) } else { @@ -557,7 +564,7 @@ func receiveRouteMatcher(o Options, out chan<- *routeTable, quit <-chan struct{} defs = o.PreProcessors[i].Do(defs) } - routes, invalidRoutes := processRouteDefs(o, o.FilterRegistry, defs) + routes, invalidRoutes := processRouteDefs(&o, defs) for i := range o.PostProcessors { routes = o.PostProcessors[i].Do(routes) diff --git a/routing/datasource_test.go b/routing/datasource_test.go index 57eaacbe93..9e3f39013a 100644 --- a/routing/datasource_test.go +++ b/routing/datasource_test.go @@ -5,10 +5,12 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/builtin" "github.com/zalando/skipper/logging" "github.com/zalando/skipper/logging/loggingtest" + "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/predicates/primitive" "github.com/zalando/skipper/predicates/query" "github.com/zalando/skipper/routing" @@ -56,10 +58,12 @@ func TestNoMultipleTreePredicates(t *testing.T) { } erred := false + o := &routing.Options{ + FilterRegistry: make(filters.Registry), + } pr := make(map[string]routing.PredicateSpec) - fr := make(filters.Registry) for _, d := range defs { - if _, err := routing.ExportProcessRouteDef(pr, fr, d); err != nil { + if _, err := routing.ExportProcessRouteDef(o, pr, d); err != nil { erred = true break } @@ -119,8 +123,11 @@ func TestProcessRouteDefErrors(t *testing.T) { } fr := make(filters.Registry) fr.Register(builtin.NewSetPath()) + o := &routing.Options{ + FilterRegistry: fr, + } for _, d := range defs { - _, err := routing.ExportProcessRouteDef(pr, fr, d) + _, err := routing.ExportProcessRouteDef(o, pr, d) if err == nil || err.Error() != ti.err { t.Errorf("expected error '%s'. Got: '%s'", ti.err, err) } @@ -298,6 +305,41 @@ func TestLogging(t *testing.T) { }) } +func TestMetrics(t *testing.T) { + t.Run("create filter latency", func(t *testing.T) { + client, err := testdataclient.NewDoc(` + r0: * -> slowCreate("100ms") -> slowCreate("200ms") -> slowCreate("100ms") -> ; + `) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + metrics := &metricstest.MockMetrics{ + Now: time.Now(), + } + fr := make(filters.Registry) + fr.Register(slowCreateSpec{}) + + r := routing.New(routing.Options{ + DataClients: []routing.DataClient{client}, + FilterRegistry: fr, + Metrics: metrics, + SignalFirstLoad: true, + }) + defer r.Close() + <-r.FirstLoad() + + metrics.WithMeasures(func(m map[string][]time.Duration) { + assert.InEpsilonSlice(t, []time.Duration{ + 100 * time.Millisecond, + 200 * time.Millisecond, + 100 * time.Millisecond, + }, m["filter.slowCreate.create"], 0.1) + }) + }) +} + type weightedPredicateSpec struct { name string weight int @@ -319,3 +361,21 @@ func (w weightedPredicateSpec) Create([]interface{}) (routing.Predicate, error) func (w weightedPredicateSpec) Weight() int { return w.weight } + +type ( + slowCreateSpec struct{} + slowCreateFilter struct{} +) + +func (s slowCreateSpec) Name() string { return "slowCreate" } + +func (s slowCreateSpec) CreateFilter(args []interface{}) (filters.Filter, error) { + d, _ := time.ParseDuration(args[0].(string)) + + time.Sleep(d) + + return slowCreateFilter{}, nil +} + +func (s slowCreateFilter) Request(ctx filters.FilterContext) {} +func (s slowCreateFilter) Response(filters.FilterContext) {} diff --git a/routing/host_test.go b/routing/host_test.go index 3fc0a835d4..9fc9a5918d 100644 --- a/routing/host_test.go +++ b/routing/host_test.go @@ -51,14 +51,16 @@ func benchmarkPredicateHost(b *testing.B, predicateFmt string) { ha := host.NewAny() pr := map[string]routing.PredicateSpec{ha.Name(): ha} - fr := make(filters.Registry) + o := &routing.Options{ + FilterRegistry: make(filters.Registry), + } var routes []*routing.Route for i := 0; i < R; i++ { p := strings.ReplaceAll(predicateFmt, "{i}", fmt.Sprintf("%d", i)) def := eskip.MustParse(fmt.Sprintf(`r%d: %s -> ;`, i, p)) - route, err := routing.ExportProcessRouteDef(pr, fr, def[0]) + route, err := routing.ExportProcessRouteDef(o, pr, def[0]) if err != nil { b.Fatal(err) } diff --git a/routing/matcher_test.go b/routing/matcher_test.go index 13c0487fc3..c40c33495c 100644 --- a/routing/matcher_test.go +++ b/routing/matcher_test.go @@ -101,7 +101,7 @@ func docToRoutes(doc string) ([]*Route, error) { if err != nil { return nil, err } - routes, _ := processRouteDefs(Options{Predicates: []PredicateSpec{&truePredicate{}}}, nil, defs) + routes, _ := processRouteDefs(&Options{Predicates: []PredicateSpec{&truePredicate{}}}, defs) return routes, nil } @@ -193,7 +193,7 @@ func generateRoutes(paths []string) []*Route { defs[i] = &eskip.Route{Id: fmt.Sprintf("route%d", i), Path: p, Backend: p} } - routes, _ := processRouteDefs(Options{}, nil, defs) + routes, _ := processRouteDefs(&Options{}, defs) return routes } From e4cc1521c42a9b374a376f3004fc5c60e622552e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:03:27 +0200 Subject: [PATCH 0828/1047] build(deps): bump alpine from `77726ef` to `b89d9c9` in /packaging (#3122) Bumps alpine from `77726ef` to `b89d9c9`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 9427671362..1fbe8f05b4 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd +FROM --platform=linux/arm64 alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index 1a58f37642..37f5afca38 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd +FROM --platform=linux/arm/v7 alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From a61d3fe868329684cb4007e6fbab5bca293ca5a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:59:46 +0200 Subject: [PATCH 0829/1047] build(deps): bump docker/build-push-action from 5.4.0 to 6.1.0 (#3124) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.4.0 to 6.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/ca052bb54ab0790a636c9b5f226502c73d547a25...31159d49c0d4756269a0940a750801a1ea5d7003) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 12669ec19c..4dc13ef424 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 + uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 8e2c2cc0396d91b4d137d39755d2b996b5e5b4d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:00:27 +0200 Subject: [PATCH 0830/1047] build(deps): bump amazonlinux from `0d172f8` to `b0016cb` in /fuzz (#3125) Bumps amazonlinux from `0d172f8` to `b0016cb`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index c0bc4948f1..f7f1ec878b 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:0d172f83b07323d8a8a08ea2242b7d64eb676f013eba269a158f229ce062a24f +FROM amazonlinux:2023@sha256:b0016cbc423cd29bca07d5ff783c432c7f0477dc8d19bdf5828dd560a0b3d0b9 WORKDIR /workspace From 2a623a940a622517b4cd489c24815e5e7b86b578 Mon Sep 17 00:00:00 2001 From: JanardhanSharma Date: Mon, 24 Jun 2024 17:35:33 +0200 Subject: [PATCH 0831/1047] Facilitate OPA decision correlation with business flows (#3041) * feature: OPA decision correlation with business flows Signed-off-by: Magnus Jungsbluth Signed-off-by: Janardhan Sharma --- docs/tutorials/auth.md | 9 +++ filters/openpolicyagent/evaluation.go | 34 ++++++++++- .../opaauthorizerequest_test.go | 58 +++++++++++++++++-- .../openpolicyagent/openpolicyagent_test.go | 20 ++++--- 4 files changed, 106 insertions(+), 15 deletions(-) diff --git a/docs/tutorials/auth.md b/docs/tutorials/auth.md index 14bf04e1f7..0ac9363792 100644 --- a/docs/tutorials/auth.md +++ b/docs/tutorials/auth.md @@ -466,6 +466,15 @@ The second argument is parsed as YAML, cannot be nested and values need to be st In Rego this can be used like this `input.attributes.contextExtensions["com.mycompany.myprop"] == "my value"` +### Decision ID in Policies + +Each evaluation yields a distinct decision, identifiable by its unique decision ID. +This decision ID can be located within the input at: + +`input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id` + +Typical use cases are either propagation of the decision ID to downstream systems or returning it as part of the response. As an example this can allow to trouble shoot deny requests by looking up details using the full decision in a control plane. + ### Quick Start Rego Playground A quick way without setting up Backend APIs is to use the [Rego Playground](https://play.openpolicyagent.org/). diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go index 03f56b450a..ccdc38c30e 100644 --- a/filters/openpolicyagent/evaluation.go +++ b/filters/openpolicyagent/evaluation.go @@ -3,8 +3,7 @@ package openpolicyagent import ( "context" "fmt" - "time" - + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" "github.com/open-policy-agent/opa-envoy-plugin/opa/decisionlog" @@ -13,6 +12,8 @@ import ( "github.com/open-policy-agent/opa/server" "github.com/open-policy-agent/opa/topdown" "github.com/opentracing/opentracing-go" + pbstruct "google.golang.org/protobuf/types/known/structpb" + "time" ) func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3.CheckRequest) (*envoyauth.EvalResult, error) { @@ -23,6 +24,12 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return nil, err } + err = setDecisionIdInRequest(req, decisionId) + if err != nil { + opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to set decision ID in Request.") + return nil, err + } + result, stopeval, err := envoyauth.NewEvalResult(withDecisionID(decisionId)) if err != nil { opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to generate new result with decision ID.") @@ -71,6 +78,29 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return result, nil } +func setDecisionIdInRequest(req *ext_authz_v3.CheckRequest, decisionId string) error { + if req.Attributes.MetadataContext == nil { + req.Attributes.MetadataContext = &ext_authz_v3_core.Metadata{ + FilterMetadata: map[string]*pbstruct.Struct{}, + } + } + + filterMeta, err := formOpenPolicyAgentMetaDataObject(decisionId) + if err != nil { + return err + } + req.Attributes.MetadataContext.FilterMetadata["open_policy_agent"] = filterMeta + return nil +} + +func formOpenPolicyAgentMetaDataObject(decisionId string) (*pbstruct.Struct, error) { + + innerFields := make(map[string]interface{}) + innerFields["decision_id"] = decisionId + + return pbstruct.NewStruct(innerFields) +} + func (opa *OpenPolicyAgentInstance) logDecision(ctx context.Context, input interface{}, result *envoyauth.EvalResult, err error) error { info := &server.Info{ Timestamp: time.Now(), diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 1e48c63c8f..4dff05ecfa 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -255,6 +255,21 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Decision id in request header", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object_decision_id_in_header", + requestMethod: "POST", + body: `{ "target_id" : "123456" }`, + requestHeaders: map[string][]string{"content-type": {"application/json"}}, + requestPath: "/allow/structured", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: map[string][]string{"Decision-Id": {"some-random-decision-id-generated-during-evaluation"}}, + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Invalid UTF-8 in Path", filterName: "opaAuthorizeRequest", @@ -345,8 +360,21 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow_body { input.parsed_body.target_id == "123456" - } - `, + } + + decision_id := input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id + + allow_object_decision_id_in_header = response { + input.parsed_path = ["allow", "structured"] + decision_id + response := { + "allowed": true, + "response_headers_to_add": { + "decision-id": decision_id + } + } + } + `, }), ) @@ -374,10 +402,24 @@ func TestAuthorizeRequestFilter(t *testing.T) { } }`, opaControlPlane.URL(), ti.regoQuery)) + envoyMetaDataConfig := []byte(`{ + "filter_metadata": { + "envoy.filters.http.header_to_metadata": { + "policy_type": "ingress" + } + } + }`) + + opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0) + opts = append(opts, + openpolicyagent.WithConfigTemplate(config), + openpolicyagent.WithEnvoyMetadataBytes(envoyMetaDataConfig)) + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) - ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, opts...) + fr.Register(ftSpec) - ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, opts...) fr.Register(ftSpec) r := eskip.MustParse(fmt.Sprintf(`* -> %s("%s", "%s") %s -> "%s"`, ti.filterName, ti.bundleName, ti.contextExtensions, ti.extraeskip, clientServer.URL)) @@ -435,8 +477,12 @@ func isHeadersPresent(t *testing.T, expectedHeaders http.Header, headers http.He if !headerFound { return false } - - assert.ElementsMatch(t, expectedValues, actualValues) + // since decision id is randomly generated we are just checking for not nil + if headerName == "Decision-Id" { + assert.NotNil(t, actualValues) + } else { + assert.ElementsMatch(t, expectedValues, actualValues) + } } return true } diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go index 117e8522bb..d6bf877940 100644 --- a/filters/openpolicyagent/openpolicyagent_test.go +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -29,7 +29,7 @@ import ( "github.com/zalando/skipper/routing" "github.com/zalando/skipper/tracing/tracingtest" "google.golang.org/protobuf/encoding/protojson" - _struct "google.golang.org/protobuf/types/known/structpb" + pbstruct "google.golang.org/protobuf/types/known/structpb" ) type MockOpenPolicyAgentFilter struct { @@ -63,22 +63,22 @@ func TestInterpolateTemplate(t *testing.T) { func TestLoadEnvoyMetadata(t *testing.T) { cfg := &OpenPolicyAgentInstanceConfig{} - WithEnvoyMetadataBytes([]byte(` + _ = WithEnvoyMetadataBytes([]byte(` { "filter_metadata": { "envoy.filters.http.header_to_metadata": { "policy_type": "ingress" - } + }, } } `))(cfg) expectedBytes, err := protojson.Marshal(&ext_authz_v3_core.Metadata{ - FilterMetadata: map[string]*_struct.Struct{ + FilterMetadata: map[string]*pbstruct.Struct{ "envoy.filters.http.header_to_metadata": { - Fields: map[string]*_struct.Value{ + Fields: map[string]*pbstruct.Value{ "policy_type": { - Kind: &_struct.Value_StringValue{StringValue: "ingress"}, + Kind: &pbstruct.Value_StringValue{StringValue: "ingress"}, }, }, }, @@ -411,7 +411,13 @@ func TestEval(t *testing.T) { span := tracer.StartSpan("open-policy-agent") ctx := opentracing.ContextWithSpan(context.Background(), span) - result, err := inst.Eval(ctx, &authv3.CheckRequest{}) + result, err := inst.Eval(ctx, &authv3.CheckRequest{ + Attributes: &authv3.AttributeContext{ + Request: nil, + ContextExtensions: nil, + MetadataContext: nil, + }, + }) assert.NoError(t, err) allowed, err := result.IsAllowed() From 83efc8a43fb57c3e018010281bc677592055525b Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 25 Jun 2024 16:49:50 +0200 Subject: [PATCH 0832/1047] config: fix defaultFiltersFlags.String (#3127) Do not store values that contaion no filters, e.g. empty strings or eskip comments only. Follow up on #3084 Signed-off-by: Alexander Yastrebov --- config/defaultfilterflags.go | 8 ++++---- config/defaultfilterflags_test.go | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/config/defaultfilterflags.go b/config/defaultfilterflags.go index 162c92f6f7..1e9d300ad9 100644 --- a/config/defaultfilterflags.go +++ b/config/defaultfilterflags.go @@ -21,10 +21,10 @@ func (dpf *defaultFiltersFlags) Set(value string) error { if err != nil { return fmt.Errorf("failed to parse default filters: %w", err) } - - dpf.values = append(dpf.values, value) - dpf.filters = append(dpf.filters, fs...) - + if len(fs) > 0 { + dpf.values = append(dpf.values, value) + dpf.filters = append(dpf.filters, fs...) + } return nil } diff --git a/config/defaultfilterflags_test.go b/config/defaultfilterflags_test.go index f1bc55004a..337b336965 100644 --- a/config/defaultfilterflags_test.go +++ b/config/defaultfilterflags_test.go @@ -60,6 +60,19 @@ field: want: manyMoreFilters, wantString: `ratelimit(5, "10s") -> tee("https://www.zalando.de/") -> inlineContent("OK\n")`, }, + { + name: "test multiple with empty filters in between", + args: []string{ + ` `, // whitespaces only + `ratelimit(5, "10s")`, + `// not a filter, just an eskip comment`, + `tee("https://www.zalando.de/")`, + ``, // empty + }, + yaml: `field: ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, + want: manyFilters, + wantString: `ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 87d5b6bc2526ef605c53cdb60bd4b5e96859a92e Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 26 Jun 2024 15:35:38 +0200 Subject: [PATCH 0833/1047] config: fix defaultFiltersFlags yaml test case (#3128) Fix yaml input to verify empty filters are ignored. Follow up on #3127 Signed-off-by: Alexander Yastrebov --- config/defaultfilterflags_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/config/defaultfilterflags_test.go b/config/defaultfilterflags_test.go index 337b336965..225bb1a47b 100644 --- a/config/defaultfilterflags_test.go +++ b/config/defaultfilterflags_test.go @@ -69,7 +69,14 @@ field: `tee("https://www.zalando.de/")`, ``, // empty }, - yaml: `field: ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, + yaml: ` +field: + - ' ' # whitespaces only + - ratelimit(5, "10s") + - // not a filter, just an eskip comment + - tee("https://www.zalando.de/") + - '' # empty +`, want: manyFilters, wantString: `ratelimit(5, "10s") -> tee("https://www.zalando.de/")`, }, From 4fee4d39b743fe343ecee2c9b42c41656e70d99a Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 26 Jun 2024 18:12:24 +0200 Subject: [PATCH 0834/1047] filters/auth: add token validator filter (#3126) The `oauthTokeninfoValidate` filter obtains token info and allows request if there was no error otherwise it responds with `401 Unauthorized` status and configured response body. It does nothing if any preceding filter already validated the token or if route is annotated with configured annotations. It is useful as a default filter to ensure each request has a valid token. Since its logic is diffrent from existing oauthTokeninfo* filters it is implemented as a separate filter type. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 29 +++++++ filters/auth/auth.go | 1 + filters/auth/tokeninfo.go | 77 ++++++++++++++++++ filters/auth/tokeninfo_test.go | 138 +++++++++++++++++++++++++++++++++ filters/filters.go | 1 + skipper.go | 1 + 6 files changed, 247 insertions(+) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index bc858894cc..026031bea0 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1343,6 +1343,35 @@ Examples: oauthTokeninfoAllKV("k1", "v1", "k2", "v2") ``` +#### oauthTokeninfoValidate + +> This filter is experimental and may change in the future, please see tests for example usage. + +The filter obtains token info and allows request if there was no error +otherwise it responds with `401 Unauthorized` status and configured response body. + +It does nothing if any preceding filter already validated the token or if route is annotated with configured annotations. + +It is useful as a default filter to ensure each request has a valid token. +[jwtMetrics](#jwtmetrics) filter may be used to discover routes serving requests without a valid token. + +The filter requires single string argument that is parsed as YAML. +For convenience use [flow style format](https://yaml.org/spec/1.2.2/#chapter-7-flow-style-productions). + +Examples: + +``` +// without opt-out annotation validates the token +oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], unauthorizedResponse: 'Authentication required, see https://auth.test/foo'}") +``` + +``` +// with opt-out annotation does not validate the token +annotate("oauth.disabled", "this endpoint is public") -> +oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], unauthorizedResponse: 'Authentication required, see https://auth.test/foo'}") +``` + + ### Tokenintrospection Tokenintrospection handled by another service. diff --git a/filters/auth/auth.go b/filters/auth/auth.go index e1b42ba50f..b138602c79 100644 --- a/filters/auth/auth.go +++ b/filters/auth/auth.go @@ -17,6 +17,7 @@ const ( checkOAuthTokeninfoAllScopes checkOAuthTokeninfoAnyKV checkOAuthTokeninfoAllKV + checkOAuthTokeninfoValidate checkOAuthTokenintrospectionAnyClaims checkOAuthTokenintrospectionAllClaims checkOAuthTokenintrospectionAnyKV diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index ee68e0e281..a38dd37f5a 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -2,11 +2,16 @@ package auth import ( "fmt" + "io" + "net/http" + "strconv" "strings" "time" + "github.com/ghodss/yaml" "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/annotate" ) const ( @@ -51,6 +56,14 @@ type ( scopes []string kv kv } + + tokeninfoValidateFilter struct { + client tokeninfoClient + config struct { + OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` + UnauthorizedResponse string `json:"unauthorizedResponse,omitempty"` + } + } ) var tokeninfoAuthClient map[string]tokeninfoClient = make(map[string]tokeninfoClient) @@ -150,6 +163,13 @@ func NewOAuthTokeninfoAnyKVWithOptions(to TokeninfoOptions) filters.Spec { } } +func NewOAuthTokeninfoValidate(to TokeninfoOptions) filters.Spec { + return &tokeninfoSpec{ + typ: checkOAuthTokeninfoValidate, + options: to, + } +} + // NewOAuthTokeninfoAnyKV creates a new auth filter specification // to validate authorization for requests. Current implementation uses // Bearer tokens to authorize requests and checks that the token @@ -192,6 +212,8 @@ func (s *tokeninfoSpec) Name() string { return filters.OAuthTokeninfoAnyKVName case checkOAuthTokeninfoAllKV: return filters.OAuthTokeninfoAllKVName + case checkOAuthTokeninfoValidate: + return filters.OAuthTokeninfoValidateName } return AuthUnknown } @@ -218,6 +240,18 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) return nil, filters.ErrInvalidFilterParameters } + if s.typ == checkOAuthTokeninfoValidate { + if len(sargs) != 1 { + return nil, fmt.Errorf("requires single string argument") + } + + f := &tokeninfoValidateFilter{client: ac} + if err := yaml.Unmarshal([]byte(sargs[0]), &f.config); err != nil { + return nil, fmt.Errorf("failed to parse configuration") + } + return f, nil + } + f := &tokeninfoFilter{typ: s.typ, client: ac, kv: make(map[string][]string)} switch f.typ { // all scopes @@ -395,3 +429,46 @@ func (f *tokeninfoFilter) Request(ctx filters.FilterContext) { } func (f *tokeninfoFilter) Response(filters.FilterContext) {} + +func (f *tokeninfoValidateFilter) Request(ctx filters.FilterContext) { + if _, ok := ctx.StateBag()[tokeninfoCacheKey]; ok { + return // tokeninfo was already validated by a preceding filter + } + + if len(f.config.OptOutAnnotations) > 0 { + annotations := annotate.GetAnnotations(ctx) + for _, annotation := range f.config.OptOutAnnotations { + if _, ok := annotations[annotation]; ok { + return // opt-out from validation + } + } + } + + token, ok := getToken(ctx.Request()) + if !ok { + f.serveUnauthorized(ctx) + return + } + + tokeninfo, err := f.client.getTokeninfo(token, ctx) + if err != nil { + f.serveUnauthorized(ctx) + return + } + + uid, _ := tokeninfo[uidKey].(string) + authorized(ctx, uid) + ctx.StateBag()[tokeninfoCacheKey] = tokeninfo +} + +func (f *tokeninfoValidateFilter) serveUnauthorized(ctx filters.FilterContext) { + ctx.Serve(&http.Response{ + StatusCode: http.StatusUnauthorized, + Header: http.Header{ + "Content-Length": []string{strconv.Itoa(len(f.config.UnauthorizedResponse))}, + }, + Body: io.NopCloser(strings.NewReader(f.config.UnauthorizedResponse)), + }) +} + +func (f *tokeninfoValidateFilter) Response(filters.FilterContext) {} diff --git a/filters/auth/tokeninfo_test.go b/filters/auth/tokeninfo_test.go index ceb5dbbb9d..5e3e93b83e 100644 --- a/filters/auth/tokeninfo_test.go +++ b/filters/auth/tokeninfo_test.go @@ -3,6 +3,7 @@ package auth import ( "encoding/json" "fmt" + "io" "net/http" "net/http/httptest" "net/url" @@ -10,9 +11,11 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/annotate" "github.com/zalando/skipper/filters/filtertest" "github.com/zalando/skipper/proxy/proxytest" ) @@ -575,3 +578,138 @@ func TestOAuthTokeninfoAllocs(t *testing.T) { t.Errorf("Expected zero allocations, got %f", allocs) } } + +func TestOAuthTokeninfoValidate(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Write([]byte("OK")) + })) + defer backend.Close() + + const validAuthHeader = "Bearer foobarbaz" + + var authRequestsTotal atomic.Int32 + testAuthServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authRequestsTotal.Add(1) + if r.Header.Get("Authorization") != validAuthHeader { + w.WriteHeader(http.StatusUnauthorized) + } else { + w.Write([]byte(`{"uid": "foobar", "scope":["foo", "bar"]}`)) + } + })) + defer testAuthServer.Close() + + tio := TokeninfoOptions{ + URL: testAuthServer.URL, + Timeout: testAuthTimeout, + } + + for _, tc := range []struct { + name string + precedingFilters string + authHeader string + expectStatus int + expectAuthRequests int32 + }{ + { + name: "reject missing token", + authHeader: "", + expectStatus: http.StatusUnauthorized, + expectAuthRequests: 0, + }, + { + name: "reject invalid token", + authHeader: "Bearer invalid", + expectStatus: http.StatusUnauthorized, + expectAuthRequests: 1, + }, + { + name: "reject invalid token type", + authHeader: "Basic foobarbaz", + expectStatus: http.StatusUnauthorized, + expectAuthRequests: 0, + }, + { + name: "reject missing token when opt-out is invalid", + precedingFilters: `annotate("oauth.invalid", "invalid opt-out")`, + authHeader: "", + expectStatus: http.StatusUnauthorized, + expectAuthRequests: 0, + }, + { + name: "allow valid token", + authHeader: validAuthHeader, + expectStatus: http.StatusOK, + expectAuthRequests: 1, + }, + { + name: "allow already validated by a preceding filter", + precedingFilters: `oauthTokeninfoAllScope("foo", "bar")`, + authHeader: validAuthHeader, + expectStatus: http.StatusOK, + expectAuthRequests: 1, // called once by oauthTokeninfoAllScope + }, + { + name: "allow missing token when opted-out", + precedingFilters: `annotate("oauth.disabled", "this endpoint is public")`, + authHeader: "", + expectStatus: http.StatusOK, + expectAuthRequests: 0, + }, + { + name: "allow invalid token when opted-out", + precedingFilters: `annotate("oauth.disabled", "this endpoint is public")`, + authHeader: "Bearer invalid", + expectStatus: http.StatusOK, + expectAuthRequests: 0, + }, + { + name: "allow invalid token type when opted-out", + precedingFilters: `annotate("oauth.disabled", "this endpoint is public")`, + authHeader: "Basic foobarbaz", + expectStatus: http.StatusOK, + expectAuthRequests: 0, + }, + } { + t.Run(tc.name, func(t *testing.T) { + fr := make(filters.Registry) + fr.Register(annotate.New()) + fr.Register(NewOAuthTokeninfoAllScopeWithOptions(tio)) + fr.Register(NewOAuthTokeninfoValidate(tio)) + + const unauthorizedResponse = `Authentication required, see https://auth.test/foo` + + filters := `oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], unauthorizedResponse: '` + unauthorizedResponse + `'}")` + if tc.precedingFilters != "" { + filters = tc.precedingFilters + " -> " + filters + } + + p := proxytest.New(fr, eskip.MustParse(fmt.Sprintf(`* -> %s -> "%s"`, filters, backend.URL))...) + defer p.Close() + + authRequestsBefore := authRequestsTotal.Load() + + req, err := http.NewRequest("GET", p.URL, nil) + require.NoError(t, err) + + if tc.authHeader != "" { + req.Header.Set(authHeaderName, tc.authHeader) + } + + resp, err := p.Client().Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, tc.expectStatus, resp.StatusCode) + assert.Equal(t, tc.expectAuthRequests, authRequestsTotal.Load()-authRequestsBefore) + + if resp.StatusCode == http.StatusUnauthorized { + assert.Equal(t, resp.ContentLength, int64(len(unauthorizedResponse))) + + b, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + assert.Equal(t, unauthorizedResponse, string(b)) + } + }) + } +} diff --git a/filters/filters.go b/filters/filters.go index 7090a0de47..612de0b723 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -287,6 +287,7 @@ const ( OAuthTokeninfoAllScopeName = "oauthTokeninfoAllScope" OAuthTokeninfoAnyKVName = "oauthTokeninfoAnyKV" OAuthTokeninfoAllKVName = "oauthTokeninfoAllKV" + OAuthTokeninfoValidateName = "oauthTokeninfoValidate" OAuthTokenintrospectionAnyClaimsName = "oauthTokenintrospectionAnyClaims" OAuthTokenintrospectionAllClaimsName = "oauthTokenintrospectionAllClaims" OAuthTokenintrospectionAnyKVName = "oauthTokenintrospectionAnyKV" diff --git a/skipper.go b/skipper.go index 619ad0f8a4..7d230c2d3c 100644 --- a/skipper.go +++ b/skipper.go @@ -1591,6 +1591,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { auth.NewOAuthTokeninfoAnyScopeWithOptions(tio), auth.NewOAuthTokeninfoAllKVWithOptions(tio), auth.NewOAuthTokeninfoAnyKVWithOptions(tio), + auth.NewOAuthTokeninfoValidate(tio), ) } From 02515fe50a088f659f8b1df0d2d396de49eb323c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 26 Jun 2024 21:23:20 +0200 Subject: [PATCH 0835/1047] metrics: register skipper_filter_create_duration_seconds (#3129) --- metrics/prometheus.go | 1 + metrics/prometheus_test.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/metrics/prometheus.go b/metrics/prometheus.go index a013d0a259..1126759317 100644 --- a/metrics/prometheus.go +++ b/metrics/prometheus.go @@ -280,6 +280,7 @@ func (p *Prometheus) registerMetrics() { p.registry.MustRegister(p.routeLookupM) p.registry.MustRegister(p.responseM) p.registry.MustRegister(p.routeErrorsM) + p.registry.MustRegister(p.filterCreateM) p.registry.MustRegister(p.filterRequestM) p.registry.MustRegister(p.filterAllRequestM) p.registry.MustRegister(p.filterAllCombinedRequestM) diff --git a/metrics/prometheus_test.go b/metrics/prometheus_test.go index 0aa6574c9e..3c4cc81dce 100644 --- a/metrics/prometheus_test.go +++ b/metrics/prometheus_test.go @@ -73,6 +73,29 @@ func TestPrometheusMetrics(t *testing.T) { }, expCode: http.StatusOK, }, + { + name: "Filter creation latency", + addMetrics: func(pm *metrics.Prometheus) { + pm.MeasureFilterCreate("filter1", time.Now().Add(-15*time.Millisecond)) + }, + expMetrics: []string{ + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.005"} 0`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.01"} 0`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.025"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.05"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.1"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.25"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="0.5"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="1"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="2.5"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="5"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="10"} 1`, + `skipper_filter_create_duration_seconds_bucket{filter="filter1",le="+Inf"} 1`, + `skipper_filter_create_duration_seconds_sum{filter="filter1"} 0.015`, + `skipper_filter_create_duration_seconds_count{filter="filter1"} 1`, + }, + expCode: http.StatusOK, + }, { name: "Measuring the filter requests should get the duration of the filter requests.", addMetrics: func(pm *metrics.Prometheus) { From 428edb8bfb8fdb16d750b9922882ae39226f8c05 Mon Sep 17 00:00:00 2001 From: Ricardo Herrera Date: Thu, 27 Jun 2024 09:07:11 -0400 Subject: [PATCH 0836/1047] cmd/skipper: allow exclusion of insecure cipher suites (#3123) Golang maintains a list of cipher suites considered insecure, which are still allowed if requested. This flag will allow those cipher suites to be completely excluded. Signed-off-by: Ricardo Herrera --- config/config.go | 20 ++++++++++++++++++++ config/config_test.go | 28 ++++++++++++++++++++++++++++ skipper.go | 7 +++++++ skipper_test.go | 7 +++++++ 4 files changed, 62 insertions(+) diff --git a/config/config.go b/config/config.go index 206f4de36f..77e3ab224c 100644 --- a/config/config.go +++ b/config/config.go @@ -219,6 +219,9 @@ type Config struct { // TLS version TLSMinVersion string `yaml:"tls-min-version"` + // Exclude insecure cipher suites + ExcludeInsecureCipherSuites bool `yaml:"exclude-insecure-cipher-suites"` + // TLS Config KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` @@ -517,6 +520,9 @@ func NewConfig() *Config { // TLS version flag.StringVar(&cfg.TLSMinVersion, "tls-min-version", defaultMinTLSVersion, "minimal TLS Version to be used in server, proxy and client connections") + // Exclude insecure cipher suites + flag.BoolVar(&cfg.ExcludeInsecureCipherSuites, "exclude-insecure-cipher-suites", false, "excludes insecure cipher suites") + // API Monitoring: flag.BoolVar(&cfg.ApiUsageMonitoringEnable, "enable-api-usage-monitoring", false, "enables the apiUsageMonitoring filter") flag.StringVar(&cfg.ApiUsageMonitoringRealmKeys, "api-usage-monitoring-realm-keys", "", "name of the property in the JWT payload that contains the authority realm") @@ -715,6 +721,7 @@ func (c *Config) ToOptions() skipper.Options { DebugListener: c.DebugListener, CertPathTLS: c.CertPathTLS, KeyPathTLS: c.KeyPathTLS, + CipherSuites: c.filterCipherSuites(), MaxLoopbacks: c.MaxLoopbacks, DefaultHTTPStatus: c.DefaultHTTPStatus, ReverseSourcePredicate: c.ReverseSourcePredicate, @@ -1031,6 +1038,19 @@ func (c *Config) getMinTLSVersion() uint16 { return tlsVersionTable[defaultMinTLSVersion] } +func (c *Config) filterCipherSuites() []uint16 { + if !c.ExcludeInsecureCipherSuites { + return nil + } + + cipherSuites := make([]uint16, 0) + for _, suite := range tls.CipherSuites() { + cipherSuites = append(cipherSuites, suite.ID) + } + + return cipherSuites +} + func (c *Config) parseHistogramBuckets() ([]float64, error) { if c.HistogramMetricBucketsString == "" { return prometheus.DefBuckets, nil diff --git a/config/config_test.go b/config/config_test.go index 6ce6c76cb3..f117546ea0 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -5,6 +5,7 @@ import ( "errors" "os" "reflect" + "slices" "strings" "testing" "time" @@ -174,6 +175,7 @@ func TestToOptions(t *testing.T) { c.ProxyPreserveHost = true // 4 c.RemoveHopHeaders = true // 16 c.RfcPatchPath = true // 32 + c.ExcludeInsecureCipherSuites = true // config c.EtcdUrls = "127.0.0.1:5555" @@ -231,6 +233,15 @@ func TestToOptions(t *testing.T) { if len(opt.EditRoute) != 1 { t.Errorf("Failed to get expected edit route: %s", c.EditRoute) } + if opt.CipherSuites == nil { + t.Errorf("Failed to get the filtered cipher suites") + } else { + for _, i := range tls.InsecureCipherSuites() { + if slices.Contains(opt.CipherSuites, i.ID) { + t.Errorf("Insecure cipher found in list: %s", i.Name) + } + } + } } func Test_Validate(t *testing.T) { @@ -405,6 +416,23 @@ func TestMinTLSVersion(t *testing.T) { }) } +func TestExcludeInsecureCipherSuites(t *testing.T) { + + t.Run("test default", func(t *testing.T) { + cfg := new(Config) + if cfg.filterCipherSuites() != nil { + t.Error("No cipher suites should be filtered by default") + } + }) + t.Run("test excluded insecure cipher suites", func(t *testing.T) { + cfg := new(Config) + cfg.ExcludeInsecureCipherSuites = true + if cfg.filterCipherSuites() == nil { + t.Error(`Failed to get list of filtered cipher suites`) + } + }) +} + type testFormatter struct { messages map[string]log.Level } diff --git a/skipper.go b/skipper.go index 7d230c2d3c..62ad7a2415 100644 --- a/skipper.go +++ b/skipper.go @@ -614,6 +614,9 @@ type Options struct { // TLSMinVersion to set the minimal TLS version for all TLS configurations TLSMinVersion uint16 + // CipherSuites sets the list of cipher suites to use for TLS 1.2 + CipherSuites []uint16 + // Flush interval for upgraded Proxy connections BackendFlushInterval time.Duration @@ -1176,6 +1179,10 @@ func (o *Options) tlsConfig(cr *certregistry.CertRegistry) (*tls.Config, error) MinVersion: o.TLSMinVersion, } + if o.CipherSuites != nil { + config.CipherSuites = o.CipherSuites + } + if cr != nil { config.GetCertificate = cr.GetCertFromHello } diff --git a/skipper_test.go b/skipper_test.go index 3a6c275997..a34cc88e26 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -186,6 +186,13 @@ func TestOptionsTLSConfig(t *testing.T) { require.NoError(t, err) require.Equal(t, uint16(tls.VersionTLS13), c.MinVersion) require.Equal(t, []tls.Certificate{cert, cert2}, c.Certificates) + + // TLS Cipher Suites + o = &Options{CipherSuites: []uint16{1}} + c, err = o.tlsConfig(cr) + require.NoError(t, err) + assert.Equal(t, len(c.CipherSuites), 1) + } func TestOptionsTLSConfigInvalidPaths(t *testing.T) { From 258fe2927c6fecccc2e6c178d71e825939fea895 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 28 Jun 2024 11:28:06 +0200 Subject: [PATCH 0837/1047] metrics: refactor prometheus metric registration (#3132) Register prometheus metrics right after creation to reduce likelihood of missing registration. Follow up on #3129 Signed-off-by: Alexander Yastrebov --- metrics/prometheus.go | 165 +++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 105 deletions(-) diff --git a/metrics/prometheus.go b/metrics/prometheus.go index 1126759317..929e8570be 100644 --- a/metrics/prometheus.go +++ b/metrics/prometheus.go @@ -59,105 +59,114 @@ type Prometheus struct { func NewPrometheus(opts Options) *Prometheus { opts = applyCompatibilityDefaults(opts) + p := &Prometheus{ + registry: opts.PrometheusRegistry, + opts: opts, + } + + if p.registry == nil { + p.registry = prometheus.NewRegistry() + } + namespace := promNamespace if opts.Prefix != "" { namespace = strings.TrimSuffix(opts.Prefix, ".") } - routeLookup := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.routeLookupM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promRouteSubsystem, Name: "lookup_duration_seconds", Help: "Duration in seconds of a route lookup.", Buckets: opts.HistogramBuckets, - }, []string{}) + }, []string{})) - routeErrors := prometheus.NewCounterVec(prometheus.CounterOpts{ + p.routeErrorsM = register(p, prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: promRouteSubsystem, Name: "error_total", Help: "The total of route lookup errors.", - }, []string{}) + }, []string{})) - response := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.responseM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promResponseSubsystem, Name: "duration_seconds", Help: "Duration in seconds of a response.", Buckets: opts.HistogramBuckets, - }, []string{"code", "method", "route"}) + }, []string{"code", "method", "route"})) - filterCreate := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterCreateM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "create_duration_seconds", Help: "Duration in seconds of filter creation.", Buckets: opts.HistogramBuckets, - }, []string{"filter"}) + }, []string{"filter"})) - filterRequest := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterRequestM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "request_duration_seconds", Help: "Duration in seconds of a filter request.", Buckets: opts.HistogramBuckets, - }, []string{"filter"}) + }, []string{"filter"})) - filterAllRequest := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterAllRequestM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "all_request_duration_seconds", Help: "Duration in seconds of a filter request by all filters.", Buckets: opts.HistogramBuckets, - }, []string{"route"}) + }, []string{"route"})) - filterAllCombinedRequest := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterAllCombinedRequestM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "all_combined_request_duration_seconds", Help: "Duration in seconds of a filter request combined by all filters.", Buckets: opts.HistogramBuckets, - }, []string{}) + }, []string{})) - proxyBackend := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.proxyBackendM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promProxySubsystem, Name: "duration_seconds", Help: "Duration in seconds of a proxy backend.", Buckets: opts.HistogramBuckets, - }, []string{"route", "host"}) + }, []string{"route", "host"})) - proxyBackendCombined := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.proxyBackendCombinedM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promProxySubsystem, Name: "combined_duration_seconds", Help: "Duration in seconds of a proxy backend combined.", Buckets: opts.HistogramBuckets, - }, []string{}) + }, []string{})) - filterResponse := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterResponseM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "response_duration_seconds", Help: "Duration in seconds of a filter request.", Buckets: opts.HistogramBuckets, - }, []string{"filter"}) + }, []string{"filter"})) - filterAllResponse := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterAllResponseM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "all_response_duration_seconds", Help: "Duration in seconds of a filter response by all filters.", Buckets: opts.HistogramBuckets, - }, []string{"route"}) + }, []string{"route"})) - filterAllCombinedResponse := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.filterAllCombinedResponseM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promFilterSubsystem, Name: "all_combined_response_duration_seconds", Help: "Duration in seconds of a filter response combined by all filters.", Buckets: opts.HistogramBuckets, - }, []string{}) + }, []string{})) metrics := []string{} if opts.EnableServeStatusCodeMetric { @@ -166,108 +175,80 @@ func NewPrometheus(opts Options) *Prometheus { if opts.EnableServeMethodMetric { metrics = append(metrics, "method") } - serveRoute := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.serveRouteM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promServeSubsystem, Name: "route_duration_seconds", Help: "Duration in seconds of serving a route.", Buckets: opts.HistogramBuckets, - }, append(metrics, "route")) - serveRouteCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ + }, append(metrics, "route"))) + p.serveRouteCounterM = register(p, prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: promServeSubsystem, Name: "route_count", Help: "Total number of requests of serving a route.", - }, []string{"code", "method", "route"}) + }, []string{"code", "method", "route"})) - serveHost := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.serveHostM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promServeSubsystem, Name: "host_duration_seconds", Help: "Duration in seconds of serving a host.", Buckets: opts.HistogramBuckets, - }, append(metrics, "host")) - serveHostCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ + }, append(metrics, "host"))) + p.serveHostCounterM = register(p, prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: promServeSubsystem, Name: "host_count", Help: "Total number of requests of serving a host.", - }, []string{"code", "method", "host"}) + }, []string{"code", "method", "host"})) - proxyBackend5xx := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + p.proxyBackend5xxM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promProxySubsystem, Name: "5xx_duration_seconds", Help: "Duration in seconds of backend 5xx.", Buckets: opts.HistogramBuckets, - }, []string{}) - proxyBackendErrors := prometheus.NewCounterVec(prometheus.CounterOpts{ + }, []string{})) + p.proxyBackendErrorsM = register(p, prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: promProxySubsystem, Name: "error_total", Help: "Total number of backend route errors.", - }, []string{"route"}) - proxyStreamingErrors := prometheus.NewCounterVec(prometheus.CounterOpts{ + }, []string{"route"})) + p.proxyStreamingErrorsM = register(p, prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: promStreamingSubsystem, Name: "error_total", Help: "Total number of streaming route errors.", - }, []string{"route"}) + }, []string{"route"})) - customCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ + p.customCounterM = register(p, prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: promCustomSubsystem, Name: "total", Help: "Total number of custom metrics.", - }, []string{"key"}) - customGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{ + }, []string{"key"})) + p.customGaugeM = register(p, prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: promCustomSubsystem, Name: "gauges", Help: "Gauges number of custom metrics.", - }, []string{"key"}) - customHistogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + }, []string{"key"})) + p.customHistogramM = register(p, prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: promCustomSubsystem, Name: "duration_seconds", Help: "Duration in seconds of custom metrics.", Buckets: opts.HistogramBuckets, - }, []string{"key"}) - - p := &Prometheus{ - routeLookupM: routeLookup, - routeErrorsM: routeErrors, - responseM: response, - filterCreateM: filterCreate, - filterRequestM: filterRequest, - filterAllRequestM: filterAllRequest, - filterAllCombinedRequestM: filterAllCombinedRequest, - proxyBackendM: proxyBackend, - proxyBackendCombinedM: proxyBackendCombined, - filterResponseM: filterResponse, - filterAllResponseM: filterAllResponse, - filterAllCombinedResponseM: filterAllCombinedResponse, - serveRouteM: serveRoute, - serveRouteCounterM: serveRouteCounter, - serveHostM: serveHost, - serveHostCounterM: serveHostCounter, - proxyBackend5xxM: proxyBackend5xx, - proxyBackendErrorsM: proxyBackendErrors, - proxyStreamingErrorsM: proxyStreamingErrors, - customCounterM: customCounter, - customGaugeM: customGauge, - customHistogramM: customHistogram, + }, []string{"key"})) - registry: opts.PrometheusRegistry, - opts: opts, - } - - if p.registry == nil { - p.registry = prometheus.NewRegistry() + // Register prometheus runtime collectors if required. + if opts.EnableRuntimeMetrics { + register(p, collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) + register(p, collectors.NewGoCollector()) } - // Register all metrics. - p.registerMetrics() return p } @@ -276,35 +257,9 @@ func (p *Prometheus) sinceS(start time.Time) float64 { return time.Since(start).Seconds() } -func (p *Prometheus) registerMetrics() { - p.registry.MustRegister(p.routeLookupM) - p.registry.MustRegister(p.responseM) - p.registry.MustRegister(p.routeErrorsM) - p.registry.MustRegister(p.filterCreateM) - p.registry.MustRegister(p.filterRequestM) - p.registry.MustRegister(p.filterAllRequestM) - p.registry.MustRegister(p.filterAllCombinedRequestM) - p.registry.MustRegister(p.proxyBackendM) - p.registry.MustRegister(p.proxyBackendCombinedM) - p.registry.MustRegister(p.filterResponseM) - p.registry.MustRegister(p.filterAllResponseM) - p.registry.MustRegister(p.filterAllCombinedResponseM) - p.registry.MustRegister(p.serveRouteM) - p.registry.MustRegister(p.serveRouteCounterM) - p.registry.MustRegister(p.serveHostM) - p.registry.MustRegister(p.serveHostCounterM) - p.registry.MustRegister(p.proxyBackend5xxM) - p.registry.MustRegister(p.proxyBackendErrorsM) - p.registry.MustRegister(p.proxyStreamingErrorsM) - p.registry.MustRegister(p.customCounterM) - p.registry.MustRegister(p.customHistogramM) - p.registry.MustRegister(p.customGaugeM) - - // Register prometheus runtime collectors if required. - if p.opts.EnableRuntimeMetrics { - p.registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) - p.registry.MustRegister(collectors.NewGoCollector()) - } +func register[T prometheus.Collector](p *Prometheus, cs T) T { + p.registry.MustRegister(cs) + return cs } func (p *Prometheus) CreateHandler() http.Handler { From b6f8424499601f891f5a18d62ab65155d1fe0107 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:09:55 +0200 Subject: [PATCH 0838/1047] build(deps): bump amazonlinux from `b0016cb` to `5bf7910` in /fuzz (#3133) Bumps amazonlinux from `b0016cb` to `5bf7910`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index f7f1ec878b..345fa24503 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:b0016cbc423cd29bca07d5ff783c432c7f0477dc8d19bdf5828dd560a0b3d0b9 +FROM amazonlinux:2023@sha256:5bf791027b4659e73c33a88a3fa2b314b8e2c0ee60cb1088a097171ee7f180db WORKDIR /workspace From 56f6f585ab1d4f8fd818f21e593c1ad699a3b7b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:10:34 +0200 Subject: [PATCH 0839/1047] build(deps): bump github.com/open-policy-agent/opa from 0.65.0 to 0.66.0 (#3135) Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.65.0 to 0.66.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.65.0...v0.66.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 5 +++-- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2d0680cd34..f6916d1fdf 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.61 github.com/oklog/ulid v1.3.1 - github.com/open-policy-agent/opa v0.65.0 + github.com/open-policy-agent/opa v0.66.0 github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 @@ -79,7 +79,8 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 // indirect - github.com/containerd/containerd v1.7.17 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/go.sum b/go.sum index ea63936d0c..fd5aa7b367 100644 --- a/go.sum +++ b/go.sum @@ -76,10 +76,12 @@ github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipa github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.17 h1:KjNnn0+tAVQHAoaWRjmdak9WlvnFR/8rU1CHHy8Rm2A= -github.com/containerd/containerd v1.7.17/go.mod h1:vK+hhT4TIv2uejlcDlbVIc8+h/BqtKLIyNrtCZol8lI= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= +github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -336,8 +338,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/open-policy-agent/opa v0.65.0 h1:wnEU0pEk80YjFi3yoDbFTMluyNssgPI4VJNJetD9a4U= -github.com/open-policy-agent/opa v0.65.0/go.mod h1:CNoLL44LuCH1Yot/zoeZXRKFylQtCJV+oGFiP2TeeEc= +github.com/open-policy-agent/opa v0.66.0 h1:DbrvfJQja0FBRcPOB3Z/BOckocN+M4ApNWyNhSRJt0w= +github.com/open-policy-agent/opa v0.66.0/go.mod h1:EIgNnJcol7AvQR/IcWLwL13k64gHVbNAVG46b2G+/EY= github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy h1:HN/mBZeRpcNUmcOVZddxW0lhtQTagiLTPxf+mszXI1s= github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy/go.mod h1:K7QVzQXxhv0S0PkDwazG/NZN6Jtaq0jQqfW0fftjPBU= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= From 9d940dde51938502508abf945f82add1a2501959 Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Mon, 1 Jul 2024 11:30:51 +0200 Subject: [PATCH 0840/1047] Group dependabot GoLang dependencies update (#3136) Signed-off-by: Mustafa Abdelrahman --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8e1fb8fa3c..d9f3ef9062 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,10 @@ updates: directory: "/" schedule: interval: "weekly" + groups: + all-go-mod-patch-and-minor: + patterns: ["*"] + update-types: ["patch", "minor"] - package-ecosystem: "github-actions" directory: "/" # For GitHub Actions, set the directory to / to check for workflow files in .github/workflows schedule: From d55bbf41f4c0ea1657c14827e999665022002391 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:52:33 +0200 Subject: [PATCH 0841/1047] build(deps): bump docker/build-push-action from 6.1.0 to 6.2.0 (#3134) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/31159d49c0d4756269a0940a750801a1ea5d7003...15560696de535e4014efeff63c48f16952e52dd1) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 4dc13ef424..106580b033 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 + uses: docker/build-push-action@15560696de535e4014efeff63c48f16952e52dd1 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 44fd905b0944da8ed1143eacd33c00f142acdeb5 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 2 Jul 2024 11:29:18 +0200 Subject: [PATCH 0842/1047] Revert "Facilitate OPA decision correlation with business flows (#3041)" (#3138) This reverts commit 2a623a940a622517b4cd489c24815e5e7b86b578. The code panics with: ``` fatal error: concurrent map writes goroutine 191993 [running]: github.com/zalando/skipper/filters/openpolicyagent.setDecisionIdInRequest(0x4004494720, {0x40067c6440?, 0x10?}) github.com/zalando/skipper/filters/openpolicyagent/evaluation.go:92 +0xe4 github.com/zalando/skipper/filters/openpolicyagent.(*OpenPolicyAgentInstance).Eval(0x40059ef400, {0x16b6bb0, 0x40044946c0}, 0x4004494720) github.com/zalando/skipper/filters/openpolicyagent/evaluation.go:27 +0x164 github.com/zalando/skipper/filters/openpolicyagent/opaauthorizerequest.(*opaAuthorizeRequestFilter).Request(0x4002234520, {0x16ccd90, 0x40091ca100}) github.com/zalando/skipper/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go:131 +0x1a4 ``` Signed-off-by: Alexander Yastrebov --- docs/tutorials/auth.md | 9 --- filters/openpolicyagent/evaluation.go | 34 +---------- .../opaauthorizerequest_test.go | 58 ++----------------- .../openpolicyagent/openpolicyagent_test.go | 20 +++---- 4 files changed, 15 insertions(+), 106 deletions(-) diff --git a/docs/tutorials/auth.md b/docs/tutorials/auth.md index 0ac9363792..14bf04e1f7 100644 --- a/docs/tutorials/auth.md +++ b/docs/tutorials/auth.md @@ -466,15 +466,6 @@ The second argument is parsed as YAML, cannot be nested and values need to be st In Rego this can be used like this `input.attributes.contextExtensions["com.mycompany.myprop"] == "my value"` -### Decision ID in Policies - -Each evaluation yields a distinct decision, identifiable by its unique decision ID. -This decision ID can be located within the input at: - -`input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id` - -Typical use cases are either propagation of the decision ID to downstream systems or returning it as part of the response. As an example this can allow to trouble shoot deny requests by looking up details using the full decision in a control plane. - ### Quick Start Rego Playground A quick way without setting up Backend APIs is to use the [Rego Playground](https://play.openpolicyagent.org/). diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go index ccdc38c30e..03f56b450a 100644 --- a/filters/openpolicyagent/evaluation.go +++ b/filters/openpolicyagent/evaluation.go @@ -3,7 +3,8 @@ package openpolicyagent import ( "context" "fmt" - ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + "time" + ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" "github.com/open-policy-agent/opa-envoy-plugin/opa/decisionlog" @@ -12,8 +13,6 @@ import ( "github.com/open-policy-agent/opa/server" "github.com/open-policy-agent/opa/topdown" "github.com/opentracing/opentracing-go" - pbstruct "google.golang.org/protobuf/types/known/structpb" - "time" ) func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3.CheckRequest) (*envoyauth.EvalResult, error) { @@ -24,12 +23,6 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return nil, err } - err = setDecisionIdInRequest(req, decisionId) - if err != nil { - opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to set decision ID in Request.") - return nil, err - } - result, stopeval, err := envoyauth.NewEvalResult(withDecisionID(decisionId)) if err != nil { opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to generate new result with decision ID.") @@ -78,29 +71,6 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return result, nil } -func setDecisionIdInRequest(req *ext_authz_v3.CheckRequest, decisionId string) error { - if req.Attributes.MetadataContext == nil { - req.Attributes.MetadataContext = &ext_authz_v3_core.Metadata{ - FilterMetadata: map[string]*pbstruct.Struct{}, - } - } - - filterMeta, err := formOpenPolicyAgentMetaDataObject(decisionId) - if err != nil { - return err - } - req.Attributes.MetadataContext.FilterMetadata["open_policy_agent"] = filterMeta - return nil -} - -func formOpenPolicyAgentMetaDataObject(decisionId string) (*pbstruct.Struct, error) { - - innerFields := make(map[string]interface{}) - innerFields["decision_id"] = decisionId - - return pbstruct.NewStruct(innerFields) -} - func (opa *OpenPolicyAgentInstance) logDecision(ctx context.Context, input interface{}, result *envoyauth.EvalResult, err error) error { info := &server.Info{ Timestamp: time.Now(), diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 4dff05ecfa..1e48c63c8f 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -255,21 +255,6 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, - { - msg: "Decision id in request header", - filterName: "opaAuthorizeRequest", - bundleName: "somebundle.tar.gz", - regoQuery: "envoy/authz/allow_object_decision_id_in_header", - requestMethod: "POST", - body: `{ "target_id" : "123456" }`, - requestHeaders: map[string][]string{"content-type": {"application/json"}}, - requestPath: "/allow/structured", - expectedStatus: http.StatusOK, - expectedBody: "Welcome!", - expectedHeaders: map[string][]string{"Decision-Id": {"some-random-decision-id-generated-during-evaluation"}}, - backendHeaders: make(http.Header), - removeHeaders: make(http.Header), - }, { msg: "Invalid UTF-8 in Path", filterName: "opaAuthorizeRequest", @@ -360,21 +345,8 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow_body { input.parsed_body.target_id == "123456" - } - - decision_id := input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id - - allow_object_decision_id_in_header = response { - input.parsed_path = ["allow", "structured"] - decision_id - response := { - "allowed": true, - "response_headers_to_add": { - "decision-id": decision_id - } - } - } - `, + } + `, }), ) @@ -402,24 +374,10 @@ func TestAuthorizeRequestFilter(t *testing.T) { } }`, opaControlPlane.URL(), ti.regoQuery)) - envoyMetaDataConfig := []byte(`{ - "filter_metadata": { - "envoy.filters.http.header_to_metadata": { - "policy_type": "ingress" - } - } - }`) - - opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0) - opts = append(opts, - openpolicyagent.WithConfigTemplate(config), - openpolicyagent.WithEnvoyMetadataBytes(envoyMetaDataConfig)) - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) - ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, opts...) - + ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) fr.Register(ftSpec) - ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, opts...) + ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) fr.Register(ftSpec) r := eskip.MustParse(fmt.Sprintf(`* -> %s("%s", "%s") %s -> "%s"`, ti.filterName, ti.bundleName, ti.contextExtensions, ti.extraeskip, clientServer.URL)) @@ -477,12 +435,8 @@ func isHeadersPresent(t *testing.T, expectedHeaders http.Header, headers http.He if !headerFound { return false } - // since decision id is randomly generated we are just checking for not nil - if headerName == "Decision-Id" { - assert.NotNil(t, actualValues) - } else { - assert.ElementsMatch(t, expectedValues, actualValues) - } + + assert.ElementsMatch(t, expectedValues, actualValues) } return true } diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go index d6bf877940..117e8522bb 100644 --- a/filters/openpolicyagent/openpolicyagent_test.go +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -29,7 +29,7 @@ import ( "github.com/zalando/skipper/routing" "github.com/zalando/skipper/tracing/tracingtest" "google.golang.org/protobuf/encoding/protojson" - pbstruct "google.golang.org/protobuf/types/known/structpb" + _struct "google.golang.org/protobuf/types/known/structpb" ) type MockOpenPolicyAgentFilter struct { @@ -63,22 +63,22 @@ func TestInterpolateTemplate(t *testing.T) { func TestLoadEnvoyMetadata(t *testing.T) { cfg := &OpenPolicyAgentInstanceConfig{} - _ = WithEnvoyMetadataBytes([]byte(` + WithEnvoyMetadataBytes([]byte(` { "filter_metadata": { "envoy.filters.http.header_to_metadata": { "policy_type": "ingress" - }, + } } } `))(cfg) expectedBytes, err := protojson.Marshal(&ext_authz_v3_core.Metadata{ - FilterMetadata: map[string]*pbstruct.Struct{ + FilterMetadata: map[string]*_struct.Struct{ "envoy.filters.http.header_to_metadata": { - Fields: map[string]*pbstruct.Value{ + Fields: map[string]*_struct.Value{ "policy_type": { - Kind: &pbstruct.Value_StringValue{StringValue: "ingress"}, + Kind: &_struct.Value_StringValue{StringValue: "ingress"}, }, }, }, @@ -411,13 +411,7 @@ func TestEval(t *testing.T) { span := tracer.StartSpan("open-policy-agent") ctx := opentracing.ContextWithSpan(context.Background(), span) - result, err := inst.Eval(ctx, &authv3.CheckRequest{ - Attributes: &authv3.AttributeContext{ - Request: nil, - ContextExtensions: nil, - MetadataContext: nil, - }, - }) + result, err := inst.Eval(ctx, &authv3.CheckRequest{}) assert.NoError(t, err) allowed, err := result.IsAllowed() From efd26545933373a717250b004cb99da58e2e8cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 3 Jul 2024 22:23:43 +0200 Subject: [PATCH 0843/1047] fix: remove polyfill.io js (#3141) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref: https://www.bleepingcomputer.com/news/security/polyfillio-javascript-supply-chain-attack-impacts-over-100k-sites/ was introduced by a0fa5d8e99117d5a43b84c4948cf819c16715efe "enable math rendering" (#2041) Signed-off-by: Sandor Szücs --- mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 466135bc80..a3ad6d20e4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -75,7 +75,6 @@ markdown_extensions: class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format extra_javascript: - - https://polyfill.io/v3/polyfill.min.js?features=es6 - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js plugins: From 5b72eb2f7fae74c1fd54c5ebf298e26c4e250c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Thu, 4 Jul 2024 16:33:19 +0200 Subject: [PATCH 0844/1047] docs: running as ingress backend (#3142) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit you can also use the newer lifecycle hook target "sleep" to let kubelet sleep for you, so no need to have a binary inside your container Signed-off-by: Sandor Szücs --- docs/kubernetes/ingress-backends.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/kubernetes/ingress-backends.md b/docs/kubernetes/ingress-backends.md index 74020c14e9..1e46401bae 100644 --- a/docs/kubernetes/ingress-backends.md +++ b/docs/kubernetes/ingress-backends.md @@ -42,6 +42,16 @@ lifecycle: command: ["sleep","20"] ``` +or use sleep in kubelet without the need of the binary `sleep` in your +container: + +```yaml +lifecycle: + preStop: + sleep: + seconds: 20 +``` + 20 seconds should be enough to fade your Pod out of the endpoints list and Skipper's routing table. From 5b66d1214dfd4a6f738e9720287b8cbf5ed6ff80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:23:00 +0200 Subject: [PATCH 0845/1047] build(deps): bump the all-go-mod-patch-and-minor group with 5 updates (#3148) Bumps the all-go-mod-patch-and-minor group with 5 updates: | Package | From | To | | --- | --- | --- | | [github.com/open-policy-agent/opa-envoy-plugin](https://github.com/open-policy-agent/opa-envoy-plugin) | `0.65.0-envoy` | `0.66.0-envoy-2` | | [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) | `0.31.0` | `0.32.0` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.24.0` | `0.25.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.26.0` | `0.27.0` | | [golang.org/x/term](https://github.com/golang/term) | `0.21.0` | `0.22.0` | Updates `github.com/open-policy-agent/opa-envoy-plugin` from 0.65.0-envoy to 0.66.0-envoy-2 - [Release notes](https://github.com/open-policy-agent/opa-envoy-plugin/releases) - [Changelog](https://github.com/open-policy-agent/opa-envoy-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa-envoy-plugin/compare/v0.65.0-envoy...v0.66.0-envoy-2) Updates `github.com/testcontainers/testcontainers-go` from 0.31.0 to 0.32.0 - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.31.0...v0.32.0) Updates `golang.org/x/crypto` from 0.24.0 to 0.25.0 - [Commits](https://github.com/golang/crypto/compare/v0.24.0...v0.25.0) Updates `golang.org/x/net` from 0.26.0 to 0.27.0 - [Commits](https://github.com/golang/net/compare/v0.26.0...v0.27.0) Updates `golang.org/x/term` from 0.21.0 to 0.22.0 - [Commits](https://github.com/golang/term/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa-envoy-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 19 ++++++++++--------- go.sum | 42 ++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index f6916d1fdf..1a17cc7512 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/miekg/dns v1.1.61 github.com/oklog/ulid v1.3.1 github.com/open-policy-agent/opa v0.66.0 - github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy + github.com/open-policy-agent/opa-envoy-plugin v0.66.0-envoy-2 github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.19.1 @@ -40,7 +40,7 @@ require ( github.com/sony/gobreaker v1.0.0 github.com/stretchr/testify v1.9.0 github.com/szuecs/rate-limit-buffer v0.9.0 - github.com/testcontainers/testcontainers-go v0.31.0 + github.com/testcontainers/testcontainers-go v0.32.0 github.com/tidwall/gjson v1.17.1 github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible @@ -48,12 +48,12 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.24.0 + golang.org/x/crypto v0.25.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.26.0 + golang.org/x/net v0.27.0 golang.org/x/oauth2 v0.21.0 golang.org/x/sync v0.7.0 - golang.org/x/term v0.21.0 + golang.org/x/term v0.22.0 golang.org/x/time v0.5.0 google.golang.org/protobuf v1.34.2 gopkg.in/go-jose/go-jose.v2 v2.6.3 @@ -87,8 +87,8 @@ require ( github.com/dgraph-io/badger/v3 v3.2103.5 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/docker/docker v25.0.5+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.0.3+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect @@ -117,13 +117,14 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/influxdata/tdigest v0.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect github.com/looplab/fsm v1.0.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect @@ -167,7 +168,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/mod v0.18.0 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/tools v0.22.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect diff --git a/go.sum b/go.sum index fd5aa7b367..d1385fa5ed 100644 --- a/go.sum +++ b/go.sum @@ -120,10 +120,10 @@ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+ github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE= -github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE= +github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -268,8 +268,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -301,6 +301,8 @@ github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= @@ -340,8 +342,8 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/open-policy-agent/opa v0.66.0 h1:DbrvfJQja0FBRcPOB3Z/BOckocN+M4ApNWyNhSRJt0w= github.com/open-policy-agent/opa v0.66.0/go.mod h1:EIgNnJcol7AvQR/IcWLwL13k64gHVbNAVG46b2G+/EY= -github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy h1:HN/mBZeRpcNUmcOVZddxW0lhtQTagiLTPxf+mszXI1s= -github.com/open-policy-agent/opa-envoy-plugin v0.65.0-envoy/go.mod h1:K7QVzQXxhv0S0PkDwazG/NZN6Jtaq0jQqfW0fftjPBU= +github.com/open-policy-agent/opa-envoy-plugin v0.66.0-envoy-2 h1:lkCFy03fL55INkwlYSisZyw7X00nJIswWl94a2RB9gw= +github.com/open-policy-agent/opa-envoy-plugin v0.66.0-envoy-2/go.mod h1:pi1KbfVr86cnYzgxeMD7GwRHLL3ZJNNW2U1M6fwy4xg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -449,8 +451,8 @@ github.com/szuecs/rate-limit-buffer v0.9.0 h1:65fBCVsaJFh0E1G5C6/sInEPlYR6dXtF9J github.com/szuecs/rate-limit-buffer v0.9.0/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U= -github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI= +github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= +github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -520,8 +522,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -559,8 +561,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -601,10 +603,10 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -694,8 +696,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= -gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= -gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0= From 0fdb5625f0a652fc630f306573fcf5ad622aeac9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:23:17 +0200 Subject: [PATCH 0846/1047] build(deps): bump amazonlinux from `5bf7910` to `d96fde2` in /fuzz (#3143) Bumps amazonlinux from `5bf7910` to `d96fde2`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 345fa24503..a271decfc4 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:5bf791027b4659e73c33a88a3fa2b314b8e2c0ee60cb1088a097171ee7f180db +FROM amazonlinux:2023@sha256:d96fde261b48b15e22c28c2eb54d087d39bcc19af7963d86539f18eea16b283c WORKDIR /workspace From e7035ed188eecd24f200328394fa8a92a07c6c5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:23:34 +0200 Subject: [PATCH 0847/1047] build(deps): bump actions/upload-artifact from 4.3.3 to 4.3.4 (#3144) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.3 to 4.3.4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/65462800fd760344b1a7b4382951275a0abb4808...0b2256b8c012f0828dc542b3febcab082c67f72b) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 3922825659..ba7060836c 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 with: name: SARIF file path: results.sarif From e83402dc3b024dbaa6dd2778275e67825d2828aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:23:50 +0200 Subject: [PATCH 0848/1047] build(deps): bump docker/setup-buildx-action from 3.3.0 to 3.4.0 (#3145) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/d70bba72b1f3fd22344832f00baa16ece964efeb...4fd812986e6c8c2a69e18311145f9371337f27d4) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 106580b033..4719be4d52 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb + uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From 793766cf9fd9c25c6a672439561cb3f446dd6989 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:24:46 +0200 Subject: [PATCH 0849/1047] build(deps): bump docker/build-push-action from 6.2.0 to 6.3.0 (#3147) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/15560696de535e4014efeff63c48f16952e52dd1...1a162644f9a7e87d8f4b053101d1d9a712edc18c) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 4719be4d52..af8de60eb6 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@15560696de535e4014efeff63c48f16952e52dd1 + uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 16d8ccd21672f7fd24cee50c44db20bf420f7627 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 12:19:24 +0200 Subject: [PATCH 0850/1047] build(deps): bump docker/setup-qemu-action from 3.0.0 to 3.1.0 (#3146) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/68827325e0b33c7199eb31dd4e31fbe9023e06e3...5927c834f5b4fdf503fca6f4c7eccda82949e1ee) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index af8de60eb6..804c27e8d2 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -47,7 +47,7 @@ jobs: make build.linux - name: Set up QEMU - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 + uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee - name: Set up Docker Buildx uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 From b7825a336ab70409963fea03b74136632d49469e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:02:16 +0200 Subject: [PATCH 0851/1047] build(deps): bump google.golang.org/grpc in the go_modules group (#3149) Bumps the go_modules group with 1 update: [google.golang.org/grpc](https://github.com/grpc/grpc-go). Updates `google.golang.org/grpc` from 1.64.0 to 1.64.1 - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.64.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1a17cc7512..83597520db 100644 --- a/go.mod +++ b/go.mod @@ -174,7 +174,7 @@ require ( gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/grpc v1.64.0 // indirect + google.golang.org/grpc v1.64.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.3.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index d1385fa5ed..3b07de957a 100644 --- a/go.sum +++ b/go.sum @@ -661,8 +661,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= 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= From d4f71007f1db99359a2def7f97d3f0bcaf1a17db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:34:37 +0200 Subject: [PATCH 0852/1047] build(deps): bump amazonlinux from `d96fde2` to `d5b9690` in /fuzz (#3154) Bumps amazonlinux from `d96fde2` to `d5b9690`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index a271decfc4..6002e9f62e 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:d96fde261b48b15e22c28c2eb54d087d39bcc19af7963d86539f18eea16b283c +FROM amazonlinux:2023@sha256:d5b9690df4ce1a70d738d8c8f7a6b4275008df95d4ddc2435e4e8e4352db61a5 WORKDIR /workspace From cfa15824a51ffdf31b0c23ff5b119ed8426a3548 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:35:33 +0200 Subject: [PATCH 0853/1047] build(deps): bump actions/setup-python from 5.1.0 to 5.1.1 (#3152) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/82c7e631bb3cdc910f68e0081d67478d79c6982d...39cd14951b08e74b54015e9e001cdefcf80e669f) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index f27f86e5a1..a637946932 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f with: python-version: 3.x - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 From 3bc6a86775bf9f647b220ae91c388b21e105ca3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:35:46 +0200 Subject: [PATCH 0854/1047] build(deps): bump actions/setup-go from 5.0.1 to 5.0.2 (#3153) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.1 to 5.0.2. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/cdcb36043654635271a94b9a6d1392de5bb323a7...0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 06db6ef549..591af73b1a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: fetch-depth: 2 - name: Setup Go - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: go-version: '^1.21' check-latest: true diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 804c27e8d2..ea8414fa1a 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -25,7 +25,7 @@ jobs: - name: Checkout uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.21' diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 6278fa73c2..92b2a345b0 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,7 +13,7 @@ jobs: contents: read steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 24138a0b6d..2af6db590b 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" From 243e4313e71556ec4dfdd12a172c223a2020daba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Mon, 15 Jul 2024 16:45:35 +0200 Subject: [PATCH 0855/1047] fix: unset ports in endpointslices (#3156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #3151 unset ports in endpointslices should not be considered in our lb members list Signed-off-by: Sandor Szücs --- dataclients/kubernetes/clusterclient.go | 8 +- .../multiple-endpointslices-one-unset.eskip | 3 + .../multiple-endpointslices-one-unset.kube | 1 + .../multiple-endpointslices-one-unset.yaml | 89 +++++++++++++++++++ .../single-endpointslices-unset.eskip | 5 ++ .../single-endpointslices-unset.kube | 1 + .../single-endpointslices-unset.yaml | 73 +++++++++++++++ 7 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.yaml diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index 87f76b278e..451109e060 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -503,8 +503,12 @@ func (c *clusterClient) loadEndpointSlices() (map[definitions.ResourceID]*skippe func collectReadyEndpoints(endpointSlices *endpointSliceList) map[definitions.ResourceID]*skipperEndpointSlice { mapSlices := make(map[definitions.ResourceID][]*endpointSlice) for _, endpointSlice := range endpointSlices.Items { - resID := endpointSlice.ToResourceID() // service resource ID - mapSlices[resID] = append(mapSlices[resID], endpointSlice) + // https://github.com/zalando/skipper/issues/3151 + // endpointslices can have nil ports + if endpointSlice.Ports != nil { + resID := endpointSlice.ToResourceID() // service resource ID + mapSlices[resID] = append(mapSlices[resID], endpointSlice) + } } result := make(map[definitions.ResourceID]*skipperEndpointSlice) diff --git a/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.eskip b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.eskip new file mode 100644 index 0000000000..efb6082a5a --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.eskip @@ -0,0 +1,3 @@ +kube_default__myapp_ingress__example_org____myapp_service: + Host("^(example[.]org[.]?(:[0-9]+)?)$") + -> "http://10.3.0.5:8080"; diff --git a/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.kube b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.kube new file mode 100644 index 0000000000..8eb0407a56 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.kube @@ -0,0 +1 @@ +enable-kubernetes-endpointslices: true diff --git a/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.yaml b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.yaml new file mode 100644 index 0000000000..ec54b981ea --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/multiple-endpointslices-one-unset.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 2 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: myapp-deployment + kubernetes.io/service-name: myapp-service + name: myapp-service-foo + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: my-zone +ports: null +--- +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: myapp-deployment + kubernetes.io/service-name: myapp-service + name: myapp-service-bar + namespace: default +endpoints: + - addresses: + - 10.3.0.5 + zone: my-zone +ports: + - port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.eskip b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.eskip new file mode 100644 index 0000000000..6fd30477b2 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.eskip @@ -0,0 +1,5 @@ +kube_default__myapp_ingress__example_org____myapp_service: + Host("^(example[.]org[.]?(:[0-9]+)?)$") + -> status(502) + -> inlineContent("no endpoints") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.kube b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.kube new file mode 100644 index 0000000000..8eb0407a56 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.kube @@ -0,0 +1 @@ +enable-kubernetes-endpointslices: true diff --git a/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.yaml b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.yaml new file mode 100644 index 0000000000..d5d1c9dfa4 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/service-ports-endpointslices/single-endpointslices-unset.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment + labels: + app: myapp +spec: + replicas: 1 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + spec: + containers: + - name: myapp + image: myapp:v1 + ports: + - containerPort: 80 + name: my-port + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myapp-deployment + name: myapp-service +spec: + clusterIP: 10.3.190.1 + ports: + - name: this-is-my-service-port-name + port: 8080 + protocol: TCP + targetPort: my-port + selector: + app: myapp + type: ClusterIP +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: myapp + name: myapp-ingress + namespace: default +spec: + rules: + - host: example.org + http: + paths: + - backend: + service: + name: myapp-service + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: EndpointSlice +metadata: + labels: + app: myapp-deployment + kubernetes.io/service-name: myapp-service + name: myapp-service-foo + namespace: default +endpoints: + - addresses: + - 10.3.0.3 + zone: my-zone +ports: null From b4389e592dc77d580f78f688ec22891cf2727785 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 20:07:34 +0200 Subject: [PATCH 0856/1047] build(deps): bump the all-go-mod-patch-and-minor group with 3 updates (#3155) Bumps the all-go-mod-patch-and-minor group with 3 updates: [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc), [github.com/instana/go-sensor](https://github.com/instana/go-sensor) and [github.com/redis/go-redis/v9](https://github.com/redis/go-redis). Updates `github.com/coreos/go-oidc/v3` from 3.10.0 to 3.11.0 - [Release notes](https://github.com/coreos/go-oidc/releases) - [Commits](https://github.com/coreos/go-oidc/compare/v3.10.0...v3.11.0) Updates `github.com/instana/go-sensor` from 1.63.0 to 1.63.1 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/RELEASE.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.63.0...v1.63.1) Updates `github.com/redis/go-redis/v9` from 9.5.3 to 9.5.4 - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.5.3...v9.5.4) --- updated-dependencies: - dependency-name: github.com/coreos/go-oidc/v3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 83597520db..820ee54d41 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 - github.com/coreos/go-oidc/v3 v3.10.0 + github.com/coreos/go-oidc/v3 v3.11.0 github.com/dchest/siphash v1.2.3 github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1 @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 - github.com/instana/go-sensor v1.63.0 + github.com/instana/go-sensor v1.63.1 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.61 github.com/oklog/ulid v1.3.1 @@ -34,7 +34,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.5.3 + github.com/redis/go-redis/v9 v9.5.4 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 @@ -95,7 +95,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect diff --git a/go.sum b/go.sum index 3b07de957a..eeb000440e 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= -github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= +github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= +github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= @@ -154,8 +154,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= -github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -255,8 +255,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.63.0 h1:6+oad7bXDIaZUoLGGSqGhPStKgwstA5rz9vg7EK1RuI= -github.com/instana/go-sensor v1.63.0/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= +github.com/instana/go-sensor v1.63.1 h1:SJuU4rT/qsquC3Ha0kCYILe0/yl+Er2EnF7zSjJkGS4= +github.com/instana/go-sensor v1.63.1/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -392,8 +392,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= -github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.5.4 h1:vOFYDKKVgrI5u++QvnMT7DksSMYg7Aw/Np4vLJLKLwY= +github.com/redis/go-redis/v9 v9.5.4/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= From 7fde930f9a7e2351c5e12366960dbf377ce3d1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 17 Jul 2024 09:21:42 +0200 Subject: [PATCH 0857/1047] feature: net.Client before and after hooks (#3140) --- net/httpclient.go | 27 +++++-- net/httpclient_example_test.go | 96 +++++++++++++++++++++++++ net/httpclient_test.go | 128 +++++++++++++++++++++++++++++---- 3 files changed, 233 insertions(+), 18 deletions(-) diff --git a/net/httpclient.go b/net/httpclient.go index 833bae6203..0de67d962f 100644 --- a/net/httpclient.go +++ b/net/httpclient.go @@ -207,6 +207,11 @@ type Options struct { // Log is used for error logging Log logging.Logger + + // BeforeSend is a hook function that runs just before executing RoundTrip(*http.Request) + BeforeSend func(*http.Request) + // AfterResponse is a hook function that runs just after executing RoundTrip(*http.Request) + AfterResponse func(*http.Response, error) } // Transport wraps an http.Transport and adds support for tracing and @@ -219,6 +224,8 @@ type Transport struct { spanName string componentName string bearerToken string + beforeSend func(*http.Request) + afterResponse func(*http.Response, error) } // NewTransport creates a wrapped http.Transport, with regular DNS @@ -275,10 +282,12 @@ func NewTransport(options Options) *Transport { } t := &Transport{ - once: sync.Once{}, - quit: make(chan struct{}), - tr: htransport, - tracer: options.Tracer, + once: sync.Once{}, + quit: make(chan struct{}), + tr: htransport, + tracer: options.Tracer, + beforeSend: options.BeforeSend, + afterResponse: options.AfterResponse, } if t.tracer != nil { @@ -342,6 +351,8 @@ func (t *Transport) shallowCopy() *Transport { spanName: t.spanName, componentName: t.componentName, bearerToken: t.bearerToken, + beforeSend: t.beforeSend, + afterResponse: t.afterResponse, } } @@ -369,7 +380,13 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { if t.bearerToken != "" { req.Header.Set("Authorization", "Bearer "+t.bearerToken) } + if t.beforeSend != nil { + t.beforeSend(req) + } rsp, err := t.tr.RoundTrip(req) + if t.afterResponse != nil { + t.afterResponse(rsp, err) + } if span != nil { span.LogKV("http_do", "stop") if rsp != nil { @@ -388,10 +405,10 @@ func (t *Transport) injectSpan(req *http.Request) (*http.Request, opentracing.Sp string(ext.HTTPUrl): req.URL.String(), }} if parentSpan := opentracing.SpanFromContext(req.Context()); parentSpan != nil { - req = req.WithContext(opentracing.ContextWithSpan(req.Context(), parentSpan)) spanOpts = append(spanOpts, opentracing.ChildOf(parentSpan.Context())) } span := t.tracer.StartSpan(t.spanName, spanOpts...) + req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) _ = t.tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) diff --git a/net/httpclient_example_test.go b/net/httpclient_example_test.go index bdd836c4b3..7af71a2183 100644 --- a/net/httpclient_example_test.go +++ b/net/httpclient_example_test.go @@ -10,11 +10,24 @@ import ( "github.com/lightstep/lightstep-tracer-go" "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/mocktracer" + "github.com/sirupsen/logrus" "github.com/zalando/skipper/net" "github.com/zalando/skipper/secrets" ) +func waitForSpanViaMockTracer(mockTracer *mocktracer.MockTracer) { + for i := 0; i < 20; i++ { + if n := len(mockTracer.FinishedSpans()); n > 0 { + logrus.Printf("found %d spans", n) + return + } + time.Sleep(100 * time.Millisecond) + } + logrus.Println("no span found") +} + func ExampleTransport() { tracer := lightstep.NewTracer(lightstep.Options{}) @@ -220,6 +233,9 @@ func ExampleClient_customTracer() { cli.Get("http://" + srv.Listener.Addr().String() + "/") + // wait for the span to be finished + waitForSpanViaMockTracer(mockTracer) + fmt.Printf("customtag: %s", mockTracer.FinishedSpans()[0].Tags()["customtag"]) // Output: @@ -325,3 +341,83 @@ func ExampleClient_hostSecret() { time.Sleep(1 * time.Second) } } + +func ExampleClient_withBeforeSendHook() { + mockTracer := mocktracer.New() + peerService := "my-peer-service" + cli := net.NewClient(net.Options{ + Tracer: &customTracer{mockTracer}, + OpentracingComponentTag: "testclient", + OpentracingSpanName: "clientSpan", + IdleConnTimeout: 2 * time.Second, + BeforeSend: func(req *http.Request) { + req.Header.Set("X-Foo", "qux") + if span := opentracing.SpanFromContext(req.Context()); span != nil { + logrus.Println("BeforeSend: found span") + span.SetTag(string(ext.PeerService), peerService) + } else { + logrus.Println("BeforeSend: no span found") + } + }, + }) + defer cli.Close() + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Printf("X-Foo: %s\n", r.Header.Get("X-Foo")) + w.WriteHeader(http.StatusOK) + })) + defer srv.Close() + + cli.Get("http://" + srv.Listener.Addr().String() + "/") + + // wait for the span to be finished + waitForSpanViaMockTracer(mockTracer) + + fmt.Printf("request tag %q set to %q", string(ext.PeerService), mockTracer.FinishedSpans()[0].Tags()[string(ext.PeerService)]) + + // Output: + // X-Foo: qux + // request tag "peer.service" set to "my-peer-service" +} + +func ExampleClient_withAfterResponseHook() { + mockTracer := mocktracer.New() + cli := net.NewClient(net.Options{ + Tracer: &customTracer{mockTracer}, + OpentracingComponentTag: "testclient", + OpentracingSpanName: "clientSpan", + BearerTokenRefreshInterval: 10 * time.Second, + BearerTokenFile: "/tmp/foo.token", + IdleConnTimeout: 2 * time.Second, + AfterResponse: func(rsp *http.Response, err error) { + if span := opentracing.SpanFromContext(rsp.Request.Context()); span != nil { + span.SetTag("status.code", rsp.StatusCode) + if err != nil { + span.SetTag("error", err.Error()) + } + } + rsp.StatusCode = 255 + }, + }) + defer cli.Close() + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer srv.Close() + + rsp, err := cli.Get("http://" + srv.Listener.Addr().String() + "/") + if err != nil { + log.Fatalf("Failed to get: %v", err) + } + + // wait for the span to be finished + waitForSpanViaMockTracer(mockTracer) + + fmt.Printf("response code: %d\n", rsp.StatusCode) + fmt.Printf("span status.code: %d", mockTracer.FinishedSpans()[0].Tags()["status.code"]) + + // Output: + // response code: 255 + // span status.code: 200 +} diff --git a/net/httpclient_test.go b/net/httpclient_test.go index 0cb2ff7dd7..980bff2da8 100644 --- a/net/httpclient_test.go +++ b/net/httpclient_test.go @@ -1,6 +1,7 @@ package net import ( + "fmt" "net/http" "net/http/httptest" "net/url" @@ -10,6 +11,9 @@ import ( "time" "github.com/AlexanderYastrebov/noleak" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/secrets" "github.com/zalando/skipper/tracing/tracers/basic" ) @@ -204,6 +208,7 @@ func TestClient(t *testing.T) { } func TestTransport(t *testing.T) { + mtracer := mocktracer.New() tracer, err := basic.InitTracer(nil) if err != nil { t.Fatalf("Failed to get a tracer: %v", err) @@ -211,12 +216,15 @@ func TestTransport(t *testing.T) { defer tracer.Close() for _, tt := range []struct { - name string - options Options - spanName string - bearerToken string - req *http.Request - wantErr bool + name string + options Options + spanName string + bearerToken string + req *http.Request + wantErr bool + checkRequestOnServer func(*http.Request) error + checkRequest func(*http.Request) error + checkResponse func(*http.Response) error }{ { name: "All defaults, with request should have a response", @@ -248,6 +256,82 @@ func TestTransport(t *testing.T) { req: httptest.NewRequest("GET", "http://example.com/", nil), wantErr: false, }, + { + name: "With hooks, should have request header and respose changed", + options: Options{ + BeforeSend: func(req *http.Request) { + if req != nil { + req.Header.Set("X-Foo", "bar") + } + }, + AfterResponse: func(rsp *http.Response, err error) { + if rsp != nil { + rsp.StatusCode = 255 + } + }, + }, + req: httptest.NewRequest("GET", "http://example.com/", nil), + wantErr: false, + checkRequestOnServer: func(req *http.Request) error { + if v := req.Header.Get("X-Foo"); v != "bar" { + return fmt.Errorf(`failed to patch request want "X-Foo": "bar", but got: %s`, v) + } + return nil + }, + checkResponse: func(rsp *http.Response) error { + if rsp.StatusCode != 255 { + return fmt.Errorf("failed to get status code 255, got: %d", rsp.StatusCode) + } + return nil + }, + }, + { + name: "With hooks and opentracing, should have request header and response changed", + options: Options{ + Tracer: mtracer, + BeforeSend: func(req *http.Request) { + if req != nil { + if span := opentracing.SpanFromContext(req.Context()); span != nil { + span.SetTag(string(ext.PeerService), "my-app") + *req = *req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) + return + } + } + }, + AfterResponse: func(rsp *http.Response, err error) { + if rsp != nil { + if span := opentracing.SpanFromContext(rsp.Request.Context()); span != nil { + span.SetTag("my.status", 255) + *rsp.Request = *rsp.Request.WithContext(opentracing.ContextWithSpan(rsp.Request.Context(), span)) + return + } + } + }, + }, + spanName: "myspan", + req: httptest.NewRequest("GET", "http://example.com/", nil), + wantErr: false, + checkRequest: func(req *http.Request) error { + if span := opentracing.SpanFromContext(req.Context()); span != nil { + peerService := mtracer.FinishedSpans()[0].Tags()[string(ext.PeerService)] + if peerService != "my-app" { + return fmt.Errorf(`failed to get Tag %s value: "my-app", got %q`, ext.PeerService, peerService) + } + return nil + } + return fmt.Errorf("failed get span from request") + }, + checkResponse: func(rsp *http.Response) error { + if span := opentracing.SpanFromContext(rsp.Request.Context()); span != nil { + status := mtracer.FinishedSpans()[0].Tags()["my.status"] + if status != 255 { + return fmt.Errorf(`failed to get Tag "my.status" value: "255", got %d`, status) + } + return nil + } + return fmt.Errorf("failed get span from request") + }, + }, } { t.Run(tt.name, func(t *testing.T) { s := startTestServer(func(r *http.Request) { @@ -259,19 +343,26 @@ func TestTransport(t *testing.T) { return } - if tt.spanName != "" && tt.options.Tracer != nil { - if r.Header.Get("Ot-Tracer-Sampled") == "" || - r.Header.Get("Ot-Tracer-Traceid") == "" || - r.Header.Get("Ot-Tracer-Spanid") == "" { - t.Errorf("One of the OT Tracer headers are missing: %v", r.Header) + if tt.spanName != "" { + if tt.options.Tracer == tracer { + if r.Header.Get("Ot-Tracer-Sampled") == "" || + r.Header.Get("Ot-Tracer-Traceid") == "" || + r.Header.Get("Ot-Tracer-Spanid") == "" { + t.Errorf("One of the OT Tracer headers are missing: %v", r.Header) + } } } - if tt.bearerToken != "" { if r.Header.Get("Authorization") != "Bearer "+string(testToken) { t.Errorf("Failed to have a token, but want to have it, got: %v, want: %v", r.Header.Get("Authorization"), "Bearer "+tt.bearerToken) } } + + if tt.checkRequestOnServer != nil { + if err := tt.checkRequestOnServer(r); err != nil { + t.Errorf("Failed to check request: %v", err) + } + } }) defer s.Close() @@ -289,11 +380,22 @@ func TestTransport(t *testing.T) { if tt.req != nil { tt.req.URL.Host = s.Listener.Addr().String() } - _, err := rt.RoundTrip(tt.req) + rsp, err := rt.RoundTrip(tt.req) if (err != nil) != tt.wantErr { t.Errorf("Transport.RoundTrip() error = %v, wantErr %v", err, tt.wantErr) return } + if tt.checkRequest != nil { + if err := tt.checkRequest(rsp.Request); err != nil { + t.Errorf("Failed to check request: %v", err) + } + } + + if tt.checkResponse != nil { + if err := tt.checkResponse(rsp); err != nil { + t.Errorf("Failed to check response: %v", err) + } + } }) } } From 07769c0dde17c2048942a80f84da9dc8282e2023 Mon Sep 17 00:00:00 2001 From: Anurag252 Date: Wed, 17 Jul 2024 10:56:53 +0200 Subject: [PATCH 0858/1047] feat: add AWS sigv4 filter (#3070) Fixes #2911 Signed-off-by: Anurag252 --- docs/reference/filters.md | 43 ++ filters/awssigner/awssigv4/doc.go | 53 +++ filters/awssigner/awssigv4/httpsigner.go | 377 ++++++++++++++++++ filters/awssigner/awssigv4/sigv4.go | 193 +++++++++ filters/awssigner/awssigv4/sigv4_test.go | 461 ++++++++++++++++++++++ filters/awssigner/internal/cache.go | 96 +++++ filters/awssigner/internal/constants.go | 25 ++ filters/awssigner/internal/credentials.go | 36 ++ filters/awssigner/internal/headers.go | 233 +++++++++++ filters/awssigner/internal/hmac.go | 12 + filters/awssigner/internal/time.go | 50 +++ filters/awssigner/internal/uri.go | 122 ++++++ filters/awssigner/internal/utils.go | 42 ++ filters/filters.go | 1 + 14 files changed, 1744 insertions(+) create mode 100644 filters/awssigner/awssigv4/doc.go create mode 100644 filters/awssigner/awssigv4/httpsigner.go create mode 100644 filters/awssigner/awssigv4/sigv4.go create mode 100644 filters/awssigner/awssigv4/sigv4_test.go create mode 100644 filters/awssigner/internal/cache.go create mode 100644 filters/awssigner/internal/constants.go create mode 100644 filters/awssigner/internal/credentials.go create mode 100644 filters/awssigner/internal/headers.go create mode 100644 filters/awssigner/internal/hmac.go create mode 100644 filters/awssigner/internal/time.go create mode 100644 filters/awssigner/internal/uri.go create mode 100644 filters/awssigner/internal/utils.go diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 026031bea0..68b721059e 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -2065,6 +2065,49 @@ so if requests on average have 100KB and the maximum memory is set to 100MB, on The filter also honors the `skip-request-body-parse` of the corresponding [configuration](https://www.openpolicyagent.org/docs/latest/envoy-introduction/#configuration) that the OPA plugin uses. +### awsSigv4 + +This filter signs request using [AWS Sig V4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html_) algorithm. The requests must provide following headers in order for this filter to generate a valid signature. +- `x-amz-accesskey` header must contain a valid AWS access key +- `x-amz-secret` header must contain a valid secret for AWS client being used. +- `x-amz-time` header must contain the time in RFC3339 format which this filter can use to generate signature and `X-Amz-Date` header on signed request. This time stamp is considered as the time stamp of generated signature. +- `x-amz-session` must contain valid AWS session token ([see](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html#using-temp-creds-sdk)) to be set as `X-Amz-Security-Token` in signed request when `DisableSessionToken` parameter defined on route is set to false. + +Filter removes these headers after reading the values. Once the signature is generated, it is appended to existing Authorization header or if there is no exisiting Authorization header, added as new and forwarded to AWS service. + +awsSigv4 filter can be defined on a route as `awsSigv4(", "", , , )` + +An example of route with awsSigv4 filter is + `editorRoute: * -> awsSigv4("dynamodb" , "us-east-1", false, false, false) -> "https://dynamodb.us-east-1.amazonaws.com";` + +This filter expects +- `Service` An aws service name. Please refer valid service names from service endpoint. + For example if service endpoint is https://dynamodb.us-east-1.amazonaws.com, then service is dynamodb + +- `Region` AWS region where service is located. Please refer valid service names from service endpoint. + For example if service endpoint is https://dynamodb.us-east-1.amazonaws.com, then region is us-east-1. + +- `DisableHeaderHoisting` Disables the Signer's moving HTTP header key/value pairs from the HTTP request header to the request's query string. This is most commonly used + with pre-signed requests preventing headers from being added to the request's query string. + +- `DisableURIPathEscaping` Disables the automatic escaping of the URI path of the request for the siganture's canonical string's path. For services that do not need additional + escaping then use this to disable the signer escaping the path. S3 is an example of a service that does not need additional escaping. + http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html + +- `DisableSessionToken` Disables setting the session token on the request as part of signing through X-Amz-Security-Token. This is needed for variations of v4 that + present the token elsewhere. + + + +#### Memory consideration +This filter reads the body in memory. This is needed to generate signature as per Signature V4 specs. Special considerations need to be taken when operating the skipper with concurrent requests. + + +#### Overwriting io.ReadCloser +This filter resets `read` and `close` implementations of body to default. So in case a filter before this filter has some custom implementations of thse methods, they would be overwritten. + + + ## Cookie Handling ### dropRequestCookie diff --git a/filters/awssigner/awssigv4/doc.go b/filters/awssigner/awssigv4/doc.go new file mode 100644 index 0000000000..61146ce30b --- /dev/null +++ b/filters/awssigner/awssigv4/doc.go @@ -0,0 +1,53 @@ +/* + +Package awssigv4 signs requests using aws signature version 4 mechanism. see https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html + +# Filter awsSigv4 +awsSigv4 filter can be defined on a route as `awsSigv4(", "", , , )` + +An example of route with awsSigv4 filter is + `editorRoute: * -> awsSigv4("dynamodb" , "us-east-1", false, false, false) -> "https://dynamodb.us-east-1.amazonaws.com";` + + This filter expects + - Service + An aws service name. Please refer valid service names from service endpoint. + For example if service endpoint is https://dynamodb.us-east-1.amazonaws.com, then service is dynamodb + + - Region + AWS region where service is located. Please refer valid service names from service endpoint. + For example if service endpoint is https://dynamodb.us-east-1.amazonaws.com, then region is us-east-1. + + - DisableHeaderHoisting + Disables the Signer's moving HTTP header key/value pairs from the HTTP request header to the request's query string. This is most commonly used + with pre-signed requests preventing headers from being added to the request's query string. + + - DisableURIPathEscaping + Disables the automatic escaping of the URI path of the request for the siganture's canonical string's path. For services that do not need additional + escaping then use this to disable the signer escaping the path. S3 is an example of a service that does not need additional escaping. + http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html + + - DisableSessionToken + Disables setting the session token on the request as part of signing through X-Amz-Security-Token. This is needed for variations of v4 that + present the token elsewhere. + + The filter also expects following headers to be present to sign the request + - x-amz-accesskey + A valid AWS access key + - x-amz-secret + A valid AWS secret + - x-amz-session + A valid session token [see](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html#using-temp-creds-sdk) + - x-amz-time + A RFC3339 format time stamp which should be considered as time stamp of signing the request. + + Filter removes these headers after reading the values and does not alter the signature produced. Once the signature is generated, it is appended to Authorization header and forwarded to AWS service. + +# Memory consideration +This filter reads the body in memory. This is needed to generate signature as per Signature V4 specs. Special considerations need to be taken when operating the skipper with concurrent requests. + + +# Overwriting io.ReadCloser +This filter resets `read` and `close` implementations of body to default. So in case a filter before this filter has some custom implementations of thse methods, they would be overwritten. +*/ + +package awssigv4 diff --git a/filters/awssigner/awssigv4/httpsigner.go b/filters/awssigner/awssigv4/httpsigner.go new file mode 100644 index 0000000000..d998a2a41e --- /dev/null +++ b/filters/awssigner/awssigv4/httpsigner.go @@ -0,0 +1,377 @@ +package awssigv4 + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "hash" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "time" + + internal "github.com/zalando/skipper/filters/awssigner/internal" +) + +type keyDerivator interface { + DeriveKey(credential internal.Credentials, service, region string, signingTime internal.SigningTime) []byte +} + +// Signer applies AWS v4 signing to given request. Use this to sign requests +// that need to be signed with AWS V4 Signatures. +type Signer struct { + options SignerOptions + keyDerivator keyDerivator +} + +type httpSigner struct { + Request *http.Request + ServiceName string + Region string + Time internal.SigningTime + Credentials internal.Credentials + KeyDerivator keyDerivator + IsPreSign bool + PayloadHash string + DisableHeaderHoisting bool + DisableURIPathEscaping bool + DisableSessionToken bool +} + +func NewSigner(optFns ...func(signer *SignerOptions)) *Signer { + options := SignerOptions{} + + for _, fn := range optFns { + fn(&options) + } + + return &Signer{options: options, keyDerivator: internal.NewSigningKeyDeriver()} +} + +func (s *httpSigner) buildCanonicalHeaders(host string, rule internal.Rule, header http.Header, length int64) (signed http.Header, signedHeaders, canonicalHeadersStr string) { + signed = make(http.Header) + + var headers []string + const hostHeader = "host" + headers = append(headers, hostHeader) + signed[hostHeader] = append(signed[hostHeader], host) + + const contentLengthHeader = "content-length" + if length > 0 { + headers = append(headers, contentLengthHeader) + signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10)) + } + + for k, v := range header { + if !rule.IsValid(k) { + continue // ignored header + } + if strings.EqualFold(k, contentLengthHeader) { + // prevent signing already handled content-length header. + continue + } + + lowerCaseKey := strings.ToLower(k) + if _, ok := signed[lowerCaseKey]; ok { + // include additional values + signed[lowerCaseKey] = append(signed[lowerCaseKey], v...) + continue + } + + headers = append(headers, lowerCaseKey) + signed[lowerCaseKey] = v + } + sort.Strings(headers) + + signedHeaders = strings.Join(headers, ";") + + var canonicalHeaders strings.Builder + n := len(headers) + const colon = ':' + for i := 0; i < n; i++ { + if headers[i] == hostHeader { + canonicalHeaders.WriteString(hostHeader) + canonicalHeaders.WriteRune(colon) + canonicalHeaders.WriteString(internal.StripExcessSpaces(host)) + } else { + canonicalHeaders.WriteString(headers[i]) + canonicalHeaders.WriteRune(colon) + // Trim out leading, trailing, and dedup inner spaces from signed header values. + values := signed[headers[i]] + for j, v := range values { + cleanedValue := strings.TrimSpace(internal.StripExcessSpaces(v)) + canonicalHeaders.WriteString(cleanedValue) + if j < len(values)-1 { + canonicalHeaders.WriteRune(',') + } + } + } + canonicalHeaders.WriteRune('\n') + } + canonicalHeadersStr = canonicalHeaders.String() + + return signed, signedHeaders, canonicalHeadersStr +} + +func (s *httpSigner) Build() (signedRequest, error) { + req := s.Request + + query := req.URL.Query() + headers := req.Header + + s.setRequiredSigningFields(headers, query) + + // Sort Each Query Key's Values + for key := range query { + sort.Strings(query[key]) + } + + internal.SanitizeHostForHeader(req) + + credentialScope := s.buildCredentialScope() + credentialStr := s.Credentials.AccessKeyID + "/" + credentialScope + if s.IsPreSign { + query.Set(internal.AmzCredentialKey, credentialStr) + } + + unsignedHeaders := headers + if s.IsPreSign && !s.DisableHeaderHoisting { + var urlValues url.Values + urlValues, unsignedHeaders = buildQuery(internal.AllowedQueryHoisting, headers) + for k := range urlValues { + query[k] = urlValues[k] + } + } + //this is not valid way to extract host as skipper never recieves aws host in req.URL. We should set it explicitly + /*host := req.URL.Host + if len(req.Host) > 0 { + host = req.Host + }*/ + host := s.ServiceName + "." + s.Region + ".amazonaws.com" + + signedHeaders, signedHeadersStr, canonicalHeaderStr := s.buildCanonicalHeaders(host, internal.IgnoredHeaders, unsignedHeaders, s.Request.ContentLength) + + if s.IsPreSign { + query.Set(internal.AmzSignedHeadersKey, signedHeadersStr) + } + + var rawQuery strings.Builder + rawQuery.WriteString(strings.Replace(query.Encode(), "+", "%20", -1)) + + canonicalURI := internal.GetURIPath(req.URL) + if !s.DisableURIPathEscaping { + canonicalURI = internal.EscapePath(canonicalURI, false) + } + + canonicalString := s.buildCanonicalString( + req.Method, + canonicalURI, + rawQuery.String(), + signedHeadersStr, + canonicalHeaderStr, + ) + + strToSign := s.buildStringToSign(credentialScope, canonicalString) + signingSignature, err := s.buildSignature(strToSign) + if err != nil { + return signedRequest{}, err + } + + if s.IsPreSign { + rawQuery.WriteString("&X-Amz-Signature=") + rawQuery.WriteString(signingSignature) + } else { + headers[internal.AuthorizationHeader] = append(headers[internal.AuthorizationHeader][:0], buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature)) + } + + req.URL.RawQuery = rawQuery.String() + + return signedRequest{ + Request: req, + SignedHeaders: signedHeaders, + CanonicalString: canonicalString, + StringToSign: strToSign, + PreSigned: s.IsPreSign, + }, nil + +} + +func (s *httpSigner) buildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders string) string { + return strings.Join([]string{ + method, + uri, + query, + canonicalHeaders, + signedHeaders, + s.PayloadHash, + }, "\n") +} + +func (s *httpSigner) buildSignature(strToSign string) (string, error) { + key := s.KeyDerivator.DeriveKey(s.Credentials, s.ServiceName, s.Region, s.Time) + return hex.EncodeToString(internal.HMACSHA256(key, []byte(strToSign))), nil +} + +type signedRequest struct { + Request *http.Request + SignedHeaders http.Header + CanonicalString string + StringToSign string + PreSigned bool +} + +func (s *httpSigner) buildStringToSign(credentialScope, canonicalRequestString string) string { + return strings.Join([]string{ + internal.SigningAlgorithm, + s.Time.TimeFormat(), + credentialScope, + hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequestString))), + }, "\n") +} + +func makeHash(hash hash.Hash, b []byte) []byte { + hash.Reset() + hash.Write(b) + return hash.Sum(nil) +} + +func buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature string) string { + const credential = "Credential=" + const signedHeaders = "SignedHeaders=" + const signature = "Signature=" + const commaSpace = ", " + + var parts strings.Builder + parts.Grow(len(internal.SigningAlgorithm) + 1 + + len(credential) + len(credentialStr) + 2 + + len(signedHeaders) + len(signedHeadersStr) + 2 + + len(signature) + len(signingSignature), + ) + parts.WriteString(internal.SigningAlgorithm) + parts.WriteRune(' ') + parts.WriteString(credential) + parts.WriteString(credentialStr) + parts.WriteString(commaSpace) + parts.WriteString(signedHeaders) + parts.WriteString(signedHeadersStr) + parts.WriteString(commaSpace) + parts.WriteString(signature) + parts.WriteString(signingSignature) + return parts.String() +} + +func buildQuery(r internal.Rule, header http.Header) (url.Values, http.Header) { + query := url.Values{} + unsignedHeaders := http.Header{} + for k, h := range header { + if r.IsValid(k) { + query[k] = h + } else { + unsignedHeaders[k] = h + } + } + + return query, unsignedHeaders +} + +func (s *httpSigner) buildCredentialScope() string { + return internal.BuildCredentialScope(s.Time, s.Region, s.ServiceName) +} + +func (s *httpSigner) setRequiredSigningFields(headers http.Header, query url.Values) { + amzDate := s.Time.TimeFormat() + + if s.IsPreSign { + query.Set(internal.AmzAlgorithmKey, internal.SigningAlgorithm) + sessionToken := s.Credentials.SessionToken + if !s.DisableSessionToken && len(sessionToken) > 0 { + query.Set("X-Amz-Security-Token", sessionToken) + } + + query.Set(internal.AmzDateKey, amzDate) + return + } + + headers[internal.AmzDateKey] = append(headers[internal.AmzDateKey][:0], amzDate) + + if !s.DisableSessionToken && len(s.Credentials.SessionToken) > 0 { + headers[internal.AmzSecurityTokenKey] = append(headers[internal.AmzSecurityTokenKey][:0], s.Credentials.SessionToken) + } +} + +// The passed in request will be modified in place. +func (s Signer) SignHTTP(credentials internal.Credentials, r *http.Request, payloadHash string, service string, region string, signingTime time.Time, optFns ...func(options *SignerOptions)) error { + options := s.options + + for _, fn := range optFns { + fn(&options) + } + + signer := &httpSigner{ + Request: r, + PayloadHash: payloadHash, + ServiceName: service, + Region: region, + Credentials: credentials, + Time: internal.NewSigningTime(signingTime.UTC()), + DisableHeaderHoisting: options.DisableHeaderHoisting, + DisableURIPathEscaping: options.DisableURIPathEscaping, + DisableSessionToken: options.DisableSessionToken, + KeyDerivator: s.keyDerivator, + } + + _, err := signer.Build() + return err +} + +func (s *httpSigner) BuildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders string) string { + return strings.Join([]string{ + method, + uri, + query, + canonicalHeaders, + signedHeaders, + s.PayloadHash, + }, "\n") +} + +func (s *httpSigner) BuildStringToSign(credentialScope, canonicalRequestString string) string { + return strings.Join([]string{ + internal.SigningAlgorithm, + s.Time.TimeFormat(), + credentialScope, + hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequestString))), + }, "\n") +} + +type SignerOptions struct { + // Disables the Signer's moving HTTP header key/value pairs from the HTTP + // request header to the request's query string. This is most commonly used + // with pre-signed requests preventing headers from being added to the + // request's query string. + DisableHeaderHoisting bool + + // Disables the automatic escaping of the URI path of the request for the + // siganture's canonical string's path. For services that do not need additional + // escaping then use this to disable the signer escaping the path. + // + // S3 is an example of a service that does not need additional escaping. + // + // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html + DisableURIPathEscaping bool + + // The logger to send log messages to. + Ctx context.Context + + // Enable logging of signed requests. + // This will enable logging of the canonical request, the string to sign, and for presigning the subsequent + // presigned URL. + LogSigning bool + + // Disables setting the session token on the request as part of signing + // through X-Amz-Security-Token. This is needed for variations of v4 that + // present the token elsewhere. + DisableSessionToken bool +} diff --git a/filters/awssigner/awssigv4/sigv4.go b/filters/awssigner/awssigv4/sigv4.go new file mode 100644 index 0000000000..db525c79f2 --- /dev/null +++ b/filters/awssigner/awssigv4/sigv4.go @@ -0,0 +1,193 @@ +package awssigv4 + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "net/http" + "strconv" + "time" + + log "github.com/sirupsen/logrus" + "github.com/zalando/skipper/filters" + internal "github.com/zalando/skipper/filters/awssigner/internal" +) + +type awsSigV4Spec struct{} + +const accessKeyHeader = "x-amz-accesskey" +const secretHeader = "x-amz-secret" +const sessionHeader = "x-amz-session" +const timeHeader = "x-amz-time" + +type awsSigV4Filter struct { + region string + service string + disableHeaderHoisting bool + disableURIPathEscaping bool + disableSessionToken bool +} + +func New() filters.Spec { + return &awsSigV4Spec{} +} + +func (*awsSigV4Spec) Name() string { + return filters.AWSSigV4Name +} + +func (c *awsSigV4Spec) CreateFilter(args []interface{}) (filters.Filter, error) { + if len(args) != 5 { + return nil, filters.ErrInvalidFilterParameters + } + + region, ok := args[0].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + service, ok := args[1].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + disableHeaderHoistingStr, ok := args[2].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + disableURIPathEscapingStr, ok := args[3].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + disableSessionTokenStr, ok := args[4].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + disableHeaderHoisting, err := strconv.ParseBool(disableHeaderHoistingStr) + if err != nil { + return nil, filters.ErrInvalidFilterParameters + } + + disableURIPathEscaping, err := strconv.ParseBool(disableURIPathEscapingStr) + if err != nil { + return nil, filters.ErrInvalidFilterParameters + } + + disableSessionToken, err := strconv.ParseBool(disableSessionTokenStr) + if err != nil { + return nil, filters.ErrInvalidFilterParameters + } + + return &awsSigV4Filter{ + region: region, + service: service, + disableHeaderHoisting: disableHeaderHoisting, + disableURIPathEscaping: disableURIPathEscaping, + disableSessionToken: disableSessionToken, + }, nil +} + +/* +sigV4Filter is a request filter that signs the request. +In case a non empty is body is present in request, +the body is read and signed. The body is later reassigned to request. Operators should ensure +that body size by all requests at any point of time is not more than the memory limit of skipper. +*/ +func (f *awsSigV4Filter) Request(ctx filters.FilterContext) { + req := ctx.Request() + + logger := log.WithContext(req.Context()) + + signer := NewSigner() + + accessKey := getAndRemoveHeader(ctx, accessKeyHeader, req) + if accessKey == "" { + return + } + + secretKey := getAndRemoveHeader(ctx, secretHeader, req) + if secretKey == "" { + return + } + sessionToken := "" + if !f.disableSessionToken { + sessionToken = getAndRemoveHeader(ctx, sessionHeader, req) + if sessionToken == "" { + return + } + } + + timeStr := getAndRemoveHeader(ctx, timeHeader, req) + if timeStr == "" { + return + } + + time, err := time.Parse(time.RFC3339, timeStr) + + if err != nil { + logger.Log(log.ErrorLevel, "time was not in RFC3339 format") + return + } + + hashedBody, body, err := hashRequest(ctx, req.Body) + if err != nil { + logger.Log(log.ErrorLevel, fmt.Sprintf("error occured while hashing the body %s", err.Error())) + return + } + creds := internal.Credentials{ + AccessKeyID: accessKey, + SessionToken: sessionToken, + SecretAccessKey: secretKey, + } + + optfn := func(options *SignerOptions) { + options.DisableHeaderHoisting = f.disableHeaderHoisting + options.DisableSessionToken = f.disableSessionToken + options.DisableURIPathEscaping = f.disableURIPathEscaping + options.Ctx = ctx.Request().Context() + } + //modifies request inplace + signer.SignHTTP(creds, req, hashedBody, f.service, f.region, time, optfn) + + ctx.Request().Body = io.NopCloser(body) //ATTN: custom close() and read() set by skipper or previous filters are lost +} + +func (f *awsSigV4Filter) Response(ctx filters.FilterContext) {} + +func hashRequest(ctx filters.FilterContext, body io.Reader) (string, io.Reader, error) { + h := sha256.New() + if body == nil { + body = http.NoBody + _, err := io.Copy(h, body) + if err != nil { + + return "", nil, err + } + return hex.EncodeToString(h.Sum(nil)), nil, nil + } else { + var buf bytes.Buffer + tee := io.TeeReader(body, &buf) + _, err := io.Copy(h, tee) + if err != nil { + return "", nil, err + } + return hex.EncodeToString(h.Sum(nil)), &buf, nil + } +} + +func getAndRemoveHeader(ctx filters.FilterContext, headerName string, req *http.Request) string { + logger := log.WithContext(ctx.Request().Context()) + headerValue := req.Header.Get(headerName) + if headerValue == "" { + logger.Logf(log.ErrorLevel, "%q header is missing", headerName) + return "" + } else { + req.Header.Del(headerName) + return headerValue + } +} diff --git a/filters/awssigner/awssigv4/sigv4_test.go b/filters/awssigner/awssigv4/sigv4_test.go new file mode 100644 index 0000000000..fe8ecf57d9 --- /dev/null +++ b/filters/awssigner/awssigv4/sigv4_test.go @@ -0,0 +1,461 @@ +package awssigv4 + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "net/http" + "reflect" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/filters" + internal "github.com/zalando/skipper/filters/awssigner/internal" + "github.com/zalando/skipper/filters/filtertest" +) + +func TestSignRequest(t *testing.T) { + var testCredentials = internal.Credentials{AccessKeyID: "AKID", SecretAccessKey: "SECRET", SessionToken: "SESSION"} + req, body := buildRequest("dynamodb", "us-east-1", "{}") + signer := NewSigner() + ctx := &filtertest.Context{ + FRequest: &http.Request{}, + } + optfn := func(options *SignerOptions) { + options.Ctx = ctx.FRequest.Context() + } + err := signer.SignHTTP(testCredentials, req, body, "dynamodb", "us-east-1", time.Unix(0, 0), optfn) + if err != nil { + t.Fatalf("expect no error, got %v", err) + } + + expectedDate := "19700101T000000Z" + expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=a518299330494908a70222cec6899f6f32f297f8595f6df1776d998936652ad9" + + q := req.Header + if e, a := expectedSig, q.Get("Authorization"); e != a { + t.Errorf("expect %v, got %v", e, a) + } + if e, a := expectedDate, q.Get("X-Amz-Date"); e != a { + t.Errorf("expect %v, got %v", e, a) + } +} + +func TestBuildCanonicalRequest(t *testing.T) { + req, _ := buildRequest("dynamodb", "us-east-1", "{}") + req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" + + ctx := &httpSigner{ + ServiceName: "dynamodb", + Region: "us-east-1", + Request: req, + Time: internal.NewSigningTime(time.Now()), + KeyDerivator: internal.NewSigningKeyDeriver(), + } + + build, err := ctx.Build() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expected := "https://example.org/bucket/key-._~,!@#$%^&*()?Foo=a&Foo=m&Foo=o&Foo=z" + if e, a := expected, build.Request.URL.String(); e != a { + t.Errorf("expect %v, got %v", e, a) + } +} + +func TestSigner_SignHTTP_NoReplaceRequestBody(t *testing.T) { + req, bodyHash := buildRequest("dynamodb", "us-east-1", "{}") + req.Body = io.NopCloser(bytes.NewReader([]byte{})) + var testCredentials = internal.Credentials{AccessKeyID: "AKID", SecretAccessKey: "SECRET", SessionToken: "SESSION"} + s := NewSigner() + + origBody := req.Body + ctx := &filtertest.Context{ + FRequest: &http.Request{ + Header: http.Header{ + "X-Foo": []string{"foo"}, + }, + }, + } + optfn := func(options *SignerOptions) { + options.Ctx = ctx.FRequest.Context() + } + err := s.SignHTTP(testCredentials, req, bodyHash, "dynamodb", "us-east-1", time.Now(), optfn) + if err != nil { + t.Fatalf("expect no error, got %v", err) + } + + if req.Body != origBody { + t.Errorf("expect request body to not be chagned") + } +} + +/* +This test is being skipped since for skipper, we cannot dervive AWS host from req. +see https://github.com/zalando/skipper/pull/3070/files#diff-59e00c1e2a1a8ea3f9e5b4111f5c0b56cd7f81b1b14d8148f1dae146958d2c45R154 +for change. We still keep this test to debug any unusual behaviours +*/ +func TestRequestHost(t *testing.T) { + t.Skip() + req, _ := buildRequest("dynamodb", "us-east-1", "{}") + req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" + req.Host = "myhost" + + query := req.URL.Query() + query.Set("X-Amz-Expires", "5") + req.URL.RawQuery = query.Encode() + + ctx := &httpSigner{ + ServiceName: "dynamodb", + Region: "us-east-1", + Request: req, + Time: internal.NewSigningTime(time.Now()), + KeyDerivator: internal.NewSigningKeyDeriver(), + } + + build, err := ctx.Build() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !strings.Contains(build.CanonicalString, "host:"+req.Host) { + t.Errorf("canonical host header invalid") + } +} + +func TestSign_buildCanonicalHeadersContentLengthPresent(t *testing.T) { + body := `{"description": "this is a test"}` + req, _ := buildRequest("dynamodb", "us-east-1", body) + req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" + req.Host = "myhost" + + contentLength := fmt.Sprintf("%d", len([]byte(body))) + req.Header.Add("Content-Length", contentLength) + + query := req.URL.Query() + query.Set("X-Amz-Expires", "5") + req.URL.RawQuery = query.Encode() + + ctx := &httpSigner{ + ServiceName: "dynamodb", + Region: "us-east-1", + Request: req, + Time: internal.NewSigningTime(time.Now()), + KeyDerivator: internal.NewSigningKeyDeriver(), + } + + build, err := ctx.Build() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !strings.Contains(build.CanonicalString, "content-length:"+contentLength+"\n") { + t.Errorf("canonical header content-length invalid") + } +} + +func TestSign_buildCanonicalHeaders(t *testing.T) { + serviceName := "mockAPI" + region := "mock-region" + endpoint := "https://" + serviceName + "." + region + ".amazonaws.com" + + req, err := http.NewRequest("POST", endpoint, nil) + if err != nil { + t.Fatalf("failed to create request, %v", err) + } + + req.Header.Set("FooInnerSpace", " inner space ") + req.Header.Set("FooLeadingSpace", " leading-space") + req.Header.Add("FooMultipleSpace", "no-space") + req.Header.Add("FooMultipleSpace", "\ttab-space") + req.Header.Add("FooMultipleSpace", "trailing-space ") + req.Header.Set("FooNoSpace", "no-space") + req.Header.Set("FooTabSpace", "\ttab-space\t") + req.Header.Set("FooTrailingSpace", "trailing-space ") + req.Header.Set("FooWrappedSpace", " wrapped-space ") + + ctx := &httpSigner{ + ServiceName: serviceName, + Region: region, + Request: req, + Time: internal.NewSigningTime(time.Date(2021, 10, 20, 12, 42, 0, 0, time.UTC)), + KeyDerivator: internal.NewSigningKeyDeriver(), + } + + build, err := ctx.Build() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + expectCanonicalString := strings.Join([]string{ + `POST`, + `/`, + ``, + `fooinnerspace:inner space`, + `fooleadingspace:leading-space`, + `foomultiplespace:no-space,tab-space,trailing-space`, + `foonospace:no-space`, + `footabspace:tab-space`, + `footrailingspace:trailing-space`, + `foowrappedspace:wrapped-space`, + `host:mockAPI.mock-region.amazonaws.com`, + `x-amz-date:20211020T124200Z`, + ``, + `fooinnerspace;fooleadingspace;foomultiplespace;foonospace;footabspace;footrailingspace;foowrappedspace;host;x-amz-date`, + ``, + }, "\n") + if diff := cmpDiff(expectCanonicalString, build.CanonicalString); diff != "" { + t.Errorf("expect match, got\n%s", diff) + } +} + +func TestSigV4(t *testing.T) { + sigV4 := awsSigV4Filter{ + region: "us-east-1", + service: "dynamodb", + disableHeaderHoisting: false, + disableURIPathEscaping: false, + disableSessionToken: false, + } + + tests := []struct { + accessKey string + secret string + session string + timeOfSigning string + expectedSignature string + name string + }{ + { + accessKey: "", + secret: "some-invalid-secret", + session: "some-invalid-session", + timeOfSigning: "2012-11-01T22:08:41+00:00", + expectedSignature: "", + name: "No_access_key_supplied", + }, + { + accessKey: "some-invalid-accesskey", + secret: "", + session: "some-invalid-session", + timeOfSigning: "2012-11-01T22:08:41+00:00", + expectedSignature: "", + name: "No_secret_key_provided", + }, + { + accessKey: "some-access-key", + secret: "some-invalid-secret", + session: "", + timeOfSigning: "2012-11-01T22:08:41+00:00", + expectedSignature: "", + name: "No_session_key_supplied", + }, + { + accessKey: "some-access-key", + secret: "some-invalid-secret", + session: "some-invalid-session", + timeOfSigning: "", + expectedSignature: "", + name: "No_time_of_signing_supplied", + }, + { + accessKey: "some-access-key", + secret: "some-invalid-secret", + session: "some-invalid-session", + timeOfSigning: "2012-11-01T22:08:41+00", + expectedSignature: "", + name: "incorrect_format_of_time_of_signing_supplied", + }, + { + accessKey: "AKID", + secret: "SECRET", + session: "SESSION", + timeOfSigning: "1970-01-01T00:00:00Z", + expectedSignature: "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=a518299330494908a70222cec6899f6f32f297f8595f6df1776d998936652ad9", + name: "all_headers_supplied", + }, + } + + for _, test := range tests { + expectedSig := test.expectedSignature + headers := &http.Header{} + headers.Add("x-amz-accesskey", test.accessKey) + headers.Add("x-amz-secret", test.secret) + headers.Add("x-amz-session", test.session) + headers.Add("x-amz-time", test.timeOfSigning) + + headers.Add("X-Amz-Target", "prefix.Operation") + headers.Add("Content-Type", "application/x-amz-json-1.0") + headers.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)") + headers.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") + headers.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") + ctx := buildfilterContext(sigV4.service, sigV4.region, "POST", strings.NewReader("{}"), "//example.org/bucket/key-._~,!@#$%^&*()", *headers) + + sigV4.Request(ctx) + + signature := ctx.Request().Header.Get(internal.AuthorizationHeader) + b, _ := io.ReadAll(ctx.Request().Body) + assert.Equal(t, string(b), "{}") // test that body remains intact + assert.Equal(t, expectedSig, signature, fmt.Sprintf("%s - test failed", test.name)) + } + +} + +func TestSigV4WithDisabledSessionToken(t *testing.T) { + + tests := []struct { + accessKey string + secret string + session string + timeOfSigning string + expectedSignature string + name string + disableSessionToken bool + }{ + { + accessKey: "some-token", + secret: "some-invalid-secret", + session: "", + timeOfSigning: "2012-11-01T22:08:41+00:00", + expectedSignature: "", + disableSessionToken: false, + name: "session_token_expected_but_not_supplied", + }, + { + accessKey: "AKID", + secret: "SECRET", + session: "", + timeOfSigning: "1970-01-01T00:00:00Z", + expectedSignature: "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-session;x-amz-target, Signature=70ec16babd243ae915f100d0b63d5a0da2ff63c31d8631f1048b0441ab26743a", + disableSessionToken: true, + name: "session_token_not_expected_and_not_supplied", // x-amz-session header is treated as normal header and used to calculate signature + }, + { + accessKey: "AKID", + secret: "SECRET", + session: "SESSION", + timeOfSigning: "1970-01-01T00:00:00Z", + expectedSignature: "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=a4366bfb9558097b242ac243bf0e099267cbb657362495b5031c509644b2c3e9", + disableSessionToken: false, + name: "session_token_expected_and_supplied", // x-amz-session header is treated as session header and is removed after reading + }, + { + accessKey: "AKID", + secret: "SECRET", + session: "SESSION", + timeOfSigning: "1970-01-01T00:00:00Z", + expectedSignature: "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-session;x-amz-target, Signature=2ee2d824eaead96cdec798f87530ce5269b59679038945608cfedf2d3622ad86", + disableSessionToken: true, + name: "session_token_not_expected_and_supplied", // x-amz-session header is treated as normal header and used to calculate signature + }, + } + for _, test := range tests { + sigV4 := awsSigV4Filter{ + region: "us-east-1", + service: "dynamodb", + disableHeaderHoisting: false, + disableURIPathEscaping: false, + } + sigV4.disableSessionToken = test.disableSessionToken + expectedSig := test.expectedSignature + headers := &http.Header{} + headers.Add("x-amz-accesskey", test.accessKey) + headers.Add("x-amz-secret", test.secret) + headers.Add("x-amz-session", test.session) + headers.Add("x-amz-time", test.timeOfSigning) + + headers.Add("X-Amz-Target", "prefix.Operation") + headers.Add("Content-Type", "application/x-amz-json-1.0") + headers.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)") + headers.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") + headers.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") + ctx := buildfilterContext(sigV4.service, sigV4.region, "POST", strings.NewReader("{}"), "", *headers) + + sigV4.Request(ctx) + + signature := ctx.Request().Header.Get(internal.AuthorizationHeader) + b, _ := io.ReadAll(ctx.Request().Body) + assert.Equal(t, string(b), "{}") // test that body remains intact + assert.Equal(t, expectedSig, signature, fmt.Sprintf("%s - test failed", test.name)) + } + +} + +func TestSigV4WithNoBody(t *testing.T) { + sigV4 := awsSigV4Filter{ + region: "us-east-1", + service: "dynamodb", + disableHeaderHoisting: false, + disableURIPathEscaping: false, + disableSessionToken: false, + } + expectedSignature := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=f779ebb258e1692ca97a514c3a9fb99ad1b6f8869608f43ff66bc00d2c0400f5" + headers := &http.Header{} + headers.Add("x-amz-accesskey", "AKID") + headers.Add("x-amz-secret", "SECRET") + headers.Add("x-amz-session", "SESSION") + headers.Add("x-amz-time", "1970-01-01T00:00:00Z") + ctx := buildfilterContext(sigV4.service, sigV4.region, "POST", strings.NewReader(""), "", *headers) + sigV4.Request(ctx) + signature := ctx.Request().Header.Get(internal.AuthorizationHeader) + b, _ := io.ReadAll(ctx.Request().Body) + assert.Equal(t, string(b), "") // test that body remains intact + assert.Equal(t, expectedSignature, signature, "test with no body has failed") +} + +func buildfilterContext(serviceName string, region string, method string, body *strings.Reader, queryParams string, headers http.Header) filters.FilterContext { + endpoint := "https://" + serviceName + "." + region + ".amazonaws.com" + + r, _ := http.NewRequest(method, endpoint, body) + r.URL.Opaque = queryParams + r.Header = headers + return &filtertest.Context{FRequest: r} +} + +func cmpDiff(e, a interface{}) string { + if !reflect.DeepEqual(e, a) { + return fmt.Sprintf("%v != %v", e, a) + } + return "" +} + +func buildRequestWithBodyReader(serviceName, region string, body io.Reader) (*http.Request, string) { + var bodyLen int + + type lenner interface { + Len() int + } + if lr, ok := body.(lenner); ok { + bodyLen = lr.Len() + } + + endpoint := "https://" + serviceName + "." + region + ".amazonaws.com" + req, _ := http.NewRequest("POST", endpoint, body) + req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()" + req.Header.Set("X-Amz-Target", "prefix.Operation") + req.Header.Set("Content-Type", "application/x-amz-json-1.0") + + if bodyLen > 0 { + req.ContentLength = int64(bodyLen) + } + + req.Header.Set("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)") + req.Header.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") + req.Header.Add("X-amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") + + h := sha256.New() + _, _ = io.Copy(h, body) + payloadHash := hex.EncodeToString(h.Sum(nil)) + + return req, payloadHash +} + +func buildRequest(serviceName, region, body string) (*http.Request, string) { + reader := strings.NewReader(body) + return buildRequestWithBodyReader(serviceName, region, reader) +} diff --git a/filters/awssigner/internal/cache.go b/filters/awssigner/internal/cache.go new file mode 100644 index 0000000000..4244592eba --- /dev/null +++ b/filters/awssigner/internal/cache.go @@ -0,0 +1,96 @@ +package awssigner + +import ( + "strings" + "sync" + "time" +) + +type derivedKeyCache struct { + values map[string]derivedKey + mutex sync.RWMutex +} + +type derivedKey struct { + AccessKey string + Date time.Time + Credential []byte +} + +// SigningKeyDeriver derives a signing key from a set of credentials +type SigningKeyDeriver struct { + cache derivedKeyCache +} + +func NewSigningKeyDeriver() *SigningKeyDeriver { + return &SigningKeyDeriver{ + cache: newDerivedKeyCache(), + } +} + +// DeriveKey returns a derived signing key from the given credentials to be used with SigV4 signing. +func (k *SigningKeyDeriver) DeriveKey(credential Credentials, service, region string, signingTime SigningTime) []byte { + return k.cache.getSigningKey(credential, service, region, signingTime) +} + +// copied from https://github.com/aws/aws-sdk-go-v2/blob/v1.25.0/aws/signer/internal/v4/cache.go#L11 +func lookupKey(service, region string) string { + var s strings.Builder + s.Grow(len(region) + len(service) + 3) + s.WriteString(region) + s.WriteRune('/') + s.WriteString(service) + return s.String() +} + +func (s *derivedKeyCache) get(key string, credentials Credentials, signingTime time.Time) ([]byte, bool) { + cacheEntry, ok := s.retrieveFromCache(key) + if ok && cacheEntry.AccessKey == credentials.AccessKeyID && isSameDay(signingTime, cacheEntry.Date) { + return cacheEntry.Credential, true + } + return nil, false +} + +func (s *derivedKeyCache) retrieveFromCache(key string) (derivedKey, bool) { + if v, ok := s.values[key]; ok { + return v, true + } + return derivedKey{}, false +} + +func (s *derivedKeyCache) getSigningKey(credentials Credentials, service, region string, signingTime SigningTime) []byte { + key := lookupKey(service, region) + s.mutex.RLock() + if cred, ok := s.get(key, credentials, signingTime.Time); ok { + s.mutex.RUnlock() + return cred + } + s.mutex.RUnlock() + + s.mutex.Lock() + defer s.mutex.Unlock() + if cred, ok := s.get(key, credentials, signingTime.Time); ok { + return cred + } + cred := deriveKey(credentials.SecretAccessKey, service, region, signingTime) + entry := derivedKey{ + AccessKey: credentials.AccessKeyID, + Date: signingTime.Time, + Credential: cred, + } + s.values[key] = entry + return cred +} + +func deriveKey(secret, service, region string, t SigningTime) []byte { + hmacDate := HMACSHA256([]byte("AWS4"+secret), []byte(t.ShortTimeFormat())) + hmacRegion := HMACSHA256(hmacDate, []byte(region)) + hmacService := HMACSHA256(hmacRegion, []byte(service)) + return HMACSHA256(hmacService, []byte("aws4_request")) +} + +func newDerivedKeyCache() derivedKeyCache { + return derivedKeyCache{ + values: make(map[string]derivedKey), + } +} diff --git a/filters/awssigner/internal/constants.go b/filters/awssigner/internal/constants.go new file mode 100644 index 0000000000..af1e18ab03 --- /dev/null +++ b/filters/awssigner/internal/constants.go @@ -0,0 +1,25 @@ +package awssigner + +const AuthorizationHeader = "Authorization" +const doubleSpace = " " + +// AmzSignedHeadersKey is the set of headers signed for the request +const AmzSignedHeadersKey = "X-Amz-SignedHeaders" + +// AmzCredentialKey is the access key ID and credential scope +const AmzCredentialKey = "X-Amz-Credential" + +// TimeFormat is the time format to be used in the X-Amz-Date header or query parameter +const TimeFormat = "20060102T150405Z" + +const SigningAlgorithm = "AWS4-HMAC-SHA256" + +// ShortTimeFormat is the shorten time format used in the credential scope +const ShortTimeFormat = "20060102" + +const AmzAlgorithmKey = "X-Amz-Algorithm" + +const AmzDateKey = "X-Amz-Date" + +// AmzSecurityTokenKey indicates the security token to be used with temporary credentials +const AmzSecurityTokenKey = "X-Amz-Security-Token" diff --git a/filters/awssigner/internal/credentials.go b/filters/awssigner/internal/credentials.go new file mode 100644 index 0000000000..fab8e4eb70 --- /dev/null +++ b/filters/awssigner/internal/credentials.go @@ -0,0 +1,36 @@ +package awssigner + +import ( + "path" + "time" +) + +// Credentials is the type to represent AWS credentials +type Credentials struct { + // AccessKeyID is AWS Access key ID + AccessKeyID string + + // SecretAccessKey is AWS Secret Access Key + SecretAccessKey string + + // SessionToken is AWS Session Token + SessionToken string + + // Source of the AWS credentials + Source string + + // CanExpire states if the AWS credentials can expire or not. + CanExpire bool + + // Expires is the time the AWS credentials will expire at. Should be ignored if CanExpire is false. + Expires time.Time +} + +// BuildCredentialScope builds part of credential string to be used as X-Amz-Credential header or query parameter. +func BuildCredentialScope(signingTime SigningTime, region, service string) string { + return path.Join( + signingTime.ShortTimeFormat(), + region, + service, + "aws4_request") +} diff --git a/filters/awssigner/internal/headers.go b/filters/awssigner/internal/headers.go new file mode 100644 index 0000000000..7289846f56 --- /dev/null +++ b/filters/awssigner/internal/headers.go @@ -0,0 +1,233 @@ +package awssigner + +import ( + "net/http" + "sort" + "strconv" + "strings" +) + +// AllowedQueryHoisting is a whitelist for Build query headers. The boolean value +// represents whether or not it is a pattern. +var AllowedQueryHoisting = InclusiveRules{ + DenyList{RequiredSignedHeaders}, + Patterns{"X-Amz-"}, +} + +// InclusiveRules rules allow for rules to depend on one another +type InclusiveRules []Rule + +// IsValid will return true if all rules are true +func (r InclusiveRules) IsValid(value string) bool { + for _, rule := range r { + if !rule.IsValid(value) { + return false + } + } + return true +} + +// RequiredSignedHeaders is a whitelist for Build canonical headers. +var RequiredSignedHeaders = Rules{ + AllowList{ + MapRule{ + "Cache-Control": struct{}{}, + "Content-Disposition": struct{}{}, + "Content-Encoding": struct{}{}, + "Content-Language": struct{}{}, + "Content-Md5": struct{}{}, + "Content-Type": struct{}{}, + "Expires": struct{}{}, + "If-Match": struct{}{}, + "If-Modified-Since": struct{}{}, + "If-None-Match": struct{}{}, + "If-Unmodified-Since": struct{}{}, + "Range": struct{}{}, + "X-Amz-Acl": struct{}{}, + "X-Amz-Copy-Source": struct{}{}, + "X-Amz-Copy-Source-If-Match": struct{}{}, + "X-Amz-Copy-Source-If-Modified-Since": struct{}{}, + "X-Amz-Copy-Source-If-None-Match": struct{}{}, + "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{}, + "X-Amz-Copy-Source-Range": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{}, + "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, + "X-Amz-Grant-Full-control": struct{}{}, + "X-Amz-Grant-Read": struct{}{}, + "X-Amz-Grant-Read-Acp": struct{}{}, + "X-Amz-Grant-Write": struct{}{}, + "X-Amz-Grant-Write-Acp": struct{}{}, + "X-Amz-Metadata-Directive": struct{}{}, + "X-Amz-Mfa": struct{}{}, + "X-Amz-Request-Payer": struct{}{}, + "X-Amz-Server-Side-Encryption": struct{}{}, + "X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Key": struct{}{}, + "X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, + "X-Amz-Storage-Class": struct{}{}, + "X-Amz-Website-Redirect-Location": struct{}{}, + "X-Amz-Content-Sha256": struct{}{}, + "X-Amz-Tagging": struct{}{}, + }, + }, + Patterns{"X-Amz-Meta-"}, +} + +// Patterns is a list of strings to match against +type Patterns []string + +// IsValid for Patterns checks each pattern and returns if a match has +// been found +func (p Patterns) IsValid(value string) bool { + for _, pattern := range p { + if HasPrefixFold(value, pattern) { + return true + } + } + return false +} + +func HasPrefixFold(s, prefix string) bool { + return len(s) >= len(prefix) && strings.EqualFold(s[0:len(prefix)], prefix) +} + +type AllowList struct { + Rule +} + +// DenyList is a generic Rule for blacklisting +type DenyList struct { + Rule +} + +// IsValid for AllowList checks if the value is within the AllowList +func (b DenyList) IsValid(value string) bool { + return !b.Rule.IsValid(value) +} + +// IsValid will iterate through all rules and see if any rules +// apply to the value and supports nested rules +func (r Rules) IsValid(value string) bool { + for _, rule := range r { + if rule.IsValid(value) { + return true + } + } + return false +} + +// IsValid for the MapRule satisfies whether it exists in the map +func (m MapRule) IsValid(value string) bool { + _, ok := m[value] + return ok +} + +// IsValid for AllowList checks if the value is within the AllowList +func (w AllowList) IsValid(value string) bool { + return w.Rule.IsValid(value) +} + +// IsValid for ExcludeList checks if the value is not within the ExcludeList +func (b ExcludeList) IsValid(value string) bool { + return !b.Rule.IsValid(value) +} + +func BuildCanonicalHeaders(host string, rule Rule, header http.Header, length int64) (signed http.Header, signedHeaders, canonicalHeadersStr string) { + signed = make(http.Header) + + var headers []string + const hostHeader = "host" + headers = append(headers, hostHeader) + signed[hostHeader] = append(signed[hostHeader], host) + + const contentLengthHeader = "content-length" + if length > 0 { + headers = append(headers, contentLengthHeader) + signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10)) + } + + for k, v := range header { + if !rule.IsValid(k) { + continue // ignored header + } + if strings.EqualFold(k, contentLengthHeader) { + // prevent signing already handled content-length header. + continue + } + + lowerCaseKey := strings.ToLower(k) + if _, ok := signed[lowerCaseKey]; ok { + // include additional values + signed[lowerCaseKey] = append(signed[lowerCaseKey], v...) + continue + } + + headers = append(headers, lowerCaseKey) + signed[lowerCaseKey] = v + } + sort.Strings(headers) + + signedHeaders = strings.Join(headers, ";") + + var canonicalHeaders strings.Builder + n := len(headers) + const colon = ':' + for i := 0; i < n; i++ { + if headers[i] == hostHeader { + canonicalHeaders.WriteString(hostHeader) + canonicalHeaders.WriteRune(colon) + canonicalHeaders.WriteString(StripExcessSpaces(host)) + } else { + canonicalHeaders.WriteString(headers[i]) + canonicalHeaders.WriteRune(colon) + // Trim out leading, trailing, and dedup inner spaces from signed header values. + values := signed[headers[i]] + for j, v := range values { + cleanedValue := strings.TrimSpace(StripExcessSpaces(v)) + canonicalHeaders.WriteString(cleanedValue) + if j < len(values)-1 { + canonicalHeaders.WriteRune(',') + } + } + } + canonicalHeaders.WriteRune('\n') + } + canonicalHeadersStr = canonicalHeaders.String() + + return signed, signedHeaders, canonicalHeadersStr +} + +// SanitizeHostForHeader removes default port from host and updates request.Host +func SanitizeHostForHeader(r *http.Request) { + host := getHost(r) + port := portOnly(host) + if port != "" && isDefaultPort(r.URL.Scheme, port) { + r.Host = stripPort(host) + } +} + +type Rule interface { + IsValid(value string) bool +} + +type Rules []Rule + +type ExcludeList struct { + Rule +} + +// MapRule generic Rule for maps +type MapRule map[string]struct{} + +var IgnoredHeaders = Rules{ + ExcludeList{ + MapRule{ + "Authorization": struct{}{}, + "User-Agent": struct{}{}, + "X-Amzn-Trace-Id": struct{}{}, + "Expect": struct{}{}, + }, + }, +} diff --git a/filters/awssigner/internal/hmac.go b/filters/awssigner/internal/hmac.go new file mode 100644 index 0000000000..14c19a3508 --- /dev/null +++ b/filters/awssigner/internal/hmac.go @@ -0,0 +1,12 @@ +package awssigner + +import ( + "crypto/hmac" + "crypto/sha256" +) + +func HMACSHA256(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} diff --git a/filters/awssigner/internal/time.go b/filters/awssigner/internal/time.go new file mode 100644 index 0000000000..330896299a --- /dev/null +++ b/filters/awssigner/internal/time.go @@ -0,0 +1,50 @@ +package awssigner + +import ( + "time" +) + +type SigningTime struct { + time.Time + timeFormat string + shortTimeFormat string +} + +func (m *SigningTime) TimeFormat() string { + return m.format(&m.timeFormat, TimeFormat) +} + +// ShortTimeFormat provides a time formatted of 20060102. +func (m *SigningTime) ShortTimeFormat() string { + return m.format(&m.shortTimeFormat, ShortTimeFormat) +} + +func (m *SigningTime) format(target *string, format string) string { + if len(*target) > 0 { + return *target + } + v := m.Time.Format(format) + *target = v + return v +} + +func isSameDay(x, y time.Time) bool { + xYear, xMonth, xDay := x.Date() + yYear, yMonth, yDay := y.Date() + + if xYear != yYear { + return false + } + + if xMonth != yMonth { + return false + } + + return xDay == yDay +} + +func NewSigningTime(t time.Time) SigningTime { + return SigningTime{ + Time: t, + } +} diff --git a/filters/awssigner/internal/uri.go b/filters/awssigner/internal/uri.go new file mode 100644 index 0000000000..6c3304de71 --- /dev/null +++ b/filters/awssigner/internal/uri.go @@ -0,0 +1,122 @@ +package awssigner + +import ( + "bytes" + "fmt" + "net" + "net/http" + "net/url" + "strings" +) + +var noEscape [256]bool + +func InitializeEscape() { + for i := 0; i < len(noEscape); i++ { + // AWS expects every character except these to be escaped + noEscape[i] = (i >= 'A' && i <= 'Z') || + (i >= 'a' && i <= 'z') || + (i >= '0' && i <= '9') || + i == '-' || + i == '.' || + i == '_' || + i == '~' + } +} + +// EscapePath escapes part of a URL path in Amazon style. +func EscapePath(path string, encodeSep bool) string { + InitializeEscape() //TODO : is getting initialized every time + var buf bytes.Buffer + for i := 0; i < len(path); i++ { + c := path[i] + if noEscape[c] || (c == '/' && !encodeSep) { + buf.WriteByte(c) + } else { + fmt.Fprintf(&buf, "%%%02X", c) + } + } + return buf.String() +} + +func GetURIPath(u *url.URL) string { + var uriPath string + + if len(u.Opaque) > 0 { + const schemeSep, pathSep, queryStart = "//", "/", "?" + + opaque := u.Opaque + // Cut off the query string if present. + if idx := strings.Index(opaque, queryStart); idx >= 0 { + opaque = opaque[:idx] + } + + // Cutout the scheme separator if present. + if strings.Index(opaque, schemeSep) == 0 { + opaque = opaque[len(schemeSep):] + } + + // capture URI path starting with first path separator. + if idx := strings.Index(opaque, pathSep); idx >= 0 { + uriPath = opaque[idx:] + } + } else { + uriPath = u.EscapedPath() + } + + if len(uriPath) == 0 { + uriPath = "/" + } + + return uriPath +} + +// Hostname returns u.Host, without any port number. +// +// If Host is an IPv6 literal with a port number, Hostname returns the +// IPv6 literal without the square brackets. IPv6 literals may include +// a zone identifier. +// +// Copied from the Go 1.8 standard library (net/url) +func stripPort(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return hostport + } + if i := strings.IndexByte(hostport, ']'); i != -1 { + return strings.TrimPrefix(hostport[:i], "[") + } + return hostport[:colon] +} + +// Returns true if the specified URI is using the standard port +// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs) +func isDefaultPort(scheme, port string) bool { + if port == "" { + return true + } + + lowerCaseScheme := strings.ToLower(scheme) + if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") { + return true + } + + return false +} + +// Returns host from request +func getHost(r *http.Request) string { + if r.Host != "" { + return r.Host + } + + return r.URL.Host +} + +func portOnly(hostport string) string { + _, port, err := net.SplitHostPort(hostport) + if err != nil { + return "" + } + return port +} diff --git a/filters/awssigner/internal/utils.go b/filters/awssigner/internal/utils.go new file mode 100644 index 0000000000..5505f70bd0 --- /dev/null +++ b/filters/awssigner/internal/utils.go @@ -0,0 +1,42 @@ +package awssigner + +import ( + "strings" +) + +func StripExcessSpaces(str string) string { + var j, k, l, m, spaces int + // Trim trailing spaces + for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- { + } + + // Trim leading spaces + for k = 0; k < j && str[k] == ' '; k++ { + } + str = str[k : j+1] + + // Strip multiple spaces. + j = strings.Index(str, doubleSpace) + if j < 0 { + return str + } + + buf := []byte(str) + for k, m, l = j, j, len(buf); k < l; k++ { + if buf[k] == ' ' { + if spaces == 0 { + // First space. + buf[m] = buf[k] + m++ + } + spaces++ + } else { + // End of multiple spaces. + spaces = 0 + buf[m] = buf[k] + m++ + } + } + + return string(buf[:m]) +} diff --git a/filters/filters.go b/filters/filters.go index 612de0b723..1c554a95db 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -362,6 +362,7 @@ const ( OpaServeResponseName = "opaServeResponse" OpaServeResponseWithReqBodyName = "opaServeResponseWithReqBody" TLSName = "tlsPassClientCertificates" + AWSSigV4Name = "awsSigv4" // Undocumented filters HealthCheckName = "healthcheck" From 7dd23d098e25c8bdcaf010c76e922e0d250de6d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:21:33 +0200 Subject: [PATCH 0859/1047] build(deps): bump docker/build-push-action from 6.3.0 to 6.4.1 (#3158) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.3.0 to 6.4.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/1a162644f9a7e87d8f4b053101d1d9a712edc18c...1ca370b3a9802c92e886402e0dd88098a2533b12) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ea8414fa1a..929010184d 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c + uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 5a4b32dfb27e3ecd8c72244aa1ef10e4a3a55af4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:24:28 +0200 Subject: [PATCH 0860/1047] build(deps): bump github.com/redis/go-redis/v9 (#3159) Bumps the all-go-mod-patch-and-minor group with 1 update: [github.com/redis/go-redis/v9](https://github.com/redis/go-redis). Updates `github.com/redis/go-redis/v9` from 9.5.4 to 9.6.0 - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/commits/v9.6.0) --- updated-dependencies: - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 820ee54d41..38f010586b 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.5.4 + github.com/redis/go-redis/v9 v9.6.0 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 diff --git a/go.sum b/go.sum index eeb000440e..76de3bd8f7 100644 --- a/go.sum +++ b/go.sum @@ -392,8 +392,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.5.4 h1:vOFYDKKVgrI5u++QvnMT7DksSMYg7Aw/Np4vLJLKLwY= -github.com/redis/go-redis/v9 v9.5.4/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.6.0 h1:NLck+Rab3AOTHw21CGRpvQpgTrAU4sgdCswqGtlhGRA= +github.com/redis/go-redis/v9 v9.6.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= From 35195d0a1eee3821f2ec6629ea9f09f784d396a2 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 22 Jul 2024 18:20:02 +0200 Subject: [PATCH 0861/1047] filters/auth: add second login redirect stub placeholder (#3161) Login redirect stub introduced by #3028 uses `{{authCodeURL}}` placeholder that is a valid mustache template and hence inconvenient to use from mustache templates. This change adds a second `{authCodeURL}` placeholder and `X-Auth-Code-Url` response header. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 13 +++++++------ filters/auth/grant.go | 4 +++- filters/auth/grant_test.go | 12 +++++++++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 68b721059e..0efec75bcb 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1685,8 +1685,9 @@ authz and access control. The filter also supports javascript login redirect stub that can be used e.g. to store location hash. To enable the stub, add preceding [annotate](#annotate) filter with `oauthGrant.loginRedirectStub` key and -HTML content that will be served to the client instead of `307 Temporary Redirect` to the authorization URL. -The filter will replace `{{authCodeURL}}` placeholder in the content with the actual authorization URL. +content that will be served to the client with `200 OK` status instead of `307 Temporary Redirect` to the authorization URL. +The filter will replace `{authCodeURL}` (and `{{authCodeURL}}`) placeholders in the content with the actual authorization URL +and add `X-Auth-Code-Url` response header with the same value. See the [tutorial](../tutorials/auth.md#oauth2-authorization-grant-flow) for step-by-step instructions. @@ -1712,7 +1713,7 @@ single_page_app: if (window.location.hash !== null) { localStorage.setItem('original-location-hash', window.location.hash); } - window.location.replace('{{authCodeURL}}'); + window.location.replace('{authCodeURL}'); @@ -2068,10 +2069,10 @@ The filter also honors the `skip-request-body-parse` of the corresponding [confi ### awsSigv4 This filter signs request using [AWS Sig V4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html_) algorithm. The requests must provide following headers in order for this filter to generate a valid signature. -- `x-amz-accesskey` header must contain a valid AWS access key +- `x-amz-accesskey` header must contain a valid AWS access key - `x-amz-secret` header must contain a valid secret for AWS client being used. - `x-amz-time` header must contain the time in RFC3339 format which this filter can use to generate signature and `X-Amz-Date` header on signed request. This time stamp is considered as the time stamp of generated signature. -- `x-amz-session` must contain valid AWS session token ([see](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html#using-temp-creds-sdk)) to be set as `X-Amz-Security-Token` in signed request when `DisableSessionToken` parameter defined on route is set to false. +- `x-amz-session` must contain valid AWS session token ([see](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html#using-temp-creds-sdk)) to be set as `X-Amz-Security-Token` in signed request when `DisableSessionToken` parameter defined on route is set to false. Filter removes these headers after reading the values. Once the signature is generated, it is appended to existing Authorization header or if there is no exisiting Authorization header, added as new and forwarded to AWS service. @@ -2097,7 +2098,7 @@ This filter expects - `DisableSessionToken` Disables setting the session token on the request as part of signing through X-Amz-Security-Token. This is needed for variations of v4 that present the token elsewhere. - + #### Memory consideration This filter reads the body in memory. This is needed to generate signature as per Signature V4 specs. Special considerations need to be taken when operating the skipper with concurrent requests. diff --git a/filters/auth/grant.go b/filters/auth/grant.go index 43cac8c4ef..a828caab78 100644 --- a/filters/auth/grant.go +++ b/filters/auth/grant.go @@ -92,10 +92,12 @@ func loginRedirectWithOverride(ctx filters.FilterContext, config *OAuthConfig, o if lrs, ok := annotate.GetAnnotations(ctx)["oauthGrant.loginRedirectStub"]; ok { lrs = strings.ReplaceAll(lrs, "{{authCodeURL}}", authCodeURL) + lrs = strings.ReplaceAll(lrs, "{authCodeURL}", authCodeURL) ctx.Serve(&http.Response{ StatusCode: http.StatusOK, Header: http.Header{ - "Content-Length": []string{strconv.Itoa(len(lrs))}, + "Content-Length": []string{strconv.Itoa(len(lrs))}, + "X-Auth-Code-Url": []string{authCodeURL}, }, Body: io.NopCloser(strings.NewReader(lrs)), }) diff --git a/filters/auth/grant_test.go b/filters/auth/grant_test.go index d0d10a4e8e..5f085cdfb8 100644 --- a/filters/auth/grant_test.go +++ b/filters/auth/grant_test.go @@ -1011,7 +1011,7 @@ func TestGrantLoginRedirectStub(t *testing.T) { config := newGrantTestConfig(tokeninfo.URL, provider.URL) - const stubContent = "stub content" + const stubContent = "foo {{authCodeURL}} bar {authCodeURL} baz" routes := eskip.MustParse(fmt.Sprintf(`* -> annotate("oauthGrant.loginRedirectStub", "%s") @@ -1027,6 +1027,12 @@ func TestGrantLoginRedirectStub(t *testing.T) { require.NoError(t, err) assert.Equal(t, rsp.StatusCode, http.StatusOK) - assert.Equal(t, int64(len(stubContent)), rsp.ContentLength) - assert.Equal(t, stubContent, string(body)) + + authCodeUrl := rsp.Header.Get("X-Auth-Code-Url") + assert.True(t, strings.HasPrefix(authCodeUrl, provider.URL)) + + expectedContent := fmt.Sprintf("foo %s bar %s baz", authCodeUrl, authCodeUrl) + + assert.Equal(t, int64(len(expectedContent)), rsp.ContentLength) + assert.Equal(t, expectedContent, string(body)) } From eacb1113f24a867f7498f2a4804b7bc3fff60bc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:12:55 +0200 Subject: [PATCH 0862/1047] build(deps): bump alpine from `b89d9c9` to `0a4eaa0` in /packaging (#3162) Bumps alpine from `b89d9c9` to `0a4eaa0`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 1fbe8f05b4..e4d5b4ccc8 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 +FROM --platform=linux/arm64 alpine@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index 37f5afca38..596318b359 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 +FROM --platform=linux/arm/v7 alpine@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 0be944733e21cd791ab2111b177e568bded72319 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 24 Jul 2024 15:17:50 +0200 Subject: [PATCH 0863/1047] filters/auth: add host opt-out to jwtMetrics and oauthTokeninfoValidate (#3164) Extend configuration of `jwtMetrics` and `oauthTokeninfoValidate` to support opt-out by request host pattern - disable metrics collection and validation when request host matches any of the configured opt-out regular expressions. This can be used to exclude internal cluster domain (*.ingress.cluster.local hosts). Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 14 +++++- filters/auth/jwt_metrics.go | 19 ++++++++ filters/auth/jwt_metrics_test.go | 57 ++++++++++++++++++++--- filters/auth/tokeninfo.go | 35 +++++++++++--- filters/auth/tokeninfo_test.go | 80 ++++++++++++++++++++++++++++++-- 5 files changed, 188 insertions(+), 17 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 0efec75bcb..581a33ce41 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1371,6 +1371,10 @@ annotate("oauth.disabled", "this endpoint is public") -> oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], unauthorizedResponse: 'Authentication required, see https://auth.test/foo'}") ``` +``` +// does not validate the token when request host matches one of the patterns: +oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], optOutHosts: ['^.+[.]domain[.]test$', '^exact.test$'], unauthorizedResponse: 'Authentication required, see https://auth.test/foo'}") +``` ### Tokenintrospection @@ -1606,7 +1610,7 @@ jwtMetrics.custom.GET.example_org.200.invalid-token and therefore requires approximately `count(HTTP methods) * count(Hosts) * count(Statuses) * 8` bytes of additional memory. -The filter does nothing if response status is 4xx or route is opt-out via annotation or state bag value. +The filter does nothing if response status is 4xx or route is opt-out via annotation, state bag value or request host pattern. The filter requires single string argument that is parsed as YAML. For convenience use [flow style format](https://yaml.org/spec/1.2.2/#chapter-7-flow-style-productions). @@ -1615,17 +1619,25 @@ Examples: ``` jwtMetrics("{issuers: ['https://example.com', 'https://example.org']}") +``` +``` // opt-out by annotation annotate("oauth.disabled", "this endpoint is public") -> jwtMetrics("{issuers: ['https://example.com', 'https://example.org'], optOutAnnotations: [oauth.disabled]}") +``` +``` // opt-out by state bag: // oauthTokeninfo* and oauthGrant filters store token info in the state bag using "tokeninfo" key. oauthTokeninfoAnyKV("foo", "bar") -> jwtMetrics("{issuers: ['https://example.com', 'https://example.org'], optOutStateBag: [tokeninfo]}") ``` +``` +// opt-out by matching request host pattern: +jwtMetrics("{issuers: ['https://example.com', 'https://example.org'], optOutHosts: ['^.+[.]domain[.]test$', '^exact.test$']}") +``` ### Forward Token Data #### forwardToken diff --git a/filters/auth/jwt_metrics.go b/filters/auth/jwt_metrics.go index d0db481cf9..1d426049fe 100644 --- a/filters/auth/jwt_metrics.go +++ b/filters/auth/jwt_metrics.go @@ -20,6 +20,9 @@ type ( Issuers []string `json:"issuers,omitempty"` OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` OptOutStateBag []string `json:"optOutStateBag,omitempty"` + OptOutHosts []string `json:"optOutHosts,omitempty"` + + optOutHostsCompiled []*regexp.Regexp } ) @@ -44,6 +47,13 @@ func (s *jwtMetricsSpec) CreateFilter(args []interface{}) (filters.Filter, error return nil, fmt.Errorf("requires single string argument") } + for _, host := range f.OptOutHosts { + if r, err := regexp.Compile(host); err != nil { + return nil, fmt.Errorf("failed to compile opt-out host pattern: %q", host) + } else { + f.optOutHostsCompiled = append(f.optOutHostsCompiled, r) + } + } return f, nil } @@ -68,6 +78,15 @@ func (f *jwtMetricsFilter) Response(ctx filters.FilterContext) { } } + if len(f.optOutHostsCompiled) > 0 { + host := ctx.Request().Host + for _, r := range f.optOutHostsCompiled { + if r.MatchString(host) { + return // opt-out + } + } + } + response := ctx.Response() if response.StatusCode >= 400 && response.StatusCode < 500 { diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index e31425fb9a..ef76bef5fd 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -23,8 +23,10 @@ import ( ) func TestJwtMetrics(t *testing.T) { + const validAuthHeader = "Bearer foobarbaz" + testAuthServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Authorization") != "Bearer foobarbaz" { + if r.Header.Get("Authorization") != validAuthHeader { w.WriteHeader(http.StatusUnauthorized) } else { w.Write([]byte(`{"foo": "bar"}`)) @@ -208,7 +210,7 @@ func TestJwtMetrics(t *testing.T) { `, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ - "Bearer foobarbaz", + validAuthHeader, }}, }, status: http.StatusOK, @@ -223,7 +225,44 @@ func TestJwtMetrics(t *testing.T) { `, request: &http.Request{Method: "GET", Host: "foo.test", Header: http.Header{"Authorization": []string{ - "Bearer foobarbaz", + validAuthHeader, + }}, + }, + status: http.StatusOK, + expected: map[string]int64{ + "jwtMetrics.custom.GET.foo_test.200.invalid-token": 1, + }, + expectedTag: "invalid-token", + }, + { + name: "no metrics when host matches opted-out domain", + filters: `jwtMetrics("{issuers: [foo, bar], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ]}")`, + request: &http.Request{Method: "GET", Host: "foo.domain.test"}, + status: http.StatusOK, + expected: map[string]int64{}, + }, + { + name: "no metrics when second level host matches opted-out domain", + filters: `jwtMetrics("{issuers: [foo, bar], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ]}")`, + request: &http.Request{Method: "GET", Host: "foo.bar.domain.test"}, + status: http.StatusOK, + expected: map[string]int64{}, + }, + { + name: "no metrics when host matches opted-out host exactly", + filters: `jwtMetrics("{issuers: [foo, bar], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ]}")`, + request: &http.Request{Method: "GET", Host: "exact.test"}, + status: http.StatusOK, + expected: map[string]int64{}, + }, + { + name: "counts invalid-token when host does not match", + filters: ` + jwtMetrics("{issuers: [foo, bar], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ]}") + `, + request: &http.Request{Method: "GET", Host: "foo.test", + Header: http.Header{"Authorization": []string{ + validAuthHeader, }}, }, status: http.StatusOK, @@ -294,9 +333,10 @@ func TestJwtMetricsArgs(t *testing.T) { `jwtMetrics("{issuers: [foo, bar]}")`, } { t.Run(def, func(t *testing.T) { - args := eskip.MustParseFilters(def)[0].Args + f := eskip.MustParseFilters(def)[0] + require.Equal(t, spec.Name(), f.Name) - _, err := spec.CreateFilter(args) + _, err := spec.CreateFilter(f.Args) assert.NoError(t, err) }) } @@ -307,11 +347,14 @@ func TestJwtMetricsArgs(t *testing.T) { `jwtMetrics("iss")`, `jwtMetrics(1)`, `jwtMetrics("iss", 1)`, + `jwtMetrics("{optOutHosts: [ '[' ]}")`, // invalid regexp } { t.Run(def, func(t *testing.T) { - args := eskip.MustParseFilters(def)[0].Args + f := eskip.MustParseFilters(def)[0] + require.Equal(t, spec.Name(), f.Name) - _, err := spec.CreateFilter(args) + _, err := spec.CreateFilter(f.Args) + t.Logf("%v", err) assert.Error(t, err) }) } diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index a38dd37f5a..7d33f50389 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "regexp" "strconv" "strings" "time" @@ -62,7 +63,9 @@ type ( config struct { OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` UnauthorizedResponse string `json:"unauthorizedResponse,omitempty"` + OptOutHosts []string `json:"optOutHosts,omitempty"` } + optOutHostsCompiled []*regexp.Regexp } ) @@ -244,12 +247,7 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) if len(sargs) != 1 { return nil, fmt.Errorf("requires single string argument") } - - f := &tokeninfoValidateFilter{client: ac} - if err := yaml.Unmarshal([]byte(sargs[0]), &f.config); err != nil { - return nil, fmt.Errorf("failed to parse configuration") - } - return f, nil + return createTokeninfoValidateFilter(ac, sargs[0]) } f := &tokeninfoFilter{typ: s.typ, client: ac, kv: make(map[string][]string)} @@ -276,6 +274,22 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) return f, nil } +func createTokeninfoValidateFilter(client tokeninfoClient, arg string) (filters.Filter, error) { + f := &tokeninfoValidateFilter{client: client} + if err := yaml.Unmarshal([]byte(arg), &f.config); err != nil { + return nil, fmt.Errorf("failed to parse configuration") + } + + for _, host := range f.config.OptOutHosts { + if r, err := regexp.Compile(host); err != nil { + return nil, fmt.Errorf("failed to compile opt-out host pattern: %q", host) + } else { + f.optOutHostsCompiled = append(f.optOutHostsCompiled, r) + } + } + return f, nil +} + // String prints nicely the tokeninfoFilter configuration based on the // configuration and check used. func (f *tokeninfoFilter) String() string { @@ -444,6 +458,15 @@ func (f *tokeninfoValidateFilter) Request(ctx filters.FilterContext) { } } + if len(f.optOutHostsCompiled) > 0 { + host := ctx.Request().Host + for _, r := range f.optOutHostsCompiled { + if r.MatchString(host) { + return // opt-out from validation + } + } + } + token, ok := getToken(ctx.Request()) if !ok { f.serveUnauthorized(ctx) diff --git a/filters/auth/tokeninfo_test.go b/filters/auth/tokeninfo_test.go index 5e3e93b83e..ffe70b1648 100644 --- a/filters/auth/tokeninfo_test.go +++ b/filters/auth/tokeninfo_test.go @@ -603,9 +603,16 @@ func TestOAuthTokeninfoValidate(t *testing.T) { Timeout: testAuthTimeout, } + const ( + unauthorizedResponse = `Authentication required, see https://auth.test/foo` + + oauthTokeninfoValidateDef = `oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ], unauthorizedResponse: '` + unauthorizedResponse + `'}")` + ) + for _, tc := range []struct { name string precedingFilters string + host string authHeader string expectStatus int expectAuthRequests int32 @@ -669,6 +676,27 @@ func TestOAuthTokeninfoValidate(t *testing.T) { expectStatus: http.StatusOK, expectAuthRequests: 0, }, + { + name: "allow missing token when request host matches opted-out domain", + host: "foo.domain.test", + authHeader: "", + expectStatus: http.StatusOK, + expectAuthRequests: 0, + }, + { + name: "allow missing token when request second level host matches opted-out domain", + host: "foo.bar.domain.test", + authHeader: "", + expectStatus: http.StatusOK, + expectAuthRequests: 0, + }, + { + name: "allow missing token when request host matches opted-out host exactly", + host: "exact.test", + authHeader: "", + expectStatus: http.StatusOK, + expectAuthRequests: 0, + }, } { t.Run(tc.name, func(t *testing.T) { fr := make(filters.Registry) @@ -676,9 +704,7 @@ func TestOAuthTokeninfoValidate(t *testing.T) { fr.Register(NewOAuthTokeninfoAllScopeWithOptions(tio)) fr.Register(NewOAuthTokeninfoValidate(tio)) - const unauthorizedResponse = `Authentication required, see https://auth.test/foo` - - filters := `oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], unauthorizedResponse: '` + unauthorizedResponse + `'}")` + filters := oauthTokeninfoValidateDef if tc.precedingFilters != "" { filters = tc.precedingFilters + " -> " + filters } @@ -691,6 +717,10 @@ func TestOAuthTokeninfoValidate(t *testing.T) { req, err := http.NewRequest("GET", p.URL, nil) require.NoError(t, err) + if tc.host != "" { + req.Host = tc.host + } + if tc.authHeader != "" { req.Header.Set(authHeaderName, tc.authHeader) } @@ -713,3 +743,47 @@ func TestOAuthTokeninfoValidate(t *testing.T) { }) } } + +func TestOAuthTokeninfoValidateArgs(t *testing.T) { + tio := TokeninfoOptions{ + URL: "https://auth.test", + Timeout: testAuthTimeout, + } + spec := NewOAuthTokeninfoValidate(tio) + + t.Run("valid", func(t *testing.T) { + for _, def := range []string{ + `oauthTokeninfoValidate("{}")`, + `oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ]}")`, + `oauthTokeninfoValidate("{unauthorizedResponse: 'Authentication required, see https://auth.test/foo'}")`, + `oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled], optOutHosts: [ '^.+[.]domain[.]test$', '^exact[.]test$' ], unauthorizedResponse: 'Authentication required, see https://auth.test/foo'}")`, + } { + t.Run(def, func(t *testing.T) { + f := eskip.MustParseFilters(def)[0] + require.Equal(t, spec.Name(), f.Name) + + _, err := spec.CreateFilter(f.Args) + assert.NoError(t, err) + }) + } + }) + + t.Run("invalid", func(t *testing.T) { + for _, def := range []string{ + `oauthTokeninfoValidate()`, + `oauthTokeninfoValidate("iss")`, + `oauthTokeninfoValidate(1)`, + `oauthTokeninfoValidate("{optOutAnnotations: [oauth.disabled]}", "extra arg")`, + `oauthTokeninfoValidate("{optOutHosts: [ '[' ]}")`, // invalid regexp + } { + t.Run(def, func(t *testing.T) { + f := eskip.MustParseFilters(def)[0] + require.Equal(t, spec.Name(), f.Name) + + _, err := spec.CreateFilter(f.Args) + t.Logf("%v", err) + assert.Error(t, err) + }) + } + }) +} From 9006efd8f2118fee6528fa91ee46ec53e4502d24 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 24 Jul 2024 17:06:48 +0200 Subject: [PATCH 0864/1047] docs: fix filter name (#3165) There is not forwardTokenPart filter. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 581a33ce41..1e89755ded 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1580,7 +1580,7 @@ secureOauthTokenintrospectionAllKV("issuerURL", "", "", "k1", "v1", "k2", "v2") The filter parses bearer jwt token from Authorization header and validates the signature using public keys discovered via /.well-known/openid-configuration endpoint. Takes issuer url as single parameter. -The filter stores token claims into the state bag where they can be used by oidcClaimsQuery() or forwardTokenPart() +The filter stores token claims into the state bag where they can be used by [oidcClaimsQuery](#oidcclaimsquery), [forwardToken](#forwardtoken) or [forwardTokenField](#forwardtokenfield) filters. Examples: From b5c4833382dd8d0e86040b5dc3415319c13b5599 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 24 Jul 2024 17:40:25 +0200 Subject: [PATCH 0865/1047] .zappr.yaml: configure repository type (#3166) This is required by COMPR checker. Signed-off-by: Alexander Yastrebov --- .zappr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.zappr.yaml b/.zappr.yaml index 01ac026dcc..413f5c8494 100644 --- a/.zappr.yaml +++ b/.zappr.yaml @@ -18,3 +18,4 @@ pull-request: - documentation - dependencies X-Zalando-Team: "teapot" +X-Zalando-Type: code From 4fdc0887f169aa420f4d85484022b01b64b4bd66 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 24 Jul 2024 18:49:36 +0200 Subject: [PATCH 0866/1047] docs/reference/filters: fix typos (#3167) Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 1e89755ded..7ef280f97e 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -5,7 +5,7 @@ The parameters can be strings, regex or float64 / int * `string` is a string surrounded by double quotes (`"`) * `regex` is a regular expression, surrounded by `/`, e.g. `/^www\.example\.org(:\d+)?$/` * `int` / `float64` are usual (decimal) numbers like `401` or `1.23456` -* `time` is a string in double quotes, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) +* `time` is a string in double quotes, parsable by [time.Duration](https://godoc.org/time#ParseDuration)) Filters are a generic tool and can change HTTP header and body in the request and response path. Filter can be chained using the arrow operator `->`. @@ -593,7 +593,7 @@ Enable adding artificial latency Parameters: -* latency in milliseconds (int) or in `time` as a string in double quotes, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) +* latency in milliseconds (int) or in `time` as a string in double quotes, parsable by [time.Duration](https://godoc.org/time#ParseDuration)) Example: @@ -1264,7 +1264,7 @@ this filter. The filter accepts variable number of string arguments, which are used to validate the incoming token from the `Authorization: Bearer ` header. There are two rejection scenarios for this filter. If the token -is not successfully validated by the oauth server, then a 401 Unauthorised +is not successfully validated by the oauth server, then a 401 Unauthorized response will be returned. However, if the token is successfully validated but the required scope match isn't satisfied, then a 403 Forbidden response will be returned. If any of the configured scopes from the filter is found @@ -1285,7 +1285,7 @@ this filter. The filter accepts variable number of string arguments, which are used to validate the incoming token from the `Authorization: Bearer ` header. There are two rejection scenarios for this filter. If the token -is not successfully validated by the oauth server, then a 401 Unauthorised +is not successfully validated by the oauth server, then a 401 Unauthorized response will be returned. However, if the token is successfully validated but the required scope match isn't satisfied, then a 403 Forbidden response will be returned. If all of the configured scopes from the filter are found @@ -1307,7 +1307,7 @@ The filter accepts an even number of variable arguments of type string, which are used to validate the incoming token from the `Authorization: Bearer ` header. There are two rejection scenarios for this filter. If the token is not successfully validated by the oauth -server, then a 401 Unauthorised response will be returned. However, +server, then a 401 Unauthorized response will be returned. However, if the token is successfully validated but the required scope match isn't satisfied, then a 403 Forbidden response will be returned. If any of the configured key value pairs from the filter is found @@ -1330,7 +1330,7 @@ The filter accepts an even number of variable arguments of type string, which are used to validate the incoming token from the `Authorization: Bearer ` header. There are two rejection scenarios for this filter. If the token is not successfully validated -by the oauth server, then a 401 Unauthorised response will be +by the oauth server, then a 401 Unauthorized response will be returned. However, if the token is successfully validated but the required scope match isn't satisfied, then a 403 Forbidden response will be returned. If all of the configured key value pairs from @@ -1979,7 +1979,7 @@ The data flow in case the policy disallows the request looks like this ```ascii ┌──────────────────┐ ┌────────────────────┐ - (1) Request │ Skipper │ │ Target Applicatio │ + (1) Request │ Skipper │ │ Target Application │ ─────────────┤ ├──────────────►│ │ │ │ │ │ (4) Response│ (2)│ ▲ (3) │ │ │ @@ -2210,9 +2210,9 @@ open state. If all succeed, it goes to closed state again. Parameters: * number of consecutive failures to open (int) -* timeout (time string, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional +* timeout (time string, parsable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional * half-open requests (int) - optional -* idle-ttl (time string, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional +* idle-ttl (time string, parsable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional See also the [circuit breaker docs](https://godoc.org/github.com/zalando/skipper/circuit). @@ -2230,9 +2230,9 @@ Parameters: * number of consecutive failures to open (int) * sliding window (int) -* timeout (time string, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional +* timeout (time string, parsable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional * half-open requests (int) - optional -* idle-ttl (time string, parseable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional +* idle-ttl (time string, parsable by [time.Duration](https://godoc.org/time#ParseDuration)) - optional See also the [circuit breaker docs](https://godoc.org/github.com/zalando/skipper/circuit). From 68d7e2b0cc8bf3c0641bce9827cc1c28fdf48c6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:16:05 +0200 Subject: [PATCH 0867/1047] build(deps): bump docker/setup-qemu-action from 3.1.0 to 3.2.0 (#3175) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/5927c834f5b4fdf503fca6f4c7eccda82949e1ee...49b3bc8e6bdd4a60e6116a5414239cba5943d3cf) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 929010184d..8b9bb0ee9e 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -47,7 +47,7 @@ jobs: make build.linux - name: Set up QEMU - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up Docker Buildx uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 From 5e6d8972d8018b9c0e7fb85ecaa265274a689b4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:18:28 +0200 Subject: [PATCH 0868/1047] build(deps): bump docker/login-action from 3.2.0 to 3.3.0 (#3174) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/0d4c9c5ea7693da7b068278f7b52bda2a190a446...9780b0c442fbb1117ed29e0efdff1e18412f7567) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 8b9bb0ee9e..448f4a422c 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -32,7 +32,7 @@ jobs: check-latest: true - name: Login to Github Container Registry - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -54,7 +54,7 @@ jobs: - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 with: registry: ghcr.io username: ${{ github.actor }} From 0560108016850f6e998de863275389d54aa9c32f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:18:50 +0200 Subject: [PATCH 0869/1047] build(deps): bump docker/build-push-action from 6.4.1 to 6.5.0 (#3173) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.4.1 to 6.5.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/1ca370b3a9802c92e886402e0dd88098a2533b12...5176d81f87c23d6fc96624dfdbcd9f3830bbe445) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 448f4a422c..a140ad7e05 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 + uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From f85f3a6c848d64c9ce4ba9542196b4ac1c2101fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:20:22 +0200 Subject: [PATCH 0870/1047] build(deps): bump ossf/scorecard-action from 2.3.3 to 2.4.0 (#3172) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.3.3 to 2.4.0. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/dc50aa9510b46c811795eb24b2f1ba02a914e534...62b2cac7ed8198b15735ed49ab1e5cf35480ba46) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index ba7060836c..63c3082941 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -39,7 +39,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif From 5c52de830eb9dc35ba993fd0f8f6856393bcb1ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:21:43 +0200 Subject: [PATCH 0871/1047] build(deps): bump amazonlinux from `d5b9690` to `7edb8e2` in /fuzz (#3170) Bumps amazonlinux from `d5b9690` to `7edb8e2`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 6002e9f62e..2f44c032c9 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:d5b9690df4ce1a70d738d8c8f7a6b4275008df95d4ddc2435e4e8e4352db61a5 +FROM amazonlinux:2023@sha256:7edb8e2f8b0a114acabae813a54144187343bf59e41bbd3e79a2cf187ca02c3d WORKDIR /workspace From f81f17aa473382fc4ebbdc29690a83b053ca1771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:23:48 +0200 Subject: [PATCH 0872/1047] build(deps): bump docker/setup-buildx-action from 3.4.0 to 3.6.0 (#3176) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.4.0 to 3.6.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/4fd812986e6c8c2a69e18311145f9371337f27d4...3d68780484996aa9d417bb9016193885cdf1f299) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index a140ad7e05..ff3f831408 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 + uses: docker/setup-buildx-action@3d68780484996aa9d417bb9016193885cdf1f299 - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From 7d28a91c1da189f0957754626539269323fa3dfd Mon Sep 17 00:00:00 2001 From: JanardhanSharma Date: Thu, 1 Aug 2024 10:41:10 +0200 Subject: [PATCH 0873/1047] Facilitate OPA decision correlation with business flows (#3160) Signed-off-by: JanardhanSharma --- docs/tutorials/auth.md | 8 +++ filters/openpolicyagent/evaluation.go | 34 ++++++++++- .../opaauthorizerequest_test.go | 56 +++++++++++++++++-- filters/openpolicyagent/openpolicyagent.go | 6 +- .../openpolicyagent/openpolicyagent_test.go | 18 ++++-- 5 files changed, 108 insertions(+), 14 deletions(-) diff --git a/docs/tutorials/auth.md b/docs/tutorials/auth.md index 14bf04e1f7..b8db9b7135 100644 --- a/docs/tutorials/auth.md +++ b/docs/tutorials/auth.md @@ -466,6 +466,14 @@ The second argument is parsed as YAML, cannot be nested and values need to be st In Rego this can be used like this `input.attributes.contextExtensions["com.mycompany.myprop"] == "my value"` +### Decision ID in Policies + +Each evaluation yields a distinct decision, identifiable by its unique decision ID. +This decision ID can be located within the input at: +`input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id` +Typical use cases are either propagation of the decision ID to downstream systems or returning it as part of the response. As an example this can allow to trouble shoot deny requests by looking up details using the full decision in a control plane. + + ### Quick Start Rego Playground A quick way without setting up Backend APIs is to use the [Rego Playground](https://play.openpolicyagent.org/). diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go index 03f56b450a..2f38958617 100644 --- a/filters/openpolicyagent/evaluation.go +++ b/filters/openpolicyagent/evaluation.go @@ -3,8 +3,7 @@ package openpolicyagent import ( "context" "fmt" - "time" - + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" "github.com/open-policy-agent/opa-envoy-plugin/opa/decisionlog" @@ -13,6 +12,8 @@ import ( "github.com/open-policy-agent/opa/server" "github.com/open-policy-agent/opa/topdown" "github.com/opentracing/opentracing-go" + pbstruct "google.golang.org/protobuf/types/known/structpb" + "time" ) func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3.CheckRequest) (*envoyauth.EvalResult, error) { @@ -23,6 +24,12 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return nil, err } + err = setDecisionIdInRequest(req, decisionId) + if err != nil { + opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to set decision ID in Request.") + return nil, err + } + result, stopeval, err := envoyauth.NewEvalResult(withDecisionID(decisionId)) if err != nil { opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to generate new result with decision ID.") @@ -71,6 +78,29 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return result, nil } +func setDecisionIdInRequest(req *ext_authz_v3.CheckRequest, decisionId string) error { + if req.Attributes.MetadataContext == nil { + req.Attributes.MetadataContext = &ext_authz_v3_core.Metadata{ + FilterMetadata: map[string]*pbstruct.Struct{}, + } + } + + filterMeta, err := FormOpenPolicyAgentMetaDataObject(decisionId) + if err != nil { + return err + } + req.Attributes.MetadataContext.FilterMetadata["open_policy_agent"] = filterMeta + return nil +} + +func FormOpenPolicyAgentMetaDataObject(decisionId string) (*pbstruct.Struct, error) { + + innerFields := make(map[string]interface{}) + innerFields["decision_id"] = decisionId + + return pbstruct.NewStruct(innerFields) +} + func (opa *OpenPolicyAgentInstance) logDecision(ctx context.Context, input interface{}, result *envoyauth.EvalResult, err error) error { info := &server.Info{ Timestamp: time.Now(), diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 1e48c63c8f..ab7616086c 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -255,6 +255,21 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Decision id in request header", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object_decision_id_in_header", + requestMethod: "POST", + body: `{ "target_id" : "123456" }`, + requestHeaders: map[string][]string{"content-type": {"application/json"}}, + requestPath: "/allow/structured", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: map[string][]string{"Decision-Id": {"some-random-decision-id-generated-during-evaluation"}}, + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Invalid UTF-8 in Path", filterName: "opaAuthorizeRequest", @@ -345,8 +360,21 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow_body { input.parsed_body.target_id == "123456" - } - `, + } + + decision_id := input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id + + allow_object_decision_id_in_header = response { + input.parsed_path = ["allow", "structured"] + decision_id + response := { + "allowed": true, + "response_headers_to_add": { + "decision-id": decision_id + } + } + } + `, }), ) @@ -374,10 +402,23 @@ func TestAuthorizeRequestFilter(t *testing.T) { } }`, opaControlPlane.URL(), ti.regoQuery)) + envoyMetaDataConfig := []byte(`{ + "filter_metadata": { + "envoy.filters.http.header_to_metadata": { + "policy_type": "ingress" + } + } + }`) + + opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0) + opts = append(opts, + openpolicyagent.WithConfigTemplate(config), + openpolicyagent.WithEnvoyMetadataBytes(envoyMetaDataConfig)) + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) - ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, opts...) fr.Register(ftSpec) - ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, opts...) fr.Register(ftSpec) r := eskip.MustParse(fmt.Sprintf(`* -> %s("%s", "%s") %s -> "%s"`, ti.filterName, ti.bundleName, ti.contextExtensions, ti.extraeskip, clientServer.URL)) @@ -436,7 +477,12 @@ func isHeadersPresent(t *testing.T, expectedHeaders http.Header, headers http.He return false } - assert.ElementsMatch(t, expectedValues, actualValues) + // since decision id is randomly generated we are just checking for not nil + if headerName == "Decision-Id" { + assert.NotNil(t, actualValues) + } else { + assert.ElementsMatch(t, expectedValues, actualValues) + } } return true } diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 3144c6c2ea..04a8845f63 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "google.golang.org/protobuf/proto" "io" "net/http" "os" @@ -205,7 +206,10 @@ func WithStartupTimeout(timeout time.Duration) func(*OpenPolicyAgentInstanceConf } func (cfg *OpenPolicyAgentInstanceConfig) GetEnvoyMetadata() *ext_authz_v3_core.Metadata { - return cfg.envoyMetadata + if cfg.envoyMetadata != nil { + return proto.Clone(cfg.envoyMetadata).(*ext_authz_v3_core.Metadata) + } + return nil } func NewOpenPolicyAgentConfig(opts ...func(*OpenPolicyAgentInstanceConfig) error) (*OpenPolicyAgentInstanceConfig, error) { diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go index 117e8522bb..6e15c859eb 100644 --- a/filters/openpolicyagent/openpolicyagent_test.go +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + pbstruct "google.golang.org/protobuf/types/known/structpb" "io" "net/http" "os" @@ -29,7 +30,6 @@ import ( "github.com/zalando/skipper/routing" "github.com/zalando/skipper/tracing/tracingtest" "google.golang.org/protobuf/encoding/protojson" - _struct "google.golang.org/protobuf/types/known/structpb" ) type MockOpenPolicyAgentFilter struct { @@ -63,7 +63,7 @@ func TestInterpolateTemplate(t *testing.T) { func TestLoadEnvoyMetadata(t *testing.T) { cfg := &OpenPolicyAgentInstanceConfig{} - WithEnvoyMetadataBytes([]byte(` + _ = WithEnvoyMetadataBytes([]byte(` { "filter_metadata": { "envoy.filters.http.header_to_metadata": { @@ -74,11 +74,11 @@ func TestLoadEnvoyMetadata(t *testing.T) { `))(cfg) expectedBytes, err := protojson.Marshal(&ext_authz_v3_core.Metadata{ - FilterMetadata: map[string]*_struct.Struct{ + FilterMetadata: map[string]*pbstruct.Struct{ "envoy.filters.http.header_to_metadata": { - Fields: map[string]*_struct.Value{ + Fields: map[string]*pbstruct.Value{ "policy_type": { - Kind: &_struct.Value_StringValue{StringValue: "ingress"}, + Kind: &pbstruct.Value_StringValue{StringValue: "ingress"}, }, }, }, @@ -411,7 +411,13 @@ func TestEval(t *testing.T) { span := tracer.StartSpan("open-policy-agent") ctx := opentracing.ContextWithSpan(context.Background(), span) - result, err := inst.Eval(ctx, &authv3.CheckRequest{}) + result, err := inst.Eval(ctx, &authv3.CheckRequest{ + Attributes: &authv3.AttributeContext{ + Request: nil, + ContextExtensions: nil, + MetadataContext: nil, + }, + }) assert.NoError(t, err) allowed, err := result.IsAllowed() From ec85ab991471125f54de34512b52cca392834959 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:06:32 +0200 Subject: [PATCH 0874/1047] build(deps): bump docker/build-push-action from 6.5.0 to 6.6.1 (#3184) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.5.0 to 6.6.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/5176d81f87c23d6fc96624dfdbcd9f3830bbe445...16ebe778df0e7752d2cfcbd924afdbbd89c1a755) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ff3f831408..c1f8ea19a3 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 + uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From b206190ad1f7b6fcec7b1b0d6ff1e57b06a6194b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:07:02 +0200 Subject: [PATCH 0875/1047] build(deps): bump actions/upload-artifact from 4.3.4 to 4.3.6 (#3185) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.3.6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...834a144ee995460fba8ed112a2fc961b36a5ec5a) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 63c3082941..fd3c74cacb 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: SARIF file path: results.sarif From 4e989acb4a6e8a67a7cbff803ac3e95a0afcdbe1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:07:58 +0200 Subject: [PATCH 0876/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 10 updates (#3186) Bumps the all-go-mod-patch-and-minor group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) | `0.66.0` | `0.67.1` | | [github.com/open-policy-agent/opa-envoy-plugin](https://github.com/open-policy-agent/opa-envoy-plugin) | `0.66.0-envoy-2` | `0.67.1-envoy` | | [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) | `9.6.0` | `9.6.1` | | [github.com/tidwall/gjson](https://github.com/tidwall/gjson) | `1.17.1` | `1.17.3` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.25.0` | `0.26.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.27.0` | `0.28.0` | | [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.21.0` | `0.22.0` | | [golang.org/x/time](https://github.com/golang/time) | `0.5.0` | `0.6.0` | Updates `github.com/open-policy-agent/opa` from 0.66.0 to 0.67.1 - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.66.0...v0.67.1) Updates `github.com/open-policy-agent/opa-envoy-plugin` from 0.66.0-envoy-2 to 0.67.1-envoy - [Release notes](https://github.com/open-policy-agent/opa-envoy-plugin/releases) - [Changelog](https://github.com/open-policy-agent/opa-envoy-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa-envoy-plugin/compare/v0.66.0-envoy-2...v0.67.1-envoy) Updates `github.com/redis/go-redis/v9` from 9.6.0 to 9.6.1 - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.6.0...v9.6.1) Updates `github.com/tidwall/gjson` from 1.17.1 to 1.17.3 - [Commits](https://github.com/tidwall/gjson/compare/v1.17.1...v1.17.3) Updates `golang.org/x/crypto` from 0.25.0 to 0.26.0 - [Commits](https://github.com/golang/crypto/compare/v0.25.0...v0.26.0) Updates `golang.org/x/net` from 0.27.0 to 0.28.0 - [Commits](https://github.com/golang/net/compare/v0.27.0...v0.28.0) Updates `golang.org/x/oauth2` from 0.21.0 to 0.22.0 - [Commits](https://github.com/golang/oauth2/compare/v0.21.0...v0.22.0) Updates `golang.org/x/sync` from 0.7.0 to 0.8.0 - [Commits](https://github.com/golang/sync/compare/v0.7.0...v0.8.0) Updates `golang.org/x/term` from 0.22.0 to 0.23.0 - [Commits](https://github.com/golang/term/compare/v0.22.0...v0.23.0) Updates `golang.org/x/time` from 0.5.0 to 0.6.0 - [Commits](https://github.com/golang/time/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/open-policy-agent/opa-envoy-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/tidwall/gjson dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/time dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 63 ++++++++++++++-------------- go.sum | 130 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 98 insertions(+), 95 deletions(-) diff --git a/go.mod b/go.mod index 38f010586b..6841339ed0 100644 --- a/go.mod +++ b/go.mod @@ -27,34 +27,34 @@ require ( github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.61 github.com/oklog/ulid v1.3.1 - github.com/open-policy-agent/opa v0.66.0 - github.com/open-policy-agent/opa-envoy-plugin v0.66.0-envoy-2 + github.com/open-policy-agent/opa v0.67.1 + github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.6.0 + github.com/redis/go-redis/v9 v9.6.1 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 github.com/stretchr/testify v1.9.0 github.com/szuecs/rate-limit-buffer v0.9.0 github.com/testcontainers/testcontainers-go v0.32.0 - github.com/tidwall/gjson v1.17.1 + github.com/tidwall/gjson v1.17.3 github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.27.0 - golang.org/x/oauth2 v0.21.0 - golang.org/x/sync v0.7.0 - golang.org/x/term v0.22.0 - golang.org/x/time v0.5.0 + golang.org/x/net v0.28.0 + golang.org/x/oauth2 v0.22.0 + golang.org/x/sync v0.8.0 + golang.org/x/term v0.23.0 + golang.org/x/time v0.6.0 google.golang.org/protobuf v1.34.2 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 @@ -69,19 +69,20 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.11.5 // indirect + github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 // indirect - github.com/containerd/containerd v1.7.18 // indirect + github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect + github.com/containerd/containerd v1.7.20 // indirect github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/badger/v3 v3.2103.5 // indirect @@ -101,14 +102,14 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.0 // indirect + github.com/golang/glog v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.0 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect @@ -157,24 +158,24 @@ require ( github.com/yashtewari/glob-intersection v0.2.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/sdk v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect + golang.org/x/tools v0.23.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/grpc v1.64.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.3.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 76de3bd8f7..1dedb2223f 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38= -github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= +github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= +github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -56,8 +56,8 @@ github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HV github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -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.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -72,18 +72,20 @@ github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 h1:cJ1E8ZwZLfercT github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199/go.mod h1:jC+zrjHA5CaxJzn+tojIoIOzSp/6BlkRWXnMlxNkB+g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= -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/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= -github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ= +github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= @@ -181,8 +183,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -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/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -227,8 +229,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -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/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -340,10 +342,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/open-policy-agent/opa v0.66.0 h1:DbrvfJQja0FBRcPOB3Z/BOckocN+M4ApNWyNhSRJt0w= -github.com/open-policy-agent/opa v0.66.0/go.mod h1:EIgNnJcol7AvQR/IcWLwL13k64gHVbNAVG46b2G+/EY= -github.com/open-policy-agent/opa-envoy-plugin v0.66.0-envoy-2 h1:lkCFy03fL55INkwlYSisZyw7X00nJIswWl94a2RB9gw= -github.com/open-policy-agent/opa-envoy-plugin v0.66.0-envoy-2/go.mod h1:pi1KbfVr86cnYzgxeMD7GwRHLL3ZJNNW2U1M6fwy4xg= +github.com/open-policy-agent/opa v0.67.1 h1:rzy26J6g1X+CKknAcx0Vfbt41KqjuSzx4E0A8DAZf3E= +github.com/open-policy-agent/opa v0.67.1/go.mod h1:aqKlHc8E2VAAylYE9x09zJYr/fYzGX+JKne89UGqFzk= +github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy h1:u8gvJx13GaPDm9koAgwGOJ1kXlh1vRP/EyrNyF9F8Lc= +github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy/go.mod h1:fW+AG2SaKmlrjuo202o2cTZlJIyl85Y4n/CJvjmc4mA= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -392,12 +394,12 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.6.0 h1:NLck+Rab3AOTHw21CGRpvQpgTrAU4sgdCswqGtlhGRA= -github.com/redis/go-redis/v9 v9.6.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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 v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 h1:L644WnBAUw4546Wrt52yzuSPoV24t0ArlMwc5iRr8U0= github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091/go.mod h1:qZKxttzn8iyVLtc7edFrmQper3FUBJsc/rHCONN2wIQ= @@ -453,8 +455,8 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= -github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= -github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= +github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= @@ -490,24 +492,24 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +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.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.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.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -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/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.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +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.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= @@ -522,8 +524,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -544,8 +546,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -561,11 +563,11 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -574,8 +576,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -603,17 +605,17 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -630,8 +632,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= @@ -650,10 +652,10 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -661,8 +663,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= 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= From 877dd059dcb7ba52391021155795564a690cce91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:12:22 +0200 Subject: [PATCH 0877/1047] build(deps): bump docker/setup-buildx-action from 3.6.0 to 3.6.1 (#3181) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/3d68780484996aa9d417bb9016193885cdf1f299...988b5a0280414f521da01fcc63a27aeeb4b104db) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index c1f8ea19a3..72c54847a4 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up Docker Buildx - uses: docker/setup-buildx-action@3d68780484996aa9d417bb9016193885cdf1f299 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From 302ec7aa1f5e2148ce067b0a3b16f8ccd3e7a0ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:13:02 +0200 Subject: [PATCH 0878/1047] build(deps): bump amazonlinux from `7edb8e2` to `ef1a9c8` in /fuzz (#3183) Bumps amazonlinux from `7edb8e2` to `ef1a9c8`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 2f44c032c9..96bf091c57 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:7edb8e2f8b0a114acabae813a54144187343bf59e41bbd3e79a2cf187ca02c3d +FROM amazonlinux:2023@sha256:ef1a9c856a0d3b2d581d0d9261540df3c6ab3a7f752546603c93f954def936a3 WORKDIR /workspace From d6c49d7cdecabcd918dcaf83af6e7ca2e7cd6d16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:32:45 +0200 Subject: [PATCH 0879/1047] build(deps): bump github.com/docker/docker (#3187) Bumps the go_modules group with 1 update in the / directory: [github.com/docker/docker](https://github.com/docker/docker). Updates `github.com/docker/docker` from 27.0.3+incompatible to 27.1.1+incompatible - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v27.0.3...v27.1.1) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6841339ed0..1934bfeaf7 100644 --- a/go.mod +++ b/go.mod @@ -89,7 +89,7 @@ require ( github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v27.0.3+incompatible // indirect + github.com/docker/docker v27.1.1+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect diff --git a/go.sum b/go.sum index 1dedb2223f..baf9fa056a 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZs github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE= -github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= From 05bd5f846feaa8a50ef20216b9733b930e06275f Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Wed, 14 Aug 2024 15:38:15 +0200 Subject: [PATCH 0880/1047] staticcheck: fix pipeline errors (#3190) pipeline link: https://github.com/zalando/skipper/actions/runs/10387833964/job/28762375336\?pr\=3188 Signed-off-by: Mustafa Abdelrahman --- config/config.go | 2 +- filters/apiusagemonitoring/spec.go | 2 +- filters/tls/pass_client_test.go | 2 +- swarm/swarm.go | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 77e3ab224c..d9cb8d25c5 100644 --- a/config/config.go +++ b/config/config.go @@ -959,7 +959,7 @@ func (c *Config) ToOptions() skipper.Options { options.ProxyFlags |= proxy.PatchPath } - if c.Certificates != nil && len(c.Certificates) > 0 { + if len(c.Certificates) > 0 { options.ClientTLS = &tls.Config{ Certificates: c.Certificates, MinVersion: c.getMinTLSVersion(), diff --git a/filters/apiusagemonitoring/spec.go b/filters/apiusagemonitoring/spec.go index bbd5425db6..fa7099be27 100644 --- a/filters/apiusagemonitoring/spec.go +++ b/filters/apiusagemonitoring/spec.go @@ -235,7 +235,7 @@ func (s *apiUsageMonitoringSpec) buildPathInfoListFromConfiguration(apis []*apiC continue } - if api.PathTemplates == nil || len(api.PathTemplates) == 0 { + if len(api.PathTemplates) == 0 { s.warnf("args[%d] ignored: does not specify any path template", apiIndex) continue } diff --git a/filters/tls/pass_client_test.go b/filters/tls/pass_client_test.go index 3caa1a6686..16e0a87f94 100644 --- a/filters/tls/pass_client_test.go +++ b/filters/tls/pass_client_test.go @@ -199,7 +199,7 @@ func TestPassTLSClientCert_PEM(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://example.com/foo", nil) require.NoError(t, err) - if test.certContents != nil && len(test.certContents) > 0 { + if len(test.certContents) > 0 { req.TLS = buildTLSWith(test.certContents) } diff --git a/swarm/swarm.go b/swarm/swarm.go index 6e4b98e33c..6b1eb81dbe 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "math" "net" "time" @@ -156,7 +157,7 @@ func newKubernetesSwarm(o Options) (*Swarm, error) { } o.KubernetesOptions.KubernetesAPIBaseURL = u - if o.SwarmPort == 0 || o.SwarmPort >= 65535 { + if o.SwarmPort == 0 || o.SwarmPort == math.MaxUint16 { log.Errorf("Wrong SwarmPort %d, set to default %d instead", o.SwarmPort, DefaultPort) o.SwarmPort = DefaultPort } From 7d724fe7b24c3235f7a28f1bb98ee44e2da834ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=C5=ADlo=20Ebermann?= Date: Wed, 14 Aug 2024 16:47:09 +0200 Subject: [PATCH 0881/1047] Fix list formatting in filters.md (#3188) The Markdown processor used on the reference docs (e.g. https://opensource.zalando.com/skipper/reference/filters/#awssigv4) requires an empty line before a list to recognize it as such (and not have the content part of the paragraph before it). This adds two such line breaks. (The difference is not visible on the Github preview, as its processor doesn't require the empty line.) Signed-off-by: Paul Ebermann --- docs/reference/filters.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 7ef280f97e..2a2f223fc3 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -2081,6 +2081,7 @@ The filter also honors the `skip-request-body-parse` of the corresponding [confi ### awsSigv4 This filter signs request using [AWS Sig V4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html_) algorithm. The requests must provide following headers in order for this filter to generate a valid signature. + - `x-amz-accesskey` header must contain a valid AWS access key - `x-amz-secret` header must contain a valid secret for AWS client being used. - `x-amz-time` header must contain the time in RFC3339 format which this filter can use to generate signature and `X-Amz-Date` header on signed request. This time stamp is considered as the time stamp of generated signature. @@ -2093,7 +2094,8 @@ awsSigv4 filter can be defined on a route as `awsSigv4(", "", < An example of route with awsSigv4 filter is `editorRoute: * -> awsSigv4("dynamodb" , "us-east-1", false, false, false) -> "https://dynamodb.us-east-1.amazonaws.com";` -This filter expects +This filter expects: + - `Service` An aws service name. Please refer valid service names from service endpoint. For example if service endpoint is https://dynamodb.us-east-1.amazonaws.com, then service is dynamodb From c0ae98a096469804471c7e527a6f4650ca226b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 16 Aug 2024 15:22:27 +0200 Subject: [PATCH 0882/1047] fix: test requires Go version 1.22 (#3193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1934bfeaf7..a277469c4e 100644 --- a/go.mod +++ b/go.mod @@ -181,4 +181,4 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -go 1.21 +go 1.22 From 6cec9481930f6966e7b324e7524f9d32e37b011f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Fri, 16 Aug 2024 15:37:08 +0200 Subject: [PATCH 0883/1047] fix: dependabot should update clusterfuzzlite (#3194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandor Szücs --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d9f3ef9062..a32cd454e0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,3 +26,7 @@ updates: directory: "/fuzz" schedule: interval: "weekly" + - package-ecosystem: "docker" + directory: "/.clusterfuzzlite" + schedule: + interval: "weekly" From d7ff7a33b937ab9e63d21b7ebbca68275849774b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:13:41 +0200 Subject: [PATCH 0884/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3195) Bumps oss-fuzz-base/base-builder-go from `f7970e7` to `54cf783`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 6df6c3b474..3726879ae4 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:f7970e735dcd3c5f360ae92b40a20414d70ef3b534ff6f3566dcad1b1cb2ec4c +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:54cf78317a403958103759e226f74cc139bf751d622d09af8668dccff201b9b9 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 87934e488a4778c28cefd458faeb3aeb3a6e3a77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:29:05 +0200 Subject: [PATCH 0885/1047] build(deps): bump the all-go-mod-patch-and-minor group with 3 updates (#3198) Bumps the all-go-mod-patch-and-minor group with 3 updates: [github.com/envoyproxy/go-control-plane](https://github.com/envoyproxy/go-control-plane), [github.com/miekg/dns](https://github.com/miekg/dns) and [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang). Updates `github.com/envoyproxy/go-control-plane` from 0.12.0 to 0.13.0 - [Release notes](https://github.com/envoyproxy/go-control-plane/releases) - [Changelog](https://github.com/envoyproxy/go-control-plane/blob/main/CHANGELOG.md) - [Commits](https://github.com/envoyproxy/go-control-plane/compare/v0.12.0...v0.13.0) Updates `github.com/miekg/dns` from 1.1.61 to 1.1.62 - [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release) - [Commits](https://github.com/miekg/dns/compare/v1.1.61...v1.1.62) Updates `github.com/prometheus/client_golang` from 1.19.1 to 1.20.0 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.19.1...v1.20.0) --- updated-dependencies: - dependency-name: github.com/envoyproxy/go-control-plane dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/miekg/dns dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 14 ++++++++------ go.sum | 30 ++++++++++++++++++------------ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index a277469c4e..5c89fb6b7a 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 github.com/docker/go-connections v0.5.0 - github.com/envoyproxy/go-control-plane v0.12.0 + github.com/envoyproxy/go-control-plane v0.13.0 github.com/ghodss/yaml v1.0.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-cmp v0.6.0 @@ -25,13 +25,13 @@ require ( github.com/hashicorp/memberlist v0.5.1 github.com/instana/go-sensor v1.63.1 github.com/lightstep/lightstep-tracer-go v0.26.0 - github.com/miekg/dns v1.1.61 + github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 github.com/open-policy-agent/opa v0.67.1 github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.19.1 + github.com/prometheus/client_golang v1.20.0 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/redis/go-redis/v9 v9.6.1 @@ -118,7 +118,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/influxdata/tdigest v0.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect github.com/looplab/fsm v1.0.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -132,6 +132,7 @@ require ( github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.19.0 // indirect @@ -139,10 +140,11 @@ require ( github.com/opencontainers/image-spec v1.1.0 // indirect github.com/peterh/liner v1.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect diff --git a/go.sum b/go.sum index baf9fa056a..75a4c5d3e3 100644 --- a/go.sum +++ b/go.sum @@ -135,8 +135,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -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.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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= @@ -270,8 +270,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -281,6 +281,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 h1:YjW+hUb8Fh2S58z4av4t/0cBMK/Q0aP48RocCFsC8yI= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7/go.mod h1:Spd59icnvRxSKuyijbbwe5AemzvcyXAUBgApa7VybMw= github.com/lightstep/lightstep-tracer-go v0.26.0 h1:ZOw8meo7+7SvvUWrL0c4IRr3bd4YIGRtrAgDBaRH6ro= @@ -299,8 +301,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= -github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -323,6 +325,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -365,6 +369,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -375,8 +381,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= +github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -385,13 +391,13 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= From ae0322316439159575dfc89aaa0095926372150c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:29:12 +0200 Subject: [PATCH 0886/1047] build(deps): bump amazonlinux from `ef1a9c8` to `07900cb` in /fuzz (#3196) Bumps amazonlinux from `ef1a9c8` to `07900cb`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 96bf091c57..6bb846870b 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:ef1a9c856a0d3b2d581d0d9261540df3c6ab3a7f752546603c93f954def936a3 +FROM amazonlinux:2023@sha256:07900cb42ce7c5620507a79d02274d4257bc754a8359d1c7b4b52e61ffd14987 WORKDIR /workspace From 69fc53fe5a53f4a04917d4ef5803f5bf6424dcbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:42:23 +0200 Subject: [PATCH 0887/1047] build(deps): bump docker/build-push-action from 6.6.1 to 6.7.0 (#3197) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.6.1 to 6.7.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/16ebe778df0e7752d2cfcbd924afdbbd89c1a755...5cd11c3a4ced054e52742c5fd54dca954e0edd85) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 72c54847a4..6fac0fb0f7 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} - name: Build and push - uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 168e443c979d155f581940aaf8e25460232c0f84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:59:49 +0200 Subject: [PATCH 0888/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3200) Bumps oss-fuzz-base/base-builder-go from `54cf783` to `efaf3b6`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 3726879ae4..44f1eb9855 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:54cf78317a403958103759e226f74cc139bf751d622d09af8668dccff201b9b9 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:efaf3b6257ce51b56c74b2ce7c1d8165674febdc5e43859904c208f11e581482 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 2c55babb33b778d1db0794cabc7a5c23702b772c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 20 Aug 2024 20:14:56 +0200 Subject: [PATCH 0889/1047] proxy: handle invalid chunked requests (#3192) Respond with 400 instead of 500 in case of invalid chunked request body. Signed-off-by: Alexander Yastrebov --- proxy/proxy.go | 16 +++++++-- proxy/proxy_test.go | 7 ++++ proxy/request_test.go | 81 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 proxy/request_test.go diff --git a/proxy/proxy.go b/proxy/proxy.go index 5553a2fdd5..0babad8da5 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1050,11 +1050,12 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co } } - ctx.proxySpan.LogKV("event", "error", "message", ensureUTF8(err.Error())) + errMessage := err.Error() + ctx.proxySpan.LogKV("event", "error", "message", ensureUTF8(errMessage)) + if perr, ok := err.(*proxyError); ok { perr.err = fmt.Errorf("failed to do backend roundtrip to %s: %w", req.URL.Host, perr.err) return nil, perr - } else if nerr, ok := err.(net.Error); ok { var status int if nerr.Timeout() { @@ -1067,6 +1068,17 @@ func (p *Proxy) makeBackendRequest(ctx *context, requestContext stdlibcontext.Co return nil, &proxyError{err: fmt.Errorf("net.Error during backend roundtrip to %s: timeout=%v temporary='%v': %w", req.URL.Host, nerr.Timeout(), nerr.Temporary(), err), code: status} } + switch errMessage { + case // net/http/internal/chunked.go + "header line too long", + "chunked encoding contains too much non-data", + "malformed chunked encoding", + "empty hex number for chunk length", + "invalid byte in chunk length", + "http chunk length too large": + return nil, &proxyError{code: http.StatusBadRequest, err: fmt.Errorf("failed to do backend roundtrip due to invalid request: %w", err)} + } + return nil, &proxyError{err: fmt.Errorf("unexpected error from Go stdlib net/http package during roundtrip: %w", err)} } p.tracing.setTag(ctx.proxySpan, HTTPStatusCodeTag, uint16(response.StatusCode)) diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index b9c080b188..21bc5e0c03 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -107,6 +107,13 @@ func (l *testLog) String() string { return l.buf.String() } +func (l *testLog) Reset() { + l.mu.Lock() + defer l.mu.Unlock() + + l.buf.Reset() +} + func (l *testLog) Close() { log.SetOutput(l.oldOut) log.SetLevel(l.oldLevel) diff --git a/proxy/request_test.go b/proxy/request_test.go new file mode 100644 index 0000000000..5620553a96 --- /dev/null +++ b/proxy/request_test.go @@ -0,0 +1,81 @@ +package proxy_test + +import ( + "bufio" + "fmt" + "net" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/proxy" + "github.com/zalando/skipper/proxy/proxytest" +) + +func TestRequestInvalidChunkedEncoding(t *testing.T) { + testLog := proxy.NewTestLog() + defer testLog.Close() + + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + defer backend.Close() + + p := proxytest.Config{ + Routes: eskip.MustParse(fmt.Sprintf(`* -> "%s"`, backend.URL)), + }.Create() + defer p.Close() + + doChunkedRequest := func(t *testing.T, body string) *http.Response { + t.Helper() + + conn, err := net.Dial("tcp", strings.TrimPrefix(p.URL, "http://")) + require.NoError(t, err) + defer conn.Close() + + _, err = conn.Write([]byte("POST / HTTP/1.1\r\nHost: skipper.test\r\nTransfer-Encoding: chunked\r\n\r\n" + body)) + require.NoError(t, err) + + resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "POST"}) + require.NoError(t, err) + resp.Body.Close() + + return resp + } + + for _, tc := range []struct { + body string + expectErrMessage string + }{ + { + // "!" is an invalid byte in chunk length + body: "!" + "\r\nabcd\r\n" + "0\r\n\r\n", + expectErrMessage: "invalid byte in chunk length", + }, + { + // empty chunk length + body: "" + "\r\nabcd\r\n" + "0\r\n\r\n", + expectErrMessage: "empty hex number for chunk length", + }, + { + // missing \r\n after first chunk + body: "4\r\nabcd" + "0\r\n\r\n", + expectErrMessage: "malformed chunked encoding", + }, + } { + t.Run(tc.expectErrMessage, func(t *testing.T) { + testLog.Reset() + + resp := doChunkedRequest(t, tc.body) + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + + err := testLog.WaitFor("failed to do backend roundtrip due to invalid request: "+tc.expectErrMessage, 100*time.Millisecond) + if !assert.NoError(t, err) { + t.Logf("proxy log: %s", testLog.String()) + } + }) + } +} From dfeffe0ee6c6a5c4b273eb2a5a29363c3c31c830 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 20 Aug 2024 20:15:26 +0200 Subject: [PATCH 0890/1047] filters/auth: handle trailing question mark in grant flow (#3201) Do not propagate trailing question mark from the initial request to redirect URI. Construct redirect url from scratch instead of resetting URL.ForceQuery. See related https://github.com/golang/go/issues/13488 Signed-off-by: Alexander Yastrebov --- filters/auth/grant_test.go | 36 ++++++++++++++++++++++++++++++++++++ filters/auth/grantconfig.go | 10 ++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/filters/auth/grant_test.go b/filters/auth/grant_test.go index 5f085cdfb8..70270e73ca 100644 --- a/filters/auth/grant_test.go +++ b/filters/auth/grant_test.go @@ -1036,3 +1036,39 @@ func TestGrantLoginRedirectStub(t *testing.T) { assert.Equal(t, int64(len(expectedContent)), rsp.ContentLength) assert.Equal(t, expectedContent, string(body)) } + +func TestGrantLoginRedirectForRequestWithTrailingQuestionMark(t *testing.T) { + const ( + applicationDomain = "foo.skipper.test" + callbackPath = "/a-callback" + ) + + dnstest.LoopbackNames(t, applicationDomain) + + provider := newGrantTestAuthServer(testToken, testAccessCode) + defer provider.Close() + + tokeninfo := newGrantTestTokeninfo(testToken, "") + defer tokeninfo.Close() + + config := newGrantTestConfig(tokeninfo.URL, provider.URL) + config.CallbackPath = callbackPath + + routes := eskip.MustParse(`* -> oauthGrant() -> status(204) -> `) + + proxy, client := newAuthProxy(t, config, routes, applicationDomain) + defer proxy.Close() + + // When requested with trailing question mark + rsp, err := client.Get(proxy.URL + "?") + require.NoError(t, err) + defer rsp.Body.Close() + + require.Equal(t, rsp.StatusCode, http.StatusTemporaryRedirect) + + location, err := url.Parse(rsp.Header.Get("Location")) + require.NoError(t, err) + + // Then redirect_uri does not contain a trailing question mark + assert.Equal(t, proxy.URL+callbackPath, location.Query().Get("redirect_uri")) +} diff --git a/filters/auth/grantconfig.go b/filters/auth/grantconfig.go index 7d216ee549..97a9dd2661 100644 --- a/filters/auth/grantconfig.go +++ b/filters/auth/grantconfig.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/http" + "net/url" "path/filepath" "strings" "time" @@ -370,10 +371,11 @@ func (c *OAuthConfig) RedirectURLs(req *http.Request) (redirect, original string original = u.String() - u.Path = c.CallbackPath - u.RawQuery = "" - - redirect = u.String() + redirect = (&url.URL{ + Scheme: u.Scheme, + Host: u.Host, + Path: c.CallbackPath, + }).String() return } From 21671286a6d2d004a2f33022a919a76237e9c7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandor=20Sz=C3=BCcs?= Date: Wed, 21 Aug 2024 17:49:07 +0200 Subject: [PATCH 0891/1047] feature: add filter that sets a span tag on response status condition (#3202) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: add filter that sets a span tag on response status condition Signed-off-by: Sandor Szücs --- docs/reference/filters.md | 9 ++ filters/builtin/builtin.go | 1 + filters/filters.go | 1 + filters/tracing/tag.go | 76 +++++++++-- filters/tracing/tag_test.go | 248 ++++++++++++++++++++++++++++++++---- 5 files changed, 301 insertions(+), 34 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 2a2f223fc3..c203d0919a 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -3352,6 +3352,15 @@ tracingTag("http.flow_id", "${request.header.X-Flow-Id}") This filter works just like [tracingTag](#tracingtag), but is applied after the request was processed. In particular, [template placeholders](#template-placeholders) referencing the response can be used in the parameters. +### tracingTagFromResponseIfStatus + +Example: set error tag to true in case response status code is `>= 500` and `<= 599` + +``` +tracingTagFromResponseIfStatus("error", "true", 500, 599) +``` + + ### tracingSpanName This filter sets the name of the outgoing (client span) in opentracing. The default name is "proxy". Example: diff --git a/filters/builtin/builtin.go b/filters/builtin/builtin.go index 78e046eb21..1003756c60 100644 --- a/filters/builtin/builtin.go +++ b/filters/builtin/builtin.go @@ -214,6 +214,7 @@ func Filters() []filters.Spec { tracing.NewBaggageToTagFilter(), tracing.NewTag(), tracing.NewTagFromResponse(), + tracing.NewTagFromResponseIfStatus(), tracing.NewStateBagToTag(), //lint:ignore SA1019 due to backward compatibility accesslog.NewAccessLogDisabled(), diff --git a/filters/filters.go b/filters/filters.go index 1c554a95db..358e666b17 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -351,6 +351,7 @@ const ( StateBagToTagName = "stateBagToTag" TracingTagName = "tracingTag" TracingTagFromResponseName = "tracingTagFromResponse" + TracingTagFromResponseIfStatusName = "tracingTagFromResponseIfStatus" TracingSpanNameName = "tracingSpanName" OriginMarkerName = "originMarker" FadeInName = "fadeIn" diff --git a/filters/tracing/tag.go b/filters/tracing/tag.go index b80de4bea0..107662eb77 100644 --- a/filters/tracing/tag.go +++ b/filters/tracing/tag.go @@ -1,6 +1,8 @@ package tracing import ( + "net/http" + opentracing "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" @@ -10,11 +12,21 @@ type tagSpec struct { typ string } +type tagFilterType int + +const ( + tagRequest tagFilterType = iota + 1 + tagResponse + tagResponseCondition +) + type tagFilter struct { - tagFromResponse bool + typ tagFilterType tagName string tagValue *eskip.Template + + condition func(*http.Response) bool } // NewTag creates a filter specification for the tracingTag filter. @@ -27,12 +39,34 @@ func NewTagFromResponse() filters.Spec { return &tagSpec{typ: filters.TracingTagFromResponseName} } +func NewTagFromResponseIfStatus() filters.Spec { + return &tagSpec{typ: filters.TracingTagFromResponseIfStatusName} +} + func (s *tagSpec) Name() string { return s.typ } func (s *tagSpec) CreateFilter(args []interface{}) (filters.Filter, error) { - if len(args) != 2 { + var typ tagFilterType + + switch s.typ { + case filters.TracingTagName: + if len(args) != 2 { + return nil, filters.ErrInvalidFilterParameters + } + typ = tagRequest + case filters.TracingTagFromResponseName: + if len(args) != 2 { + return nil, filters.ErrInvalidFilterParameters + } + typ = tagResponse + case filters.TracingTagFromResponseIfStatusName: + if len(args) != 4 { + return nil, filters.ErrInvalidFilterParameters + } + typ = tagResponseCondition + default: return nil, filters.ErrInvalidFilterParameters } @@ -46,23 +80,49 @@ func (s *tagSpec) CreateFilter(args []interface{}) (filters.Filter, error) { return nil, filters.ErrInvalidFilterParameters } - return &tagFilter{ - tagFromResponse: s.typ == filters.TracingTagFromResponseName, - + f := &tagFilter{ + typ: typ, tagName: tagName, tagValue: eskip.NewTemplate(tagValue), - }, nil + } + + if len(args) == 4 { + minValue, ok := args[2].(float64) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + maxValue, ok := args[3].(float64) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + minVal := int(minValue) + maxVal := int(maxValue) + if minVal < 0 || maxVal > 599 || minVal > maxVal { + return nil, filters.ErrInvalidFilterParameters + } + + f.condition = func(rsp *http.Response) bool { + return minVal <= rsp.StatusCode && rsp.StatusCode <= maxVal + } + } + + return f, nil } func (f *tagFilter) Request(ctx filters.FilterContext) { - if !f.tagFromResponse { + if f.typ == tagRequest { f.setTag(ctx) } } func (f *tagFilter) Response(ctx filters.FilterContext) { - if f.tagFromResponse { + switch f.typ { + case tagResponse: f.setTag(ctx) + case tagResponseCondition: + if f.condition(ctx.Response()) { + f.setTag(ctx) + } } } diff --git a/filters/tracing/tag_test.go b/filters/tracing/tag_test.go index 4947e3f2fd..266b2158d3 100644 --- a/filters/tracing/tag_test.go +++ b/filters/tracing/tag_test.go @@ -6,6 +6,7 @@ import ( "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/mocktracer" + "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/filtertest" ) @@ -37,20 +38,160 @@ func TestTagName(t *testing.T) { if NewTagFromResponse().Name() != filters.TracingTagFromResponseName { t.Error("Wrong tag spec name") } + if NewTagFromResponseIfStatus().Name() != filters.TracingTagFromResponseIfStatusName { + t.Error("Wrong tag spec name") + } } func TestTagCreateFilter(t *testing.T) { - spec := tagSpec{} - if _, err := spec.CreateFilter(nil); err != filters.ErrInvalidFilterParameters { - t.Errorf("Create filter without args should return error") - } - - if _, err := spec.CreateFilter([]interface{}{3, "foo"}); err != filters.ErrInvalidFilterParameters { - t.Errorf("Create filter without first arg is string should return error") - } + for _, tt := range []struct { + name string + args []interface{} + spec filters.Spec + want error + }{ + { + name: "create filter with unknown filter", + spec: &tagSpec{}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with no args", + spec: NewTag(), + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with no args", + spec: NewTagFromResponse(), + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with no args", + spec: NewTagFromResponseIfStatus(), + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with one args", + spec: NewTag(), + args: []interface{}{"foo"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with one args", + spec: NewTagFromResponse(), + args: []interface{}{"foo"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with one args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args one wrong", + spec: NewTag(), + args: []interface{}{"foo", 3}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args one wrong", + spec: NewTagFromResponse(), + args: []interface{}{"foo", 3}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args one wrong", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", 3}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args one wrong", + spec: NewTag(), + args: []interface{}{3, "foo"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args one wrong", + spec: NewTagFromResponse(), + args: []interface{}{3, "foo"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args one wrong", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{3, "foo"}, + want: filters.ErrInvalidFilterParameters, + }, - if _, err := spec.CreateFilter([]interface{}{"foo", 3}); err != filters.ErrInvalidFilterParameters { - t.Errorf("Create filter without second arg is string should return error") + // special + { + name: "create filter with three args want 2", + spec: NewTag(), + args: []interface{}{"foo", "bar", "qux"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with three args want 2", + spec: NewTagFromResponse(), + args: []interface{}{"foo", "bar", "qux"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with two args want 4 args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with three args want 4", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", 300}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with four args wrong args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", 300, "500"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with four args wrong args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", "300", 500}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with four args wrong args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", "300", "500"}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with four args wrong args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", 300, 600}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with four args wrong args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", 500, 400}, + want: filters.ErrInvalidFilterParameters, + }, + { + name: "create filter with four args wrong args", + spec: NewTagFromResponseIfStatus(), + args: []interface{}{"foo", "bar", -1, 400}, + want: filters.ErrInvalidFilterParameters, + }, + } { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.spec.CreateFilter(tt.args); err != tt.want { + t.Errorf("Failed to create filter: Want %v, got %v", tt.want, err) + } + }) } } @@ -59,22 +200,21 @@ func TestTracingTag(t *testing.T) { for _, ti := range []struct { name string - spec filters.Spec - value string + doc string context *filtertest.Context + tagName string expected interface{} }{{ "plain key value", - NewTag(), - "test_value", + `tracingTag("test_tag", "test_value")`, &filtertest.Context{ FRequest: &http.Request{}, }, + "test_tag", "test_value", }, { "tag from header", - NewTag(), - "${request.header.X-Flow-Id}", + `tracingTag("test_tag", "${request.header.X-Flow-Id}")`, &filtertest.Context{ FRequest: &http.Request{ Header: http.Header{ @@ -82,11 +222,11 @@ func TestTracingTag(t *testing.T) { }, }, }, + "test_tag", "foo", }, { "tag from response", - NewTagFromResponse(), - "${response.header.X-Fallback}", + `tracingTagFromResponse("test_tag", "${response.header.X-Fallback}")`, &filtertest.Context{ FRequest: &http.Request{}, FResponse: &http.Response{ @@ -95,19 +235,63 @@ func TestTracingTag(t *testing.T) { }, }, }, + "test_tag", "true", + }, { + "tag from response if condition is true", + `tracingTagFromResponseIfStatus("error", "true", 500, 599)`, + &filtertest.Context{ + FRequest: &http.Request{}, + FResponse: &http.Response{ + StatusCode: 500, + }, + }, + "error", + "true", + }, { + "tag from response if condition is true", + `tracingTagFromResponseIfStatus("error", "true", 500, 500)`, + &filtertest.Context{ + FRequest: &http.Request{}, + FResponse: &http.Response{ + StatusCode: 500, + }, + }, + "error", + "true", + }, { + "tag from response if condition is true", + `tracingTagFromResponseIfStatus("error", "true", 500, 599)`, + &filtertest.Context{ + FRequest: &http.Request{}, + FResponse: &http.Response{ + StatusCode: 599, + }, + }, + "error", + "true", + }, { + "do not tag from response if condition is false", + `tracingTagFromResponseIfStatus("error", "true", 500, 599)`, + &filtertest.Context{ + FRequest: &http.Request{}, + FResponse: &http.Response{ + StatusCode: 499, + }, + }, + "error", + nil, }, { "tag from missing header", - NewTag(), - "${request.header.missing}", + `tracingTag("test_tag", "${request.header.missing}")`, &filtertest.Context{ FRequest: &http.Request{}, }, + "test_tag", nil, }, { "tracingTag is not processed on response", - NewTag(), - "${response.header.X-Fallback}", + `tracingTag("test_tag", "${request.header.X-Fallback}")`, &filtertest.Context{ FRequest: &http.Request{}, FResponse: &http.Response{ @@ -116,6 +300,7 @@ func TestTracingTag(t *testing.T) { }, }, }, + "test_tag", nil, }, } { @@ -127,9 +312,20 @@ func TestTracingTag(t *testing.T) { FRequest: ti.context.FRequest.WithContext(opentracing.ContextWithSpan(ti.context.FRequest.Context(), span)), } - f, err := ti.spec.CreateFilter([]interface{}{"test_tag", ti.value}) + fEskip := eskip.MustParseFilters(ti.doc)[0] + + fr := make(filters.Registry) + fr.Register(NewTag()) + fr.Register(NewTagFromResponse()) + fr.Register(NewTagFromResponseIfStatus()) + + spec, ok := fr[fEskip.Name] + if !ok { + t.Fatalf("Failed to find filter spec: %q", fEskip.Name) + } + f, err := spec.CreateFilter(fEskip.Args) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to create filter %q with %v: %v", fEskip.Name, fEskip.Args, err) } f.Request(requestContext) @@ -138,8 +334,8 @@ func TestTracingTag(t *testing.T) { f.Response(requestContext) - if got := span.Tag("test_tag"); got != ti.expected { - t.Errorf("unexpected tag value '%v' != '%v'", got, ti.expected) + if got := span.Tag(ti.tagName); got != ti.expected { + t.Errorf("unexpected tag %q value '%v' != '%v'", ti.tagName, got, ti.expected) } }) } From 85ae2fefee2557da29aeab431e7d95855962f47e Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 23 Aug 2024 16:55:54 +0200 Subject: [PATCH 0892/1047] options: Add RegisterFilters callback (#3203) Add a callback to register additional filters that can also re-use already registered filters. Signed-off-by: Alexander Yastrebov --- skipper.go | 11 ++++++++++- skipper_test.go | 48 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/skipper.go b/skipper.go index 62ad7a2415..8b4bdc2697 100644 --- a/skipper.go +++ b/skipper.go @@ -130,6 +130,10 @@ type Options struct { // List of custom filter specifications. CustomFilters []filters.Spec + // RegisterFilters callback can be used to register additional filters. + // Built-in and custom filters are registered before the callback is called. + RegisterFilters func(registry filters.Registry) + // Urls of nodes in an etcd cluster, storing route definitions. EtcdUrls []string @@ -1141,7 +1145,8 @@ func initLog(o Options) error { } // filterRegistry creates a filter registry with the builtin and -// custom filter specs registered excluding disabled filters +// custom filter specs registered excluding disabled filters. +// If [Options.RegisterFilters] callback is set, it will be called. func (o *Options) filterRegistry() filters.Registry { registry := make(filters.Registry) @@ -1162,6 +1167,10 @@ func (o *Options) filterRegistry() filters.Registry { } } + if o.RegisterFilters != nil { + o.RegisterFilters(registry) + } + return registry } diff --git a/skipper_test.go b/skipper_test.go index a34cc88e26..c5d7ba47a5 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -89,24 +89,42 @@ func findAddress() (string, error) { } func TestOptionsFilterRegistry(t *testing.T) { - o := &Options{ - CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, - } - fr := o.filterRegistry() + t.Run("custom filters", func(t *testing.T) { + o := &Options{ + CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, + } + fr := o.filterRegistry() - assert.Contains(t, fr, filters.SetRequestHeaderName) - assert.Contains(t, fr, filters.LuaName) - assert.Contains(t, fr, filters.BearerInjectorName) + assert.Contains(t, fr, filters.SetRequestHeaderName) + assert.Contains(t, fr, filters.LuaName) + assert.Contains(t, fr, filters.BearerInjectorName) + }) - o = &Options{ - CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, - DisabledFilters: []string{filters.LuaName, filters.BearerInjectorName}, - } - fr = o.filterRegistry() + t.Run("disabled filters", func(t *testing.T) { + o := &Options{ + CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, + DisabledFilters: []string{filters.LuaName, filters.BearerInjectorName}, + } + fr := o.filterRegistry() - assert.Contains(t, fr, filters.SetRequestHeaderName) - assert.NotContains(t, fr, filters.LuaName) - assert.NotContains(t, fr, filters.BearerInjectorName) + assert.Contains(t, fr, filters.SetRequestHeaderName) + assert.NotContains(t, fr, filters.LuaName) + assert.NotContains(t, fr, filters.BearerInjectorName) + }) + + t.Run("register filters", func(t *testing.T) { + o := &Options{ + CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, + RegisterFilters: func(registry filters.Registry) { + registry.Register(auth.NewWebhook(0)) + }, + } + fr := o.filterRegistry() + + assert.Contains(t, fr, filters.SetRequestHeaderName) + assert.Contains(t, fr, filters.BearerInjectorName) + assert.Contains(t, fr, filters.WebhookName) + }) } func TestOptionsOpenTracingTracerInstanceOverridesOpenTracing(t *testing.T) { From f424c876f6ba693af6916dcbaf661028f76f98cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:15:40 +0200 Subject: [PATCH 0893/1047] build(deps): bump amazonlinux from `07900cb` to `171fe68` in /fuzz (#3204) Bumps amazonlinux from `07900cb` to `171fe68`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 6bb846870b..a2bcb3c597 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:07900cb42ce7c5620507a79d02274d4257bc754a8359d1c7b4b52e61ffd14987 +FROM amazonlinux:2023@sha256:171fe68a5c4114e48c19513c6341da2c6d693454f62edddaf7ac0ad2f13693d0 WORKDIR /workspace From dda558b97081764b7377b30e94bb53be2412f2fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:16:04 +0200 Subject: [PATCH 0894/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3205) Bumps oss-fuzz-base/base-builder-go from `efaf3b6` to `74a18c1`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 44f1eb9855..f5741dd685 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:efaf3b6257ce51b56c74b2ce7c1d8165674febdc5e43859904c208f11e581482 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:74a18c1f123123003408451656a487ec52915f0e721b669de4fe0b3aac51d6b2 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 2ad395d25d8056e447cf2b7b3d12e0243f3ec5f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:16:41 +0200 Subject: [PATCH 0895/1047] build(deps): bump the all-go-mod-patch-and-minor group with 3 updates (#3206) Bumps the all-go-mod-patch-and-minor group with 3 updates: [github.com/instana/go-sensor](https://github.com/instana/go-sensor), [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) and [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go). Updates `github.com/instana/go-sensor` from 1.63.1 to 1.64.0 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/RELEASE.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.63.1...v1.64.0) Updates `github.com/prometheus/client_golang` from 1.20.0 to 1.20.2 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.0...v1.20.2) Updates `github.com/testcontainers/testcontainers-go` from 0.32.0 to 0.33.0 - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.32.0...v0.33.0) --- updated-dependencies: - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 7 +++---- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 5c89fb6b7a..8ce149a243 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 - github.com/instana/go-sensor v1.63.1 + github.com/instana/go-sensor v1.64.0 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 @@ -31,7 +31,7 @@ require ( github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.20.0 + github.com/prometheus/client_golang v1.20.2 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/redis/go-redis/v9 v9.6.1 @@ -40,7 +40,7 @@ require ( github.com/sony/gobreaker v1.0.0 github.com/stretchr/testify v1.9.0 github.com/szuecs/rate-limit-buffer v0.9.0 - github.com/testcontainers/testcontainers-go v0.32.0 + github.com/testcontainers/testcontainers-go v0.33.0 github.com/tidwall/gjson v1.17.3 github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible @@ -69,7 +69,6 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect diff --git a/go.sum b/go.sum index 75a4c5d3e3..e1e8f0a775 100644 --- a/go.sum +++ b/go.sum @@ -257,8 +257,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.63.1 h1:SJuU4rT/qsquC3Ha0kCYILe0/yl+Er2EnF7zSjJkGS4= -github.com/instana/go-sensor v1.63.1/go.mod h1:VANqbeoHzgsGeilVYvTG8Kl/zFPuk2tr2DlyvBt8200= +github.com/instana/go-sensor v1.64.0 h1:sago7DnfjuQ0OYDbs6amhc9RqSc76AkOfkyV2W209k8= +github.com/instana/go-sensor v1.64.0/go.mod h1:xRwSUW7zoxo2mKzzKGc163N82F8CuX5EpeNDQeg689M= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -381,8 +381,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= -github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -459,8 +459,8 @@ github.com/szuecs/rate-limit-buffer v0.9.0 h1:65fBCVsaJFh0E1G5C6/sInEPlYR6dXtF9J github.com/szuecs/rate-limit-buffer v0.9.0/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= -github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= +github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= +github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= From ba12cf7d382eb1a59c18128c0a7a5ebf3cb1fe42 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 26 Aug 2024 18:13:31 +0200 Subject: [PATCH 0896/1047] options: test filter registration order (#3208) Test that built-in and CustomFilters are registered before RegisterFilters callback. Follow up on #3203 Signed-off-by: Alexander Yastrebov --- skipper_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/skipper_test.go b/skipper_test.go index c5d7ba47a5..c47bc979b5 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -117,6 +117,10 @@ func TestOptionsFilterRegistry(t *testing.T) { CustomFilters: []filters.Spec{auth.NewBearerInjector(nil)}, RegisterFilters: func(registry filters.Registry) { registry.Register(auth.NewWebhook(0)) + + // Check that built-in and CustomFilters are already registered + assert.Contains(t, registry, filters.SetRequestHeaderName) + assert.Contains(t, registry, filters.BearerInjectorName) }, } fr := o.filterRegistry() From 2c39d54584256c13b1538bcf257ff0a133537e4a Mon Sep 17 00:00:00 2001 From: Farasath Ahamed Date: Tue, 27 Aug 2024 10:48:46 +0200 Subject: [PATCH 0897/1047] Minor improvements to opaServeResponse filter (#3191) * OPA: Add decision outcome to span in opaServeResponse filter * Increment OPA decision counter metric in opaServeResponse filter To be symmetric with opaAuthorizeRequest filter, OPA decision counts are updated Signed-off-by: Farasath Ahamed --- .../opaserveresponse/opaserveresponse.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse.go index 64ad14d1f9..e95104dfbe 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse.go @@ -126,9 +126,21 @@ func (f *opaServeResponseFilter) Request(fc filters.FilterContext) { fc.Metrics().MeasureSince(f.opa.MetricsKey("eval_time"), start) if err != nil { f.opa.ServeInvalidDecisionError(fc, span, result, err) + return + } + allowed, err := result.IsAllowed() + if err != nil { + f.opa.ServeInvalidDecisionError(fc, span, result, err) return } + span.SetTag("opa.decision.allowed", allowed) + + if allowed { + fc.Metrics().IncCounter(f.opa.MetricsKey("decision.allow")) + } else { + fc.Metrics().IncCounter(f.opa.MetricsKey("decision.deny")) + } f.opa.ServeResponse(fc, span, result) } From 3815a131e07f0531dc5abffc6cba323e97ef1056 Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Tue, 27 Aug 2024 17:12:36 +0200 Subject: [PATCH 0898/1047] use standard `errors.Join` since we already use go1.22 (#3209) check https://pkg.go.dev/errors#Join follow up on https://github.com/zalando/skipper/pull/2492 Signed-off-by: Mustafa Abdelrahman --- dataclients/kubernetes/definitions/definitions.go | 15 --------------- .../kubernetes/definitions/ingressvalidator.go | 3 ++- .../kubernetes/definitions/routegroupvalidator.go | 12 ++++++------ 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/dataclients/kubernetes/definitions/definitions.go b/dataclients/kubernetes/definitions/definitions.go index ba4c5bba05..a7dfc812f5 100644 --- a/dataclients/kubernetes/definitions/definitions.go +++ b/dataclients/kubernetes/definitions/definitions.go @@ -1,7 +1,6 @@ package definitions import ( - "strings" "time" "errors" @@ -44,17 +43,3 @@ type WeightedBackend interface { GetName() string GetWeight() float64 } - -// TODO: use https://pkg.go.dev/errors#Join with go1.21 -func errorsJoin(errs ...error) error { - var errVals []string - for _, err := range errs { - if err != nil { - errVals = append(errVals, err.Error()) - } - } - if len(errVals) > 0 { - return errors.New(strings.Join(errVals, "\n")) - } - return nil -} diff --git a/dataclients/kubernetes/definitions/ingressvalidator.go b/dataclients/kubernetes/definitions/ingressvalidator.go index 1a7e2499d8..53de8b485a 100644 --- a/dataclients/kubernetes/definitions/ingressvalidator.go +++ b/dataclients/kubernetes/definitions/ingressvalidator.go @@ -1,6 +1,7 @@ package definitions import ( + "errors" "fmt" "github.com/zalando/skipper/eskip" @@ -15,7 +16,7 @@ func (igv *IngressV1Validator) Validate(item *IngressV1Item) error { errs = append(errs, igv.validatePredicateAnnotation(item.Metadata.Annotations)) errs = append(errs, igv.validateRoutesAnnotation(item.Metadata.Annotations)) - return errorsJoin(errs...) + return errors.Join(errs...) } func (igv *IngressV1Validator) validateFilterAnnotation(annotations map[string]string) error { diff --git a/dataclients/kubernetes/definitions/routegroupvalidator.go b/dataclients/kubernetes/definitions/routegroupvalidator.go index 3f96167194..a42baa8d9e 100644 --- a/dataclients/kubernetes/definitions/routegroupvalidator.go +++ b/dataclients/kubernetes/definitions/routegroupvalidator.go @@ -27,7 +27,7 @@ func ValidateRouteGroups(rgl *RouteGroupList) error { for _, rg := range rgl.Items { errs = append(errs, defaultRouteGroupValidator.Validate(rg)) } - return errorsJoin(errs...) + return errors.Join(errs...) } func (rgv *RouteGroupValidator) Validate(item *RouteGroupItem) error { @@ -41,7 +41,7 @@ func (rgv *RouteGroupValidator) Validate(item *RouteGroupItem) error { errs = append(errs, rgv.validateBackends(item)) errs = append(errs, rgv.validateHosts(item)) - return errorsJoin(errs...) + return errors.Join(errs...) } // TODO: we need to pass namespace/name in all errors @@ -76,7 +76,7 @@ func (rgv *RouteGroupValidator) validateFilters(item *RouteGroupItem) error { } } - return errorsJoin(errs...) + return errors.Join(errs...) } func (rgv *RouteGroupValidator) validatePredicates(item *RouteGroupItem) error { @@ -91,7 +91,7 @@ func (rgv *RouteGroupValidator) validatePredicates(item *RouteGroupItem) error { } } } - return errorsJoin(errs...) + return errors.Join(errs...) } func (rgv *RouteGroupValidator) validateBackends(item *RouteGroupItem) error { @@ -108,7 +108,7 @@ func (rgv *RouteGroupValidator) validateBackends(item *RouteGroupItem) error { } } } - return errorsJoin(errs...) + return errors.Join(errs...) } func (rgv *RouteGroupValidator) validateHosts(item *RouteGroupItem) error { @@ -120,7 +120,7 @@ func (rgv *RouteGroupValidator) validateHosts(item *RouteGroupItem) error { } uniqueHosts[host] = struct{}{} } - return errorsJoin(errs...) + return errors.Join(errs...) } // TODO: we need to pass namespace/name in all errors From 08aba89084b0032a4683b734bc20a46aac989ba3 Mon Sep 17 00:00:00 2001 From: Pushpalanka Jayawardhana Date: Mon, 2 Sep 2024 10:00:12 +0200 Subject: [PATCH 0899/1047] Remove `dependency.base` calculation in optional body parsing. (#3210) Signed-off-by: Pushpalanka Jayawardhana --- .../opaauthorizerequest_test.go | 105 ++++++++++++++---- filters/openpolicyagent/openpolicyagent.go | 24 ++-- 2 files changed, 88 insertions(+), 41 deletions(-) diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index ab7616086c..7164b67ab7 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -545,6 +545,54 @@ func BenchmarkAuthorizeRequest(b *testing.B) { } }) + b.Run("authorize-request-with-body", func(b *testing.B) { + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle("/bundles/somebundle.tar.gz", map[string]string{ + "main.rego": ` + package envoy.authz + + import rego.v1 + + default allow = false + + allow if { + endswith(input.parsed_body.email, "@zalando.de") + } + `, + }), + ) + + f, err := createBodyBasedOpaFilter(opaControlPlane) + assert.NoError(b, err) + + url, err := url.Parse("http://opa-authorized.test/somepath") + assert.NoError(b, err) + + body := `{"email": "bench-test@zalando.de"}` + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + Method: "POST", + Header: map[string][]string{ + "Authorization": {"Bearer FOOBAR"}, + "Content-Type": {"application/json"}, + }, + URL: url, + Body: io.NopCloser(strings.NewReader(body)), + ContentLength: int64(len(body)), + }, + FMetrics: &metricstest.MockMetrics{}, + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + f.Request(ctx) + } + }) + b.Run("authorize-request-jwt-validation", func(b *testing.B) { publicKey, err := os.ReadFile(certPath) @@ -639,30 +687,39 @@ func BenchmarkAuthorizeRequest(b *testing.B) { } func createOpaFilter(opaControlPlane *opasdktest.Server) (filters.Filter, error) { - config := []byte(fmt.Sprintf(`{ - "services": { - "test": { - "url": %q - } - }, - "bundles": { - "test": { - "resource": "/bundles/{{ .bundlename }}" - } - }, - "labels": { - "environment": "test" - }, - "plugins": { - "envoy_ext_authz_grpc": { - "path": %q, - "dry-run": false - } - } - }`, opaControlPlane.URL(), "envoy/authz/allow")) - + config := generateConfig(opaControlPlane, "envoy/authz/allow") opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() spec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) - f, err := spec.CreateFilter([]interface{}{"somebundle.tar.gz"}) - return f, err + return spec.CreateFilter([]interface{}{"somebundle.tar.gz"}) +} + +func createBodyBasedOpaFilter(opaControlPlane *opasdktest.Server) (filters.Filter, error) { + config := generateConfig(opaControlPlane, "envoy/authz/allow") + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + spec := NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + return spec.CreateFilter([]interface{}{"somebundle.tar.gz"}) +} + +func generateConfig(opaControlPlane *opasdktest.Server, path string) []byte { + return []byte(fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/{{ .bundlename }}" + } + }, + "labels": { + "environment": "test" + }, + "plugins": { + "envoy_ext_authz_grpc": { + "path": %q, + "dry-run": false + } + } + }`, opaControlPlane.URL(), path)) } diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 04a8845f63..282f7613bb 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -18,7 +18,6 @@ import ( "github.com/google/uuid" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/config" - "github.com/open-policy-agent/opa/dependencies" "github.com/open-policy-agent/opa/logging" "github.com/open-policy-agent/opa/plugins" "github.com/open-policy-agent/opa/plugins/discovery" @@ -709,24 +708,15 @@ func (opa *OpenPolicyAgentInstance) ExtractHttpBodyOptionally(req *http.Request) if body != nil && !opa.EnvoyPluginConfig().SkipRequestBodyParse && req.ContentLength <= int64(opa.maxBodyBytes) { - bases, err := dependencies.Base(opa.Compiler(), opa.EnvoyPluginConfig().ParsedQuery) - if err != nil { - return req.Body, nil, func() {}, nil - } - - for _, base := range bases { - if base.HasPrefix(ast.MustParseRef("input.parsed_body")) { - wrapper := newBufferedBodyReader(req.Body, opa.maxBodyBytes, opa.bodyReadBufferSize) + wrapper := newBufferedBodyReader(req.Body, opa.maxBodyBytes, opa.bodyReadBufferSize) - requestedBodyBytes := bodyUpperBound(req.ContentLength, opa.maxBodyBytes) - if !opa.registry.maxMemoryBodyParsingSem.TryAcquire(requestedBodyBytes) { - return req.Body, nil, func() {}, ErrTotalBodyBytesExceeded - } - - rawBody, err := wrapper.fillBuffer(req.ContentLength) - return wrapper, rawBody, func() { opa.registry.maxMemoryBodyParsingSem.Release(requestedBodyBytes) }, err - } + requestedBodyBytes := bodyUpperBound(req.ContentLength, opa.maxBodyBytes) + if !opa.registry.maxMemoryBodyParsingSem.TryAcquire(requestedBodyBytes) { + return req.Body, nil, func() {}, ErrTotalBodyBytesExceeded } + + rawBody, err := wrapper.fillBuffer(req.ContentLength) + return wrapper, rawBody, func() { opa.registry.maxMemoryBodyParsingSem.Release(requestedBodyBytes) }, err } return req.Body, nil, func() {}, nil From 29682264f7727e13e3062c09bc6ee06052cb8ed9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:15:34 +0200 Subject: [PATCH 0900/1047] build(deps): bump actions/setup-python from 5.1.1 to 5.2.0 (#3214) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/39cd14951b08e74b54015e9e001cdefcf80e669f...f677139bbe7f9c59b41e40162b753c062f5d49a3) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index a637946932..804b537592 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 From e1656bae1d12c41436639eaae80b4b2526caa3d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:15:52 +0200 Subject: [PATCH 0901/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3212) Bumps oss-fuzz-base/base-builder-go from `74a18c1` to `fea3ac4`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index f5741dd685..dbafa3a906 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:74a18c1f123123003408451656a487ec52915f0e721b669de4fe0b3aac51d6b2 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:fea3ac4074ac94e5f5c2dfc73b4bfb22b15f9071c965faa139349087576c96ef COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 5dae8c83726d9d9b87597a745e915b107803f4fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:16:11 +0200 Subject: [PATCH 0902/1047] build(deps): bump actions/upload-artifact from 4.3.6 to 4.4.0 (#3213) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.6 to 4.4.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/834a144ee995460fba8ed112a2fc961b36a5ec5a...50769540e7f4bd5e21e526ee35c689e35e0d6874) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index fd3c74cacb..c4de6de05b 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: SARIF file path: results.sarif From d3e3756fffbaf7efba26aed56b02aac86b538aea Mon Sep 17 00:00:00 2001 From: czhou-brex Date: Thu, 5 Sep 2024 06:37:28 -0700 Subject: [PATCH 0903/1047] fallback to env variable for oidc,oauth2 client id and secret (#3217) Signed-off-by: Carl Zhou --- config/config.go | 4 ++-- docs/reference/filters.md | 42 +++++++++++++++++++-------------------- filters/auth/oidc.go | 26 +++++++++++++++++------- skipper.go | 18 +++++++++++++---- 4 files changed, 56 insertions(+), 34 deletions(-) diff --git a/config/config.go b/config/config.go index d9cb8d25c5..20799722e1 100644 --- a/config/config.go +++ b/config/config.go @@ -481,8 +481,8 @@ func NewConfig() *Config { flag.StringVar(&cfg.Oauth2RevokeTokenURL, "oauth2-revoke-token-url", "", "the url where the access and refresh tokens can be revoked when logging out") flag.StringVar(&cfg.Oauth2TokeninfoURL, "oauth2-tokeninfo-url", "", "sets the default tokeninfo URL to query information about an incoming OAuth2 token in oauth2Tokeninfo filters") flag.StringVar(&cfg.Oauth2SecretFile, "oauth2-secret-file", "", "sets the filename with the encryption key for the authentication cookie and grant flow state stored in secrets registry") - flag.StringVar(&cfg.Oauth2ClientID, "oauth2-client-id", "", "sets the OAuth2 client id of the current service, used to exchange the access code") - flag.StringVar(&cfg.Oauth2ClientSecret, "oauth2-client-secret", "", "sets the OAuth2 client secret associated with the oauth2-client-id, used to exchange the access code") + flag.StringVar(&cfg.Oauth2ClientID, "oauth2-client-id", "", "sets the OAuth2 client id of the current service, used to exchange the access code. Falls back to env variable OAUTH2_CLIENT_ID if value is empty.") + flag.StringVar(&cfg.Oauth2ClientSecret, "oauth2-client-secret", "", "sets the OAuth2 client secret associated with the oauth2-client-id, used to exchange the access code. Falls back to env variable OAUTH2_CLIENT_SECRET if value is empty.") flag.StringVar(&cfg.Oauth2ClientIDFile, "oauth2-client-id-file", "", "sets the path of the file containing the OAuth2 client id of the current service, used to exchange the access code. "+ "File name may contain {host} placeholder which will be replaced by the request host") flag.StringVar(&cfg.Oauth2ClientSecretFile, "oauth2-client-secret-file", "", "sets the path of the file containing the OAuth2 client secret associated with the oauth2-client-id, used to exchange the access code. "+ diff --git a/docs/reference/filters.md b/docs/reference/filters.md index c203d0919a..584333ba68 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1736,25 +1736,25 @@ single_page_app: Skipper arguments: -| Argument | Required? | Description | -| -------- | --------- | ----------- | -| `-enable-oauth2-grant-flow` | **yes** | toggle flag to enable the `oauthGrant()` filter. Must be set if you use the filter in routes. Example: `-enable-oauth2-grant-flow` | -| `-oauth2-auth-url` | **yes** | URL of the OAuth2 provider's authorize endpoint. Example: `-oauth2-auth-url=https://identity.example.com/oauth2/authorize` | -| `-oauth2-token-url` | **yes** | URL of the OAuth2 provider's token endpoint. Example: `-oauth2-token-url=https://identity.example.com/oauth2/token` | -| `-oauth2-tokeninfo-url` | **yes** | URL of the OAuth2 provider's tokeninfo endpoint. Example: `-oauth2-tokeninfo-url=https://identity.example.com/oauth2/tokeninfo` | -| `-oauth2-secret-file` | **yes** | path to the file containing the secret for encrypting and decrypting the grant token cookie (the secret can be anything). Example: `-oauth2-secret-file=/path/to/secret` | -| `-oauth2-client-id-file` | conditional | path to the file containing the OAuth2 client ID. Required if you have not set `-oauth2-client-id`. Example: `-oauth2-client-id-file=/path/to/client_id` | -| `-oauth2-client-secret-file` | conditional | path to the file containing the OAuth2 client secret. Required if you have not set `-oauth2-client-secret`. Example: `-oauth2-client-secret-file=/path/to/client_secret` | -| `-oauth2-client-id` | conditional | OAuth2 client ID for authenticating with your OAuth2 provider. Required if you have not set `-oauth2-client-id-file`. Example: `-oauth2-client-id=myclientid` | -| `-oauth2-client-secret` | conditional | OAuth2 client secret for authenticating with your OAuth2 provider. Required if you have not set `-oauth2-client-secret-file`. Example: `-oauth2-client-secret=myclientsecret` | -| `-credentials-update-interval` | no | the time interval for updating client id and client secret from files. Example: `-credentials-update-interval=30s` | -| `-oauth2-access-token-header-name` | no | the name of the request header where the user's bearer token should be set. Example: `-oauth2-access-token-header-name=X-Grant-Authorization` | -| `-oauth2-grant-tokeninfo-keys` | no | comma separated list of keys to preserve in OAuth2 Grant Flow tokeninfo. Default: empty, preserves all tokeninfo keys. Example: `-oauth2-grant-tokeninfo-keys=scope,realm,expires_in` | -| `-oauth2-auth-url-parameters` | no | any additional URL query parameters to set for the OAuth2 provider's authorize and token endpoint calls. Example: `-oauth2-auth-url-parameters=key1=foo,key2=bar` | -| `-oauth2-callback-path` | no | path of the Skipper route containing the `grantCallback()` filter for accepting an authorization code and using it to get an access token. Example: `-oauth2-callback-path=/oauth/callback` | -| `-oauth2-token-cookie-name` | no | the name of the cookie where the access tokens should be stored in encrypted form. Default: `oauth-grant`. Example: `-oauth2-token-cookie-name=SESSION` | -| `-oauth2-token-cookie-remove-subdomains` | no | the number of subdomains to remove from the callback request hostname to obtain token cookie domain. Default: `1`. Example: `-oauth2-token-cookie-remove-subdomains=0` | -| `-oauth2-grant-insecure` | no | omits `Secure` attribute of the token cookie and uses `http` scheme for callback url. Default: `false` | +| Argument | Required? | Description | +| -------- | --------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `-enable-oauth2-grant-flow` | **yes** | toggle flag to enable the `oauthGrant()` filter. Must be set if you use the filter in routes. Example: `-enable-oauth2-grant-flow` | +| `-oauth2-auth-url` | **yes** | URL of the OAuth2 provider's authorize endpoint. Example: `-oauth2-auth-url=https://identity.example.com/oauth2/authorize` | +| `-oauth2-token-url` | **yes** | URL of the OAuth2 provider's token endpoint. Example: `-oauth2-token-url=https://identity.example.com/oauth2/token` | +| `-oauth2-tokeninfo-url` | **yes** | URL of the OAuth2 provider's tokeninfo endpoint. Example: `-oauth2-tokeninfo-url=https://identity.example.com/oauth2/tokeninfo` | +| `-oauth2-secret-file` | **yes** | path to the file containing the secret for encrypting and decrypting the grant token cookie (the secret can be anything). Example: `-oauth2-secret-file=/path/to/secret` | +| `-oauth2-client-id-file` | conditional | path to the file containing the OAuth2 client ID. Required if you have not set `-oauth2-client-id`. Example: `-oauth2-client-id-file=/path/to/client_id` | +| `-oauth2-client-secret-file` | conditional | path to the file containing the OAuth2 client secret. Required if you have not set `-oauth2-client-secret`. Example: `-oauth2-client-secret-file=/path/to/client_secret` | +| `-oauth2-client-id` | conditional | OAuth2 client ID for authenticating with your OAuth2 provider. Required if you have not set `-oauth2-client-id-file` or `OAUTH2_CLIENT_ID` env variable. Example: `-oauth2-client-id=myclientid` | +| `-oauth2-client-secret` | conditional | OAuth2 client secret for authenticating with your OAuth2 provider. Required if you have not set `-oauth2-client-secret-file` or `OAUTH2_CLIENT_SECRET` env variable. Example: `-oauth2-client-secret=myclientsecret` | +| `-credentials-update-interval` | no | the time interval for updating client id and client secret from files. Example: `-credentials-update-interval=30s` | +| `-oauth2-access-token-header-name` | no | the name of the request header where the user's bearer token should be set. Example: `-oauth2-access-token-header-name=X-Grant-Authorization` | +| `-oauth2-grant-tokeninfo-keys` | no | comma separated list of keys to preserve in OAuth2 Grant Flow tokeninfo. Default: empty, preserves all tokeninfo keys. Example: `-oauth2-grant-tokeninfo-keys=scope,realm,expires_in` | +| `-oauth2-auth-url-parameters` | no | any additional URL query parameters to set for the OAuth2 provider's authorize and token endpoint calls. Example: `-oauth2-auth-url-parameters=key1=foo,key2=bar` | +| `-oauth2-callback-path` | no | path of the Skipper route containing the `grantCallback()` filter for accepting an authorization code and using it to get an access token. Example: `-oauth2-callback-path=/oauth/callback` | +| `-oauth2-token-cookie-name` | no | the name of the cookie where the access tokens should be stored in encrypted form. Default: `oauth-grant`. Example: `-oauth2-token-cookie-name=SESSION` | +| `-oauth2-token-cookie-remove-subdomains` | no | the number of subdomains to remove from the callback request hostname to obtain token cookie domain. Default: `1`. Example: `-oauth2-token-cookie-remove-subdomains=0` | +| `-oauth2-grant-insecure` | no | omits `Secure` attribute of the token cookie and uses `http` scheme for callback url. Default: `false` | #### grantCallback @@ -1835,8 +1835,8 @@ oauthOidcUserInfo("https://oidc-provider.example.com", "client_id", "client_secr The filter needs the following parameters: * **OpenID Connect Provider URL** For example Google OpenID Connect is available on `https://accounts.google.com` -* **Client ID** This value is obtained from the provider upon registration of the application. -* **Client Secret** Also obtained from the provider +* **Client ID** This value is obtained from the provider upon registration of the application. Falls back to```OIDC_CLIENT_ID``` env variable for empty value. +* **Client Secret** Also obtained from the provider. Falls back to ```OIDC_CLIENT_SECRET``` env variable for empty value. * **Callback URL** The entire path to the callback from the provider on which the token will be received. It can be any value which is a subpath on which the filter is applied. * **Scopes** The OpenID scopes separated by spaces which need to be specified when requesting the token from the provider. diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index 98cec128a0..6d4e502b7e 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -88,10 +88,12 @@ const ( ) type OidcOptions struct { - MaxIdleConns int - CookieValidity time.Duration - Timeout time.Duration - Tracer opentracing.Tracer + MaxIdleConns int + CookieValidity time.Duration + Timeout time.Duration + Tracer opentracing.Tracer + OidcClientId string + OidcClientSecret string } type ( @@ -242,19 +244,29 @@ func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) validity = defaultCookieValidity } + oidcClientId := sargs[paramClientID] + if oidcClientId == "" { + oidcClientId = s.options.OidcClientId + } + + oidcClientSecret := sargs[paramClientSecret] + if oidcClientSecret == "" { + oidcClientSecret = s.options.OidcClientSecret + } + f := &tokenOidcFilter{ typ: s.typ, redirectPath: redirectURL.Path, config: &oauth2.Config{ - ClientID: sargs[paramClientID], - ClientSecret: sargs[paramClientSecret], + ClientID: oidcClientId, + ClientSecret: oidcClientSecret, RedirectURL: sargs[paramCallbackURL], // self endpoint Endpoint: provider.Endpoint(), Scopes: []string{oidc.ScopeOpenID}, // mandatory scope by spec }, provider: provider, verifier: provider.Verifier(&oidc.Config{ - ClientID: sargs[paramClientID], + ClientID: oidcClientId, }), validity: validity, cookiename: generatedCookieName, diff --git a/skipper.go b/skipper.go index 8b4bdc2697..83a12a673a 100644 --- a/skipper.go +++ b/skipper.go @@ -981,7 +981,13 @@ func (o *Options) OAuthGrantOptions() *auth.OAuthConfig { oauthConfig.TokeninfoURL = o.OAuthTokeninfoURL oauthConfig.SecretFile = o.OAuth2SecretFile oauthConfig.ClientID = o.OAuth2ClientID + if oauthConfig.ClientID == "" { + oauthConfig.ClientID, _ = os.LookupEnv("OAUTH2_CLIENT_ID") + } oauthConfig.ClientSecret = o.OAuth2ClientSecret + if oauthConfig.ClientSecret == "" { + oauthConfig.ClientSecret, _ = os.LookupEnv("OAUTH2_CLIENT_SECRET") + } oauthConfig.ClientIDFile = o.OAuth2ClientIDFile oauthConfig.ClientSecretFile = o.OAuth2ClientSecretFile oauthConfig.CallbackPath = o.OAuth2CallbackPath @@ -1672,11 +1678,15 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ) if o.OIDCSecretsFile != "" { + oidcClientId, _ := os.LookupEnv("OIDC_CLIENT_ID") + oidcClientSecret, _ := os.LookupEnv("OIDC_CLIENT_SECRET") opts := auth.OidcOptions{ - CookieValidity: o.OIDCCookieValidity, - Timeout: o.OIDCDistributedClaimsTimeout, - MaxIdleConns: o.IdleConnectionsPerHost, - Tracer: tracer, + CookieValidity: o.OIDCCookieValidity, + Timeout: o.OIDCDistributedClaimsTimeout, + MaxIdleConns: o.IdleConnectionsPerHost, + Tracer: tracer, + OidcClientId: oidcClientId, + OidcClientSecret: oidcClientSecret, } o.CustomFilters = append(o.CustomFilters, From 00bcc0b30d44db9399eaf696cdee39d3e5a490f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:28:59 +0200 Subject: [PATCH 0904/1047] build(deps): bump amazonlinux from `171fe68` to `03b3981` in /fuzz (#3220) Bumps amazonlinux from `171fe68` to `03b3981`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index a2bcb3c597..75f40f41c0 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:171fe68a5c4114e48c19513c6341da2c6d693454f62edddaf7ac0ad2f13693d0 +FROM amazonlinux:2023@sha256:03b398175f863675186c1a8b38f45d702c5ff0c0faa3a3de9089a0fc58ed7269 WORKDIR /workspace From 04149c5b6d2ff4b14f6c74ac631e4909880bcaf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:29:31 +0200 Subject: [PATCH 0905/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3219) Bumps oss-fuzz-base/base-builder-go from `fea3ac4` to `71ed90b`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index dbafa3a906..557f0da742 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:fea3ac4074ac94e5f5c2dfc73b4bfb22b15f9071c965faa139349087576c96ef +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:71ed90bdf01cd9fad6d508c1d601a4c34b10d9c3f2042808490efae3baba95e1 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 4460a025d8eca7438b7017a8de5877ab969d6220 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:30:20 +0200 Subject: [PATCH 0906/1047] build(deps): bump alpine from `0a4eaa0` to `beefdbd` in /packaging (#3218) Bumps alpine from `0a4eaa0` to `beefdbd`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index e4d5b4ccc8..183cd71fc1 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 +FROM --platform=linux/arm64 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index 596318b359..af7aa9a356 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 +FROM --platform=linux/arm/v7 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 04972ff025021e8563651b6fad7593735bcdd6af Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 10 Sep 2024 17:09:39 +0200 Subject: [PATCH 0907/1047] docs: fix HeaderRegexp example (#3222) From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For > Elements are comma-separated, with optional whitespace surrounding the commas. > If a request goes through multiple proxies, the IP addresses of each successive proxy is listed. > This means that, given well-behaved client and proxies, the rightmost IP address is the IP address > of the most recent proxy and the leftmost IP address is the IP address of the originating client. Fix example by removing trailing space. Signed-off-by: Alexander Yastrebov --- docs/reference/predicates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/predicates.md b/docs/reference/predicates.md index 3908e88b32..b9cd9b5371 100644 --- a/docs/reference/predicates.md +++ b/docs/reference/predicates.md @@ -309,7 +309,7 @@ Parameters: Examples: ``` -HeaderRegexp("X-Forwarded-For", "^192\.168\.0\.[0-2]?[0-9]?[0-9] ") +HeaderRegexp("X-Forwarded-For", "^192\.168\.0\.[0-2]?[0-9]?[0-9]") HeaderRegexp("Accept", "application/(json|xml)") ``` From bd87e31f76fbbf400aaeb4f7a345e277df8f0028 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:59:48 +0200 Subject: [PATCH 0908/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 6 updates (#3224) Bumps the all-go-mod-patch-and-minor group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/instana/go-sensor](https://github.com/instana/go-sensor) | `1.64.0` | `1.65.0` | | [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) | `1.20.2` | `1.20.3` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.26.0` | `0.27.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.28.0` | `0.29.0` | | [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.22.0` | `0.23.0` | Updates `github.com/instana/go-sensor` from 1.64.0 to 1.65.0 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/RELEASE.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.64.0...v1.65.0) Updates `github.com/prometheus/client_golang` from 1.20.2 to 1.20.3 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/v1.20.3/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.2...v1.20.3) Updates `golang.org/x/crypto` from 0.26.0 to 0.27.0 - [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.27.0) Updates `golang.org/x/net` from 0.28.0 to 0.29.0 - [Commits](https://github.com/golang/net/compare/v0.28.0...v0.29.0) Updates `golang.org/x/oauth2` from 0.22.0 to 0.23.0 - [Commits](https://github.com/golang/oauth2/compare/v0.22.0...v0.23.0) Updates `golang.org/x/term` from 0.23.0 to 0.24.0 - [Commits](https://github.com/golang/term/compare/v0.23.0...v0.24.0) --- updated-dependencies: - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 8ce149a243..080cfb0050 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 - github.com/instana/go-sensor v1.64.0 + github.com/instana/go-sensor v1.65.0 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 @@ -31,7 +31,7 @@ require ( github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.20.2 + github.com/prometheus/client_golang v1.20.3 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/redis/go-redis/v9 v9.6.1 @@ -48,12 +48,12 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.27.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.28.0 - golang.org/x/oauth2 v0.22.0 + golang.org/x/net v0.29.0 + golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.8.0 - golang.org/x/term v0.23.0 + golang.org/x/term v0.24.0 golang.org/x/time v0.6.0 google.golang.org/protobuf v1.34.2 gopkg.in/go-jose/go-jose.v2 v2.6.3 @@ -170,8 +170,8 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/tools v0.23.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect diff --git a/go.sum b/go.sum index e1e8f0a775..9eb1b19331 100644 --- a/go.sum +++ b/go.sum @@ -257,8 +257,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.64.0 h1:sago7DnfjuQ0OYDbs6amhc9RqSc76AkOfkyV2W209k8= -github.com/instana/go-sensor v1.64.0/go.mod h1:xRwSUW7zoxo2mKzzKGc163N82F8CuX5EpeNDQeg689M= +github.com/instana/go-sensor v1.65.0 h1:tvghnyNsSE2Bj1nsARLwLFc7GIR9+NXN7QAjMRw6EHg= +github.com/instana/go-sensor v1.65.0/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -381,8 +381,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= +github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -530,8 +530,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -569,11 +569,11 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -611,15 +611,15 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 7cb3dc275ab4594a4e8219c4a2c1e954bb2847a9 Mon Sep 17 00:00:00 2001 From: Farasath Ahamed Date: Thu, 12 Sep 2024 14:28:10 +0200 Subject: [PATCH 0909/1047] opa: pass URL query parameters to OPA policy evaluation (#3207) Improve the envoy request adapting logic to include query parameters sent in the request. This would allow the policy evaluationin opaAuthorizeRequest* and opaServeResponse* filters to make use of query parameters/values in the policy. - Add test cases to cover multi valued query params and trailing ? in URL path - Change the logic to build path with query params - Use escaped path + raw query string to build the path set in envoy request - Add test cases to cover few additional special cases (empty query string, space in path) - Use req.URL.RequestURI() to set the path with query params Signed-off-by: Farasath Ahamed --- .../internal/envoy/skipperadapter.go | 25 +++- .../opaauthorizerequest_test.go | 110 +++++++++++++++++- .../opaserveresponse/opaserveresponse_test.go | 54 ++++++++- 3 files changed, 181 insertions(+), 8 deletions(-) diff --git a/filters/openpolicyagent/internal/envoy/skipperadapter.go b/filters/openpolicyagent/internal/envoy/skipperadapter.go index 6d8cf771bd..7c2284ecb3 100644 --- a/filters/openpolicyagent/internal/envoy/skipperadapter.go +++ b/filters/openpolicyagent/internal/envoy/skipperadapter.go @@ -3,6 +3,7 @@ package envoy import ( "fmt" "net/http" + "net/url" "strings" "unicode/utf8" @@ -11,6 +12,9 @@ import ( ) func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metadata, contextExtensions map[string]string, rawBody []byte) (*ext_authz_v3.CheckRequest, error) { + if err := validateURLForInvalidUTF8(req.URL); err != nil { + return nil, fmt.Errorf("invalid url: %w", err) + } headers := make(map[string]string, len(req.Header)) for h, vv := range req.Header { @@ -25,7 +29,7 @@ func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metada Http: &ext_authz_v3.AttributeContext_HttpRequest{ Host: req.Host, Method: req.Method, - Path: req.URL.Path, + Path: req.URL.RequestURI(), Headers: headers, RawBody: rawBody, }, @@ -35,9 +39,22 @@ func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metada }, } - if !utf8.ValidString(ereq.Attributes.Request.Http.Path) { - return nil, fmt.Errorf("invalid utf8 in path: %q", ereq.Attributes.Request.Http.Path) + return ereq, nil +} + +func validateURLForInvalidUTF8(u *url.URL) error { + if !utf8.ValidString(u.Path) { + return fmt.Errorf("invalid utf8 in path: %q", u.Path) } - return ereq, nil + decodedQuery, err := url.QueryUnescape(u.RawQuery) + if err != nil { + return fmt.Errorf("error unescaping query string %q: %w", u.RawQuery, err) + } + + if !utf8.ValidString(decodedQuery) { + return fmt.Errorf("invalid utf8 in query: %q", u.RawQuery) + } + + return nil } diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 7164b67ab7..5256d663d8 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -2,6 +2,7 @@ package opaauthorizerequest import ( "fmt" + "github.com/zalando/skipper/filters/builtin" "io" "log" "net/http" @@ -29,7 +30,8 @@ func TestAuthorizeRequestFilter(t *testing.T) { for _, ti := range []struct { msg string filterName string - extraeskip string + extraeskipBefore string + extraeskipAfter string bundleName string regoQuery string requestPath string @@ -57,6 +59,49 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Allow Requests with spaces in path", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/my%20path", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Allow Requests with request path overridden by the setPath filter", + filterName: "opaAuthorizeRequest", + extraeskipBefore: `setPath("/allow") ->`, + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/some-random-path-that-would-fail", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Allow Requests with query parameters", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow-with-query?pass=yes&id=1&id=2&msg=help%20me", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Allow Matching Context Extension", filterName: "opaAuthorizeRequest", @@ -71,6 +116,20 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Allow Requests with an empty query string", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_context_extensions", + requestPath: "/path-with-empty-query?", + requestMethod: "GET", + contextExtensions: "com.mycompany.myprop: myvalue", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Allow Matching Environment", filterName: "opaAuthorizeRequest", @@ -96,6 +155,19 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Simple Forbidden with Query Parameters", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow-with-query?tofail=true", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusForbidden, + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Allow With Structured Rules", filterName: "opaAuthorizeRequest", @@ -242,7 +314,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { { msg: "Chained OPA filter with body", filterName: "opaAuthorizeRequestWithBody", - extraeskip: `-> opaAuthorizeRequestWithBody("somebundle.tar.gz")`, + extraeskipAfter: `-> opaAuthorizeRequestWithBody("somebundle.tar.gz")`, bundleName: "somebundle.tar.gz", regoQuery: "envoy/authz/allow_body", requestMethod: "POST", @@ -284,6 +356,20 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Invalid UTF-8 in Query", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow?%c0%ae=%c0%ae%c0%ae", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusBadRequest, + expectedBody: "", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, } { t.Run(ti.msg, func(t *testing.T) { t.Logf("Running test for %v", ti) @@ -309,6 +395,23 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow { input.parsed_path = [ "allow" ] + input.parsed_query = {} + } + + allow { + input.parsed_path = [ "my path" ] + } + + allow { + input.parsed_path = [ "/path-with-empty-query" ] + input.parsed_query = {} + } + + allow { + input.parsed_path = [ "allow-with-query" ] + input.parsed_query.pass == ["yes"] + input.parsed_query.id == ["1", "2"] + input.parsed_query.msg == ["help me"] } allow_context_extensions { @@ -420,8 +523,9 @@ func TestAuthorizeRequestFilter(t *testing.T) { fr.Register(ftSpec) ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, opts...) fr.Register(ftSpec) + fr.Register(builtin.NewSetPath()) - r := eskip.MustParse(fmt.Sprintf(`* -> %s("%s", "%s") %s -> "%s"`, ti.filterName, ti.bundleName, ti.contextExtensions, ti.extraeskip, clientServer.URL)) + r := eskip.MustParse(fmt.Sprintf(`* -> %s %s("%s", "%s") %s -> "%s"`, ti.extraeskipBefore, ti.filterName, ti.bundleName, ti.contextExtensions, ti.extraeskipAfter, clientServer.URL)) proxy := proxytest.New(fr, r...) diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go index 2746c9e8c7..89c4e611d8 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -17,7 +17,7 @@ import ( "github.com/zalando/skipper/filters/openpolicyagent" ) -func TestAuthorizeRequestFilter(t *testing.T) { +func TestServerResponseFilter(t *testing.T) { for _, ti := range []struct { msg string filterName string @@ -70,6 +70,26 @@ func TestAuthorizeRequestFilter(t *testing.T) { expectedBody: "Welcome from policy!", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, }, + { + msg: "Allow With Structured Rules and empty query string in path", + filterName: "opaServeResponse", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "/allow/structured/with-empty-query-string?", + expectedStatus: http.StatusOK, + expectedBody: "Welcome from policy!", + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, + }, + { + msg: "Allow With Structured Rules and Query Params", + filterName: "opaServeResponse", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "/allow/structured/with-query?pass=yes", + expectedStatus: http.StatusOK, + expectedBody: "Welcome from policy!", + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, + }, { msg: "Allow With opa.runtime execution", filterName: "opaServeResponse", @@ -133,6 +153,16 @@ func TestAuthorizeRequestFilter(t *testing.T) { expectedBody: "", expectedHeaders: make(http.Header), }, + { + msg: "Invalid UTF-8 in Query String", + filterName: "opaServeResponse", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow?%c0%ae=%c0%ae%c0%ae", + expectedStatus: http.StatusBadRequest, + expectedBody: "", + expectedHeaders: make(http.Header), + }, } { t.Run(ti.msg, func(t *testing.T) { t.Logf("Running test for %v", ti) @@ -164,6 +194,28 @@ func TestAuthorizeRequestFilter(t *testing.T) { "http_status": 200 } } + + allow_object = response { + input.parsed_path = [ "allow", "structured", "with-empty-query-string" ] + input.parsed_query == {} + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": "Welcome from policy!", + "http_status": 200 + } + } + + allow_object = response { + input.parsed_path = [ "allow", "structured", "with-query" ] + input.parsed_query.pass == ["yes"] + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": "Welcome from policy!", + "http_status": 200 + } + } allow_object = response { input.parsed_path = [ "allow", "production" ] From e085caa96c1e34b548dfe35d6d54180b0505cefe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:47:29 +0200 Subject: [PATCH 0910/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3227) Bumps oss-fuzz-base/base-builder-go from `71ed90b` to `130bed2`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 557f0da742..f241d0ccfa 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:71ed90bdf01cd9fad6d508c1d601a4c34b10d9c3f2042808490efae3baba95e1 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:130bed23f6e9f8f09963a4165d814b66164967c300409aeb8a6f6db48b6783ff COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 7b74d4707507829abe054af2199a846ad7618cf0 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 19 Sep 2024 17:37:58 +0200 Subject: [PATCH 0911/1047] filters/auth: cache yaml config (#3225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * filters/auth: benchmark jwtMetrics filter creation Signed-off-by: Alexander Yastrebov * filters/auth: cache yaml config Cache parsed yaml config for `jwtMetrics` and `oauthTokeninfoValidate` filters to avoid parsing the same configuration over and over when these filters are appended as default filters to all routes. ``` │ HEAD~1 │ HEAD │ │ sec/op │ sec/op vs base │ JwtMetrics_CreateFilter-8 30777.00n ± 6% 18.45n ± 11% -99.94% (p=0.000 n=10) │ HEAD~1 │ HEAD │ │ B/op │ B/op vs base │ JwtMetrics_CreateFilter-8 24.33Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.000 n=10) │ HEAD~1 │ HEAD │ │ allocs/op │ allocs/op vs base │ JwtMetrics_CreateFilter-8 180.0 ± 0% 0.0 ± 0% -100.00% (p=0.000 n=10) ``` Signed-off-by: Alexander Yastrebov --------- Signed-off-by: Alexander Yastrebov --- filters/auth/jwt_metrics.go | 30 ++++---- filters/auth/jwt_metrics_test.go | 14 ++++ filters/auth/tokeninfo.go | 59 ++++++++-------- filters/auth/yamlconfig.go | 84 +++++++++++++++++++++++ filters/auth/yamlconfig_test.go | 114 +++++++++++++++++++++++++++++++ 5 files changed, 259 insertions(+), 42 deletions(-) create mode 100644 filters/auth/yamlconfig.go create mode 100644 filters/auth/yamlconfig_test.go diff --git a/filters/auth/jwt_metrics.go b/filters/auth/jwt_metrics.go index 1d426049fe..686a5da9d0 100644 --- a/filters/auth/jwt_metrics.go +++ b/filters/auth/jwt_metrics.go @@ -6,7 +6,6 @@ import ( "slices" "strings" - "github.com/ghodss/yaml" "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/annotate" @@ -14,8 +13,12 @@ import ( ) type ( - jwtMetricsSpec struct{} + jwtMetricsSpec struct { + yamlConfigParser[jwtMetricsFilter] + } + // jwtMetricsFilter implements [yamlConfig], + // make sure it is not modified after initialization. jwtMetricsFilter struct { Issuers []string `json:"issuers,omitempty"` OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` @@ -27,7 +30,9 @@ type ( ) func NewJwtMetrics() filters.Spec { - return &jwtMetricsSpec{} + return &jwtMetricsSpec{ + newYamlConfigParser[jwtMetricsFilter](64), + } } func (s *jwtMetricsSpec) Name() string { @@ -35,26 +40,21 @@ func (s *jwtMetricsSpec) Name() string { } func (s *jwtMetricsSpec) CreateFilter(args []interface{}) (filters.Filter, error) { - f := &jwtMetricsFilter{} - - if len(args) == 1 { - if config, ok := args[0].(string); !ok { - return nil, fmt.Errorf("requires single string argument") - } else if err := yaml.Unmarshal([]byte(config), f); err != nil { - return nil, fmt.Errorf("failed to parse configuration") - } - } else if len(args) > 1 { - return nil, fmt.Errorf("requires single string argument") + if len(args) == 0 { + return &jwtMetricsFilter{}, nil } + return s.parseSingleArg(args) +} +func (f *jwtMetricsFilter) initialize() error { for _, host := range f.OptOutHosts { if r, err := regexp.Compile(host); err != nil { - return nil, fmt.Errorf("failed to compile opt-out host pattern: %q", host) + return fmt.Errorf("failed to compile opt-out host pattern: %q", host) } else { f.optOutHostsCompiled = append(f.optOutHostsCompiled, r) } } - return f, nil + return nil } func (f *jwtMetricsFilter) Request(ctx filters.FilterContext) {} diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index ef76bef5fd..833a8bc82f 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -368,3 +368,17 @@ func marshalBase64JSON(t *testing.T, v any) string { } return base64.RawURLEncoding.EncodeToString(d) } + +func BenchmarkJwtMetrics_CreateFilter(b *testing.B) { + spec := auth.NewJwtMetrics() + args := []any{`{issuers: [foo, bar], optOutAnnotations: [oauth.disabled], optOutHosts: [ '^.+[.]domain[.]test$' ]}`} + + _, err := spec.CreateFilter(args) + require.NoError(b, err) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = spec.CreateFilter(args) + } +} diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index 7d33f50389..53323012cb 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -9,7 +9,6 @@ import ( "strings" "time" - "github.com/ghodss/yaml" "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/annotate" @@ -49,6 +48,8 @@ type ( tokeninfoSpec struct { typ roleCheckType options TokeninfoOptions + + tokeninfoValidateYamlConfigParser *yamlConfigParser[tokeninfoValidateFilterConfig] } tokeninfoFilter struct { @@ -60,11 +61,16 @@ type ( tokeninfoValidateFilter struct { client tokeninfoClient - config struct { - OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` - UnauthorizedResponse string `json:"unauthorizedResponse,omitempty"` - OptOutHosts []string `json:"optOutHosts,omitempty"` - } + config *tokeninfoValidateFilterConfig + } + + // tokeninfoValidateFilterConfig implements [yamlConfig], + // make sure it is not modified after initialization. + tokeninfoValidateFilterConfig struct { + OptOutAnnotations []string `json:"optOutAnnotations,omitempty"` + UnauthorizedResponse string `json:"unauthorizedResponse,omitempty"` + OptOutHosts []string `json:"optOutHosts,omitempty"` + optOutHostsCompiled []*regexp.Regexp } ) @@ -167,9 +173,12 @@ func NewOAuthTokeninfoAnyKVWithOptions(to TokeninfoOptions) filters.Spec { } func NewOAuthTokeninfoValidate(to TokeninfoOptions) filters.Spec { + p := newYamlConfigParser[tokeninfoValidateFilterConfig](64) return &tokeninfoSpec{ typ: checkOAuthTokeninfoValidate, options: to, + + tokeninfoValidateYamlConfigParser: &p, } } @@ -244,10 +253,11 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) } if s.typ == checkOAuthTokeninfoValidate { - if len(sargs) != 1 { - return nil, fmt.Errorf("requires single string argument") + config, err := s.tokeninfoValidateYamlConfigParser.parseSingleArg(args) + if err != nil { + return nil, err } - return createTokeninfoValidateFilter(ac, sargs[0]) + return &tokeninfoValidateFilter{client: ac, config: config}, nil } f := &tokeninfoFilter{typ: s.typ, client: ac, kv: make(map[string][]string)} @@ -274,22 +284,6 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) return f, nil } -func createTokeninfoValidateFilter(client tokeninfoClient, arg string) (filters.Filter, error) { - f := &tokeninfoValidateFilter{client: client} - if err := yaml.Unmarshal([]byte(arg), &f.config); err != nil { - return nil, fmt.Errorf("failed to parse configuration") - } - - for _, host := range f.config.OptOutHosts { - if r, err := regexp.Compile(host); err != nil { - return nil, fmt.Errorf("failed to compile opt-out host pattern: %q", host) - } else { - f.optOutHostsCompiled = append(f.optOutHostsCompiled, r) - } - } - return f, nil -} - // String prints nicely the tokeninfoFilter configuration based on the // configuration and check used. func (f *tokeninfoFilter) String() string { @@ -444,6 +438,17 @@ func (f *tokeninfoFilter) Request(ctx filters.FilterContext) { func (f *tokeninfoFilter) Response(filters.FilterContext) {} +func (c *tokeninfoValidateFilterConfig) initialize() error { + for _, host := range c.OptOutHosts { + if r, err := regexp.Compile(host); err != nil { + return fmt.Errorf("failed to compile opt-out host pattern: %q", host) + } else { + c.optOutHostsCompiled = append(c.optOutHostsCompiled, r) + } + } + return nil +} + func (f *tokeninfoValidateFilter) Request(ctx filters.FilterContext) { if _, ok := ctx.StateBag()[tokeninfoCacheKey]; ok { return // tokeninfo was already validated by a preceding filter @@ -458,9 +463,9 @@ func (f *tokeninfoValidateFilter) Request(ctx filters.FilterContext) { } } - if len(f.optOutHostsCompiled) > 0 { + if len(f.config.optOutHostsCompiled) > 0 { host := ctx.Request().Host - for _, r := range f.optOutHostsCompiled { + for _, r := range f.config.optOutHostsCompiled { if r.MatchString(host) { return // opt-out from validation } diff --git a/filters/auth/yamlconfig.go b/filters/auth/yamlconfig.go new file mode 100644 index 0000000000..4d5f56dd96 --- /dev/null +++ b/filters/auth/yamlconfig.go @@ -0,0 +1,84 @@ +package auth + +import ( + "fmt" + + "github.com/ghodss/yaml" +) + +// yamlConfigParser parses and caches yaml configurations of type T. +// Use [newYamlConfigParser] to create instances and ensure that *T implements [yamlConfig]. +type yamlConfigParser[T any] struct { + initialize func(*T) error + cacheSize int + cache map[string]*T +} + +// yamlConfig must be implemented by config value pointer type. +// It is used to initialize the value after parsing. +type yamlConfig interface { + initialize() error +} + +// newYamlConfigParser creates a new parser with a given cache size. +func newYamlConfigParser[T any, PT interface { + *T + yamlConfig +}](cacheSize int) yamlConfigParser[T] { + // We want user to specify config type T but ensure that *T implements [yamlConfig]. + // + // Type inference only works for functions but not for types + // (see https://github.com/golang/go/issues/57270 and https://github.com/golang/go/issues/51527) + // therefore we create instances using function with two type parameters + // but second parameter is inferred from the first so the caller does not have to specify it. + // + // To use *T.initialize we setup initialize field + return yamlConfigParser[T]{ + initialize: func(v *T) error { return PT(v).initialize() }, + cacheSize: cacheSize, + cache: make(map[string]*T, cacheSize), + } +} + +// parseSingleArg calls [yamlConfigParser.parse] with the first string argument. +// If args slice does not contain a single string, it returns an error. +func (p *yamlConfigParser[T]) parseSingleArg(args []any) (*T, error) { + if len(args) != 1 { + return nil, fmt.Errorf("requires single string argument") + } + config, ok := args[0].(string) + if !ok { + return nil, fmt.Errorf("requires single string argument") + } + return p.parse(config) +} + +// parse parses a yaml configuration or returns a cached value +// if the exact configuration was already parsed before. +// Returned value is shared by multiple callers and therefore must not be modified. +func (p *yamlConfigParser[T]) parse(config string) (*T, error) { + if v, ok := p.cache[config]; ok { + return v, nil + } + + v := new(T) + if err := yaml.Unmarshal([]byte(config), v); err != nil { + return nil, err + } + + if err := p.initialize(v); err != nil { + return nil, err + } + + // evict random element if cache is full + if p.cacheSize > 0 && len(p.cache) == p.cacheSize { + for k := range p.cache { + delete(p.cache, k) + break + } + } + + p.cache[config] = v + + return v, nil +} diff --git a/filters/auth/yamlconfig_test.go b/filters/auth/yamlconfig_test.go new file mode 100644 index 0000000000..4a0df3662e --- /dev/null +++ b/filters/auth/yamlconfig_test.go @@ -0,0 +1,114 @@ +package auth + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testConfig struct { + Name string + Value int + Error bool + + initialized bool +} + +func (tc *testConfig) initialize() error { + if tc.Error { + return fmt.Errorf("error initializing %s", tc.Name) + } + tc.initialized = true + return nil +} + +func TestYamlConfig_parse(t *testing.T) { + const ( + fooConfig = `{name: foo, value: 42}` + barConfig = `{name: bar, value: 1984}` + bazConfig = `{name: baz, value: 3024}` + ) + + yc := newYamlConfigParser[testConfig](2) + + foo1, err := yc.parse(fooConfig) + require.NoError(t, err) + assert.Equal(t, "foo", foo1.Name) + assert.Equal(t, 42, foo1.Value) + assert.True(t, foo1.initialized) + + foo2, err := yc.parse(fooConfig) + require.NoError(t, err) + assert.True(t, foo1 == foo2, "expected cached instance") + + bar1, err := yc.parse(barConfig) + require.NoError(t, err) + assert.Equal(t, "bar", bar1.Name) + assert.Equal(t, 1984, bar1.Value) + assert.True(t, bar1.initialized) + + baz1, err := yc.parse(bazConfig) + require.NoError(t, err) + assert.Equal(t, "baz", baz1.Name) + assert.Equal(t, 3024, baz1.Value) + assert.True(t, baz1.initialized) + + // check either foo or bar was evicted + assert.Len(t, yc.cache, 2) + assert.Contains(t, yc.cache, bazConfig) + assert.Subset(t, map[string]*testConfig{ + fooConfig: foo1, + barConfig: bar1, + bazConfig: baz1, + }, yc.cache) +} + +func TestYamlConfig_parse_errors(t *testing.T) { + t.Run("invalid yaml", func(t *testing.T) { + yc := newYamlConfigParser[testConfig](1) + + config, err := yc.parse(`invalid yaml`) + assert.Error(t, err) + assert.Nil(t, config) + }) + + t.Run("initialize error", func(t *testing.T) { + yc := newYamlConfigParser[testConfig](1) + + config, err := yc.parse(`{name: foo, error: true}`) + assert.EqualError(t, err, "error initializing foo") + assert.Nil(t, config) + }) +} + +func TestYamlConfig_parseSingleArg(t *testing.T) { + yc := newYamlConfigParser[testConfig](1) + + t.Run("single string arg", func(t *testing.T) { + config, err := yc.parseSingleArg([]any{`{name: foo, value: 42}`}) + require.NoError(t, err) + assert.Equal(t, "foo", config.Name) + assert.Equal(t, 42, config.Value) + assert.True(t, config.initialized) + }) + + t.Run("single non-string arg", func(t *testing.T) { + config, err := yc.parseSingleArg([]any{42}) + assert.EqualError(t, err, "requires single string argument") + assert.Nil(t, config) + }) + + t.Run("empty args", func(t *testing.T) { + config, err := yc.parseSingleArg([]any{}) + assert.EqualError(t, err, "requires single string argument") + assert.Nil(t, config) + }) + + t.Run("too many args", func(t *testing.T) { + config, err := yc.parseSingleArg([]any{`{name: foo, value: 42}`, `{name: bar, value: 1984}`}) + assert.EqualError(t, err, "requires single string argument") + assert.Nil(t, config) + }) +} From 3c2a615fb98ed7b73bef2147c04dc34d1ce52f16 Mon Sep 17 00:00:00 2001 From: czhou-brex Date: Thu, 19 Sep 2024 09:21:34 -0700 Subject: [PATCH 0912/1047] Add configurable cookie name prefix for the OIDC filter (#3234) Allow specifying cookie name prefix for OIDC filters Signed-off-by: Carl Zhou --- docs/reference/filters.md | 1 + filters/auth/oidc.go | 36 +++++++++++++++++++++--------------- filters/auth/oidc_test.go | 17 +++++++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 584333ba68..9bd4af47e3 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1844,6 +1844,7 @@ The filter needs the following parameters: * **Auth Code Options** (optional) Passes key/value parameters to a provider's authorization endpoint. The value can be dynamically set by a query parameter with the same key name if the placeholder `skipper-request-query` is used. * **Upstream Headers** (optional) The upstream endpoint will receive these headers which values are parsed from the OIDC information. The header definition can be one or more header-query pairs, space delimited. The query syntax is [GJSON](https://github.com/tidwall/gjson/blob/master/SYNTAX.md). * **SubdomainsToRemove** (optional, default "1") Configures number of subdomains to remove from the request hostname to derive OIDC cookie domain. By default one subdomain is removed, e.g. for the www.example.com request hostname the OIDC cookie domain will be example.com (to support SSO for all subdomains of the example.com). Configure "0" to use the same hostname. Note that value is a string. +* **Custom Cookie Name** (optional) Defines a constant cookie name generated by the OIDC filter. By default the cookie name is SkipperOauthOidc{hash}, where {hash} is a generated value. #### oauthOidcAnyClaims diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index 6d4e502b7e..88c952c52c 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -85,6 +85,7 @@ const ( paramAuthCodeOpts paramUpstrHeaders paramSubdomainsToRemove + paramCookieName ) type OidcOptions struct { @@ -201,22 +202,27 @@ func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) return nil, filters.ErrInvalidFilterParameters } - h := sha256.New() - for i, s := range sargs { - // CallbackURL not taken into account for cookie hashing for additional sub path ingresses - if i == paramCallbackURL { - continue - } - // SubdomainsToRemove not taken into account for cookie hashing for additional sub-domain ingresses - if i == paramSubdomainsToRemove { - continue + var cookieName string + if len(sargs) > paramCookieName && sargs[paramCookieName] != "" { + cookieName = sargs[paramCookieName] + } else { + h := sha256.New() + for i, s := range sargs { + // CallbackURL not taken into account for cookie hashing for additional sub path ingresses + if i == paramCallbackURL { + continue + } + // SubdomainsToRemove not taken into account for cookie hashing for additional sub-domain ingresses + if i == paramSubdomainsToRemove { + continue + } + h.Write([]byte(s)) } - h.Write([]byte(s)) + byteSlice := h.Sum(nil) + sargsHash := fmt.Sprintf("%x", byteSlice)[:8] + cookieName = oauthOidcCookieName + sargsHash + "-" } - byteSlice := h.Sum(nil) - sargsHash := fmt.Sprintf("%x", byteSlice)[:8] - generatedCookieName := oauthOidcCookieName + sargsHash + "-" - log.Debugf("Generated Cookie Name: %s", generatedCookieName) + log.Debugf("Cookie Name: %s", cookieName) redirectURL, err := url.Parse(sargs[paramCallbackURL]) if err != nil || sargs[paramCallbackURL] == "" { @@ -269,7 +275,7 @@ func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) ClientID: oidcClientId, }), validity: validity, - cookiename: generatedCookieName, + cookiename: cookieName, encrypter: encrypter, compressor: newDeflatePoolCompressor(flate.BestCompression), subdomainsToRemove: subdomainsToRemove, diff --git a/filters/auth/oidc_test.go b/filters/auth/oidc_test.go index 7198bd223d..771fde01fe 100644 --- a/filters/auth/oidc_test.go +++ b/filters/auth/oidc_test.go @@ -711,6 +711,7 @@ func TestOIDCSetup(t *testing.T) { expectCookieDomain string filterCookies []string extraClaims jwt.MapClaims + expectCookieName string }{{ msg: "wrong provider", filter: `oauthOidcAnyClaims("no url", "", "", "{{ .RedirectURL }}", "", "")`, @@ -848,6 +849,16 @@ func TestOIDCSetup(t *testing.T) { expected: 200, expectCookieDomain: "bar.foo.skipper.test", filterCookies: []string{"badheader", "malformed"}, + }, { + msg: "custom cookie name", + filter: `oauthOidcUserInfo("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "", "", "", "", "", "custom-cookie")`, + expected: 200, + expectCookieName: "custom-cookie", + }, { + msg: "default cookie name when not specified", + filter: `oauthOidcUserInfo("{{ .OIDCServerURL }}", "valid-client", "mysec", "{{ .RedirectURL }}", "", "")`, + expected: 200, + expectCookieName: "skipperOauthOidc", }} { t.Run(tc.msg, func(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -976,6 +987,12 @@ func TestOIDCSetup(t *testing.T) { assert.True(t, c.Value == "") } } + + // Check for custom cookie name + if tc.expectCookieName != "" { + assert.True(t, strings.HasPrefix(c.Name, tc.expectCookieName), + "Cookie name should start with %s, but got %s", tc.expectCookieName, c.Name) + } } } }) From 7f6f0378e85e083c276224e60ee0fe2ebe747fc7 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 19 Sep 2024 19:47:24 +0200 Subject: [PATCH 0913/1047] predicates/host: add HostAny benchmark (#3239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current implementation uses linear probe that works well for small number of hosts. I thought about implementing it using map lookup but that is slower for less than 50 hosts so I propose to only keep the benchmark that may be used for future improvements. Here is comparison with map-based implementation for reference: ``` │ HEAD~1 │ HEAD │ │ sec/op │ sec/op vs base │ HostAny/1-8 3.738n ± 3% 5.844n ± 3% +56.36% (p=0.000 n=10) HostAny/2-8 7.728n ± 4% 9.274n ± 2% +20.02% (p=0.000 n=10) HostAny/5-8 11.32n ± 4% 13.09n ± 4% +15.63% (p=0.000 n=10) HostAny/10-8 22.35n ± 3% 11.86n ± 15% -46.94% (p=0.000 n=10) HostAny/20-8 7.684n ± 2% 11.310n ± 9% +47.19% (p=0.000 n=10) HostAny/50-8 66.75n ± 4% 12.10n ± 12% -81.87% (p=0.000 n=10) HostAny/100-8 161.80n ± 1% 11.92n ± 13% -92.63% (p=0.000 n=10) geomean 17.98n 10.46n -41.79% │ HEAD~1 │ HEAD │ │ B/op │ B/op vs base │ HostAny/1-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/2-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/5-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/10-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/20-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/50-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/100-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean │ HEAD~1 │ HEAD │ │ allocs/op │ allocs/op vs base │ HostAny/1-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/2-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/5-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/10-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/20-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/50-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ HostAny/100-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ``` Signed-off-by: Alexander Yastrebov --- predicates/host/any_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/predicates/host/any_test.go b/predicates/host/any_test.go index 4081b56827..d67ce24767 100644 --- a/predicates/host/any_test.go +++ b/predicates/host/any_test.go @@ -106,3 +106,33 @@ func TestHostAnyMatch(t *testing.T) { }) } } + +var matchSink bool + +func BenchmarkHostAny(b *testing.B) { + for _, n := range []int{1, 2, 5, 10, 20, 50, 100} { + b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { + s := NewAny() + args := make([]any, n) + for i := 0; i < n; i++ { + args[i] = fmt.Sprintf("example%d.org", i) + } + p, err := s.Create(args) + if err != nil { + b.Fatal(err) + } + + req := &http.Request{Host: args[len(args)/2].(string)} + matchSink = p.Match(req) + if !matchSink { + b.Fatal("expected to match") + } + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + matchSink = p.Match(req) + } + }) + } +} From d161ac57c23b2b064f024a27946239631ab80afc Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 19 Sep 2024 22:25:16 +0200 Subject: [PATCH 0914/1047] filters/auth: fix assert usage in tokeninfocache tests (#3240) Second argument of assert.Equal should be expected value. Signed-off-by: Alexander Yastrebov --- filters/auth/tokeninfocache_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/filters/auth/tokeninfocache_test.go b/filters/auth/tokeninfocache_test.go index f5edb33da1..62a6d9f41b 100644 --- a/filters/auth/tokeninfocache_test.go +++ b/filters/auth/tokeninfocache_test.go @@ -99,8 +99,8 @@ func TestTokeninfoCache(t *testing.T) { require.NoError(t, err) assert.Equal(t, int32(1), authRequests) - assert.Equal(t, info["uid"], token) - assert.Equal(t, info["expires_in"], float64(600), "expected TokenTTLSeconds") + assert.Equal(t, token, info["uid"]) + assert.Equal(t, float64(600), info["expires_in"], "expected TokenTTLSeconds") // Second request after "sleeping" fractional number of seconds const delay = float64(5.7) @@ -110,8 +110,8 @@ func TestTokeninfoCache(t *testing.T) { require.NoError(t, err) assert.Equal(t, int32(1), authRequests, "expected no request to auth sever") - assert.Equal(t, info["uid"], token) - assert.Equal(t, info["expires_in"], float64(595), "expected TokenTTLSeconds - truncate(delay)") + assert.Equal(t, token, info["uid"]) + assert.Equal(t, float64(595), info["expires_in"], "expected TokenTTLSeconds - truncate(delay)") // Third request after "sleeping" longer than cache TTL clock.add(CacheTTL) @@ -120,8 +120,8 @@ func TestTokeninfoCache(t *testing.T) { require.NoError(t, err) assert.Equal(t, int32(2), authRequests, "expected new request to auth sever") - assert.Equal(t, info["uid"], token) - assert.Equal(t, info["expires_in"], float64(294), "expected truncate(TokenTTLSeconds - CacheTTL - delay)") + assert.Equal(t, token, info["uid"]) + assert.Equal(t, float64(294), info["expires_in"], "expected truncate(TokenTTLSeconds - CacheTTL - delay)") // Fourth request with a new token evicts cached value token = newTestTokeninfoToken(clock.now()).String() @@ -130,8 +130,8 @@ func TestTokeninfoCache(t *testing.T) { require.NoError(t, err) assert.Equal(t, int32(3), authRequests, "expected new request to auth sever") - assert.Equal(t, info["uid"], token) - assert.Equal(t, info["expires_in"], float64(600), "expected TokenTTLSeconds") + assert.Equal(t, token, info["uid"]) + assert.Equal(t, float64(600), info["expires_in"], "expected TokenTTLSeconds") } // Tests race between reading and writing cache for the same token From e34f98895ab4f845975d414df3af6704f50fa512 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 20 Sep 2024 19:33:10 +0200 Subject: [PATCH 0915/1047] filters/auth: release tokeninfo cache mutex earlier (#3241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * filters/auth: reset parallelism for BenchmarkTokeninfoCache Signed-off-by: Alexander Yastrebov * filters/auth: release tokeninfo cache mutex earlier Cached value is readonly so release cache mutex early to copy and adjust cached value outside of critical section. ``` goos: linux goarch: amd64 pkg: github.com/zalando/skipper/filters/auth cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz │ HEAD~1 │ HEAD │ │ sec/op │ sec/op vs base │ TokeninfoCache/tokens=1,cacheSize=1,p=0-8 639.4n ± 21% 271.9n ± 6% -57.48% (p=0.000 n=10) TokeninfoCache/tokens=2,cacheSize=2,p=0-8 625.7n ± 24% 420.4n ± 32% -32.81% (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=100,p=0-8 869.8n ± 17% 463.4n ± 3% -46.72% (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=100,p=10000-8 670.8n ± 12% 594.6n ± 2% -11.36% (p=0.000 n=10) TokeninfoCache/tokens=4,cacheSize=2,p=0-8 2.553m ± 1% 2.554m ± 1% ~ (p=0.684 n=10) TokeninfoCache/tokens=100,cacheSize=10,p=0-8 2.581m ± 1% 2.566m ± 0% ~ (p=0.089 n=10) geomean 10.74µ 7.688µ -28.44% │ HEAD~1 │ HEAD │ │ B/op │ B/op vs base │ TokeninfoCache/tokens=1,cacheSize=1,p=0-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=2,cacheSize=2,p=0-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=0-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=10000-8 366.0 ± 0% 366.0 ± 1% ~ (p=0.452 n=10) TokeninfoCache/tokens=4,cacheSize=2,p=0-8 27.00 ± 4% 27.00 ± 0% ~ (p=0.474 n=10) TokeninfoCache/tokens=100,cacheSize=10,p=0-8 28.00 ± 7% 27.00 ± 7% ~ (p=0.259 n=10) geomean 149.7 148.8 -0.60% ¹ all samples are equal │ HEAD~1 │ HEAD │ │ allocs/op │ allocs/op vs base │ TokeninfoCache/tokens=1,cacheSize=1,p=0-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=2,cacheSize=2,p=0-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=0-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=10000-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=4,cacheSize=2,p=0-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=10,p=0-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ``` Signed-off-by: Alexander Yastrebov --------- Signed-off-by: Alexander Yastrebov --- filters/auth/tokeninfocache.go | 10 ++++++++-- filters/auth/tokeninfocache_test.go | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/filters/auth/tokeninfocache.go b/filters/auth/tokeninfocache.go index 65cd65e112..c925ae5834 100644 --- a/filters/auth/tokeninfocache.go +++ b/filters/auth/tokeninfocache.go @@ -61,16 +61,19 @@ func (c *tokeninfoCache) cached(token string) map[string]any { now := c.now() c.mu.Lock() - defer c.mu.Unlock() if e, ok := c.cache[token]; ok { if now.Before(e.expiresAt) { c.history.MoveToFront(e.href) + cachedInfo := e.info + c.mu.Unlock() + // It might be ok to return cached value // without adjusting "expires_in" to avoid copy + // if caller never modifies the result and // when "expires_in" did not change (same second) // or for small TTL values - info := shallowCopyOf(e.info) + info := shallowCopyOf(cachedInfo) elapsed := now.Sub(e.cachedAt).Truncate(time.Second).Seconds() info[expiresInField] = info[expiresInField].(float64) - elapsed @@ -81,6 +84,9 @@ func (c *tokeninfoCache) cached(token string) map[string]any { c.history.Remove(e.href) } } + + c.mu.Unlock() + return nil } diff --git a/filters/auth/tokeninfocache_test.go b/filters/auth/tokeninfocache_test.go index 62a6d9f41b..a5cfbf4a0e 100644 --- a/filters/auth/tokeninfocache_test.go +++ b/filters/auth/tokeninfocache_test.go @@ -249,6 +249,8 @@ func BenchmarkTokeninfoCache(b *testing.B) { if bi.parallelism != 0 { b.SetParallelism(bi.parallelism) + } else { + b.SetParallelism(1) } b.ReportAllocs() From cbc392aeb19009fcbccecd00b91ca37c43ea5aa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:43:27 +0200 Subject: [PATCH 0916/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3242) Bumps oss-fuzz-base/base-builder-go from `130bed2` to `2347618`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index f241d0ccfa..14f2130cf8 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:130bed23f6e9f8f09963a4165d814b66164967c300409aeb8a6f6db48b6783ff +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:2347618de7ba036a4822540f562636b6fb06c804be5381de24e2d3b2d5d70207 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 85937e6d80b682caab0c2099f6a728e2a932eacb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:44:20 +0200 Subject: [PATCH 0917/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 3 updates (#3245) Bumps the all-go-mod-patch-and-minor group with 3 updates in the / directory: [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa), [github.com/open-policy-agent/opa-envoy-plugin](https://github.com/open-policy-agent/opa-envoy-plugin) and [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang). Updates `github.com/open-policy-agent/opa` from 0.67.1 to 0.68.0 - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.67.1...v0.68.0) Updates `github.com/open-policy-agent/opa-envoy-plugin` from 0.67.1-envoy to 0.68.0-envoy-4 - [Release notes](https://github.com/open-policy-agent/opa-envoy-plugin/releases) - [Changelog](https://github.com/open-policy-agent/opa-envoy-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa-envoy-plugin/compare/v0.67.1-envoy...v0.68.0-envoy-4) Updates `github.com/prometheus/client_golang` from 1.20.3 to 1.20.4 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.3...v1.20.4) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/open-policy-agent/opa-envoy-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 17 +++++++++-------- go.sum | 34 ++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 080cfb0050..ecaba54f5d 100644 --- a/go.mod +++ b/go.mod @@ -27,11 +27,11 @@ require ( github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 - github.com/open-policy-agent/opa v0.67.1 - github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy + github.com/open-policy-agent/opa v0.68.0 + github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4 github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.20.3 + github.com/prometheus/client_golang v1.20.4 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/redis/go-redis/v9 v9.6.1 @@ -78,7 +78,7 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect - github.com/containerd/containerd v1.7.20 // indirect + github.com/containerd/containerd v1.7.21 // indirect github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect @@ -128,7 +128,8 @@ require ( github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -169,14 +170,14 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect - golang.org/x/mod v0.19.0 // indirect + golang.org/x/mod v0.20.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.23.0 // indirect + golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.65.0 // indirect + google.golang.org/grpc v1.66.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.3.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 9eb1b19331..9356ad0578 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,8 @@ github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnTh github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ= -github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0= +github.com/containerd/containerd v1.7.21 h1:USGXRK1eOC/SX0L195YgxTHb0a00anxajOzgfN0qrCA= +github.com/containerd/containerd v1.7.21/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= @@ -315,8 +315,10 @@ github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vyg github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -346,10 +348,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/open-policy-agent/opa v0.67.1 h1:rzy26J6g1X+CKknAcx0Vfbt41KqjuSzx4E0A8DAZf3E= -github.com/open-policy-agent/opa v0.67.1/go.mod h1:aqKlHc8E2VAAylYE9x09zJYr/fYzGX+JKne89UGqFzk= -github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy h1:u8gvJx13GaPDm9koAgwGOJ1kXlh1vRP/EyrNyF9F8Lc= -github.com/open-policy-agent/opa-envoy-plugin v0.67.1-envoy/go.mod h1:fW+AG2SaKmlrjuo202o2cTZlJIyl85Y4n/CJvjmc4mA= +github.com/open-policy-agent/opa v0.68.0 h1:Jl3U2vXRjwk7JrHmS19U3HZO5qxQRinQbJ2eCJYSqJQ= +github.com/open-policy-agent/opa v0.68.0/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w= +github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4 h1:yq9TvPBS1qA301q88k8MbGEwba3yu14FaeGVriCvALU= +github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4/go.mod h1:vRdAOKLNgXitws+CdhIAND6TT0wkFc2IVzAHB3tK4qw= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -381,8 +383,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= -github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -552,8 +554,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -638,8 +640,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 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= @@ -669,8 +671,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= 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= From ce501f8a5d22901587be2ca021685295b65997f5 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 24 Sep 2024 15:35:59 +0200 Subject: [PATCH 0918/1047] skipper: add server connection keepalive limits (#3246) Clients may connect to a subset of Skipper fleet which leads to uneven request distribution and increased cpu usage. Autoscaling of Skipper fleet is not effective because clients stay connected to old instances while new instances are underutilized. This change adds ConnManager that tracks creation of new connections and closes connections when their age or number of requests served reaches configured limits. Signed-off-by: Alexander Yastrebov --- config/config.go | 6 ++ docs/operation/operation.md | 10 +++ net/connmanager.go | 95 ++++++++++++++++++++++++++ net/connmanager_test.go | 128 ++++++++++++++++++++++++++++++++++++ skipper.go | 19 +++++- 5 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 net/connmanager.go create mode 100644 net/connmanager_test.go diff --git a/config/config.go b/config/config.go index 20799722e1..16b205fb7f 100644 --- a/config/config.go +++ b/config/config.go @@ -243,6 +243,8 @@ type Config struct { ReadHeaderTimeoutServer time.Duration `yaml:"read-header-timeout-server"` WriteTimeoutServer time.Duration `yaml:"write-timeout-server"` IdleTimeoutServer time.Duration `yaml:"idle-timeout-server"` + KeepaliveServer time.Duration `yaml:"keepalive-server"` + KeepaliveRequestsServer int `yaml:"keepalive-requests-server"` MaxHeaderBytes int `yaml:"max-header-bytes"` EnableConnMetricsServer bool `yaml:"enable-connection-metrics"` TimeoutBackend time.Duration `yaml:"timeout-backend"` @@ -544,6 +546,8 @@ func NewConfig() *Config { flag.DurationVar(&cfg.ReadHeaderTimeoutServer, "read-header-timeout-server", 60*time.Second, "set ReadHeaderTimeout for http server connections") flag.DurationVar(&cfg.WriteTimeoutServer, "write-timeout-server", 60*time.Second, "set WriteTimeout for http server connections") flag.DurationVar(&cfg.IdleTimeoutServer, "idle-timeout-server", 60*time.Second, "set IdleTimeout for http server connections") + flag.DurationVar(&cfg.KeepaliveServer, "keepalive-server", 0*time.Second, "sets maximum age for http server connections. The connection is closed after it existed for this duration. Default is 0 for unlimited.") + flag.IntVar(&cfg.KeepaliveRequestsServer, "keepalive-requests-server", 0, "sets maximum number of requests for http server connections. The connection is closed after serving this number of requests. Default is 0 for unlimited.") flag.IntVar(&cfg.MaxHeaderBytes, "max-header-bytes", http.DefaultMaxHeaderBytes, "set MaxHeaderBytes for http server connections") flag.BoolVar(&cfg.EnableConnMetricsServer, "enable-connection-metrics", false, "enables connection metrics for http server connections") flag.DurationVar(&cfg.TimeoutBackend, "timeout-backend", 60*time.Second, "sets the TCP client connection timeout for backend connections") @@ -879,6 +883,8 @@ func (c *Config) ToOptions() skipper.Options { ReadHeaderTimeoutServer: c.ReadHeaderTimeoutServer, WriteTimeoutServer: c.WriteTimeoutServer, IdleTimeoutServer: c.IdleTimeoutServer, + KeepaliveServer: c.KeepaliveServer, + KeepaliveRequestsServer: c.KeepaliveRequestsServer, MaxHeaderBytes: c.MaxHeaderBytes, EnableConnMetricsServer: c.EnableConnMetricsServer, TimeoutBackend: c.TimeoutBackend, diff --git a/docs/operation/operation.md b/docs/operation/operation.md index c7ee3e9c5a..142254ff7f 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -124,6 +124,16 @@ combinations of idle timeouts can lead to a few unexpected HTTP 502. -idle-timeout-server duration maximum idle connections per backend host (default 1m0s) +This configures maximum number of requests served by server connections: + + -keepalive-requests-server int + sets maximum number of requests for http server connections. The connection is closed after serving this number of requests. Default is 0 for unlimited. + +This configures maximum age for server connections: + + -keepalive-server duration + sets maximum age for http server connections. The connection is closed after it existed for this duration. Default is 0 for unlimited. + This will set MaxHeaderBytes in [http.Server](https://golang.org/pkg/net/http/#Server) to limit the size of the http header from your clients. diff --git a/net/connmanager.go b/net/connmanager.go new file mode 100644 index 0000000000..fb65f79bf9 --- /dev/null +++ b/net/connmanager.go @@ -0,0 +1,95 @@ +package net + +import ( + "context" + "fmt" + "net" + "net/http" + "time" + + "github.com/zalando/skipper/metrics" +) + +// ConnManager tracks creation of HTTP server connections and +// closes connections when their age or number of requests served reaches configured limits. +// Use [ConnManager.Configure] method to setup ConnManager for an [http.Server]. +type ConnManager struct { + // Metrics is an optional metrics registry to count connection events. + Metrics metrics.Metrics + + // Keepalive is the duration after which server connection is closed. + Keepalive time.Duration + + // KeepaliveRequests is the number of requests after which server connection is closed. + KeepaliveRequests int + + handler http.Handler +} + +type connState struct { + expiresAt time.Time + requests int +} + +type contextKey struct{} + +var connection contextKey + +func (cm *ConnManager) Configure(server *http.Server) { + cm.handler = server.Handler + server.Handler = http.HandlerFunc(cm.serveHTTP) + + if cc := server.ConnContext; cc != nil { + server.ConnContext = func(ctx context.Context, c net.Conn) context.Context { + ctx = cc(ctx, c) + return cm.connContext(ctx, c) + } + } else { + server.ConnContext = cm.connContext + } + + if cs := server.ConnState; cs != nil { + server.ConnState = func(c net.Conn, state http.ConnState) { + cs(c, state) + cm.connState(c, state) + } + } else { + server.ConnState = cm.connState + } +} + +func (cm *ConnManager) serveHTTP(w http.ResponseWriter, r *http.Request) { + state, _ := r.Context().Value(connection).(*connState) + state.requests++ + + if cm.KeepaliveRequests > 0 && state.requests >= cm.KeepaliveRequests { + w.Header().Set("Connection", "close") + + cm.count("lb-conn-closed.keepalive-requests") + } + + if cm.Keepalive > 0 && time.Now().After(state.expiresAt) { + w.Header().Set("Connection", "close") + + cm.count("lb-conn-closed.keepalive") + } + + cm.handler.ServeHTTP(w, r) +} + +func (cm *ConnManager) connContext(ctx context.Context, _ net.Conn) context.Context { + state := &connState{ + expiresAt: time.Now().Add(cm.Keepalive), + } + return context.WithValue(ctx, connection, state) +} + +func (cm *ConnManager) connState(_ net.Conn, state http.ConnState) { + cm.count(fmt.Sprintf("lb-conn-%s", state)) +} + +func (cm *ConnManager) count(name string) { + if cm.Metrics != nil { + cm.Metrics.IncCounter(name) + } +} diff --git a/net/connmanager_test.go b/net/connmanager_test.go new file mode 100644 index 0000000000..0b1aa06624 --- /dev/null +++ b/net/connmanager_test.go @@ -0,0 +1,128 @@ +package net_test + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zalando/skipper/metrics/metricstest" + snet "github.com/zalando/skipper/net" +) + +func TestConnManager(t *testing.T) { + const ( + keepaliveRequests = 3 + keepalive = 100 * time.Millisecond + + testRequests = keepaliveRequests * 5 + ) + t.Run("does not close connection without limits", func(t *testing.T) { + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + m := &metricstest.MockMetrics{} + cm := &snet.ConnManager{ + Metrics: m, + } + cm.Configure(ts.Config) + + ts.Start() + defer ts.Close() + + for i := 0; i < testRequests; i++ { + resp, err := ts.Client().Get(ts.URL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.False(t, resp.Close) + } + + time.Sleep(100 * time.Millisecond) // wait for connection state update + + m.WithCounters(func(counters map[string]int64) { + assert.Equal(t, int64(1), counters["lb-conn-new"]) + assert.Equal(t, int64(testRequests), counters["lb-conn-active"]) + assert.Equal(t, int64(testRequests), counters["lb-conn-idle"]) + assert.Equal(t, int64(0), counters["lb-conn-closed"]) + }) + }) + t.Run("closes connection after keepalive requests", func(t *testing.T) { + const keepaliveRequests = 3 + + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + m := &metricstest.MockMetrics{} + cm := &snet.ConnManager{ + Metrics: m, + KeepaliveRequests: keepaliveRequests, + } + cm.Configure(ts.Config) + + ts.Start() + defer ts.Close() + + for i := 1; i < testRequests; i++ { + resp, err := ts.Client().Get(ts.URL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + if i%keepaliveRequests == 0 { + assert.True(t, resp.Close) + } else { + assert.False(t, resp.Close) + } + } + + time.Sleep(100 * time.Millisecond) // wait for connection state update + + m.WithCounters(func(counters map[string]int64) { + rounds := int64(testRequests / keepaliveRequests) + + assert.Equal(t, rounds, counters["lb-conn-new"]) + assert.Equal(t, rounds-1, counters["lb-conn-closed"]) + assert.Equal(t, rounds-1, counters["lb-conn-closed.keepalive-requests"]) + }) + }) + + t.Run("closes connection after keepalive timeout", func(t *testing.T) { + const keepalive = 100 * time.Millisecond + + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + m := &metricstest.MockMetrics{} + cm := &snet.ConnManager{ + Metrics: m, + Keepalive: keepalive, + } + cm.Configure(ts.Config) + + ts.Start() + defer ts.Close() + + for i := 0; i < testRequests; i++ { + resp, err := ts.Client().Get(ts.URL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.False(t, resp.Close) + } + + time.Sleep(2 * keepalive) + + resp, err := ts.Client().Get(ts.URL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.True(t, resp.Close) + + time.Sleep(100 * time.Millisecond) // wait for connection state update + + m.WithCounters(func(counters map[string]int64) { + assert.Equal(t, int64(1), counters["lb-conn-new"]) + assert.Equal(t, int64(1), counters["lb-conn-closed"]) + assert.Equal(t, int64(1), counters["lb-conn-closed.keepalive"]) + }) + }) +} diff --git a/skipper.go b/skipper.go index 83a12a673a..bce6097b9d 100644 --- a/skipper.go +++ b/skipper.go @@ -360,6 +360,14 @@ type Options struct { // Defines IdleTimeout for server http connections. IdleTimeoutServer time.Duration + // KeepaliveServer configures maximum age for server http connections. + // The connection is closed after it existed for this duration. + KeepaliveServer time.Duration + + // KeepaliveRequestsServer configures maximum number of requests for server http connections. + // The connection is closed after serving this number of requests. + KeepaliveRequestsServer int + // Defines MaxHeaderBytes for server http connections. MaxHeaderBytes int @@ -1334,12 +1342,17 @@ func listenAndServeQuit( ErrorLog: newServerErrorLog(), } + cm := &skpnet.ConnManager{ + Keepalive: o.KeepaliveServer, + KeepaliveRequests: o.KeepaliveRequestsServer, + } + if o.EnableConnMetricsServer { - srv.ConnState = func(conn net.Conn, state http.ConnState) { - mtr.IncCounter(fmt.Sprintf("lb-conn-%s", state)) - } + cm.Metrics = mtr } + cm.Configure(srv) + log.Infof("Listen on %v", address) l, err := listen(o, address, mtr) From 8ea6c5b58b4922c76c9936de43c73be6848cd6d5 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 24 Sep 2024 19:05:56 +0200 Subject: [PATCH 0919/1047] docs: document single argument variant of `tracingBaggageToTag` (#3247) Implementation allows single argument variant which is used in the wild so document it. Follow up on #1160 and #1168 Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 9bd4af47e3..015f35d185 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -3303,11 +3303,19 @@ Syntax: tracingBaggageToTag("", "") ``` -Example: If a trace consists of baggage item named `foo` with a value `bar`. Adding below filter will add a tag named `baz` with value `bar` +Example: + +If a trace consists of a baggage item named `foo` with a value of `bar`, +then adding the filter below will add a tag named `baz` with the value `bar`: ``` tracingBaggageToTag("foo", "baz") ``` +A single-argument variant uses the baggage item name as the tag name, i.e., it will add a tag named `foo` with the value `bar`: +``` +tracingBaggageToTag("foo") +``` + ### stateBagToTag This filter sets an opentracing tag from the filter context (state bag). From 1db1ac7cb61ebef6caca67e9f96ea6944c51e212 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:13:53 +0200 Subject: [PATCH 0920/1047] build(deps): bump alpine from `beefdbd` to `7eccd4d` in /packaging (#3249) --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 183cd71fc1..efddfd3d05 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d +FROM --platform=linux/arm64 alpine@sha256:7eccd4d5d574a3c889b0a6d97b2cdd0308c8e1afc2bba8d467c2b87d879b0c1c LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index af7aa9a356..d805120dd0 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d +FROM --platform=linux/arm/v7 alpine@sha256:7eccd4d5d574a3c889b0a6d97b2cdd0308c8e1afc2bba8d467c2b87d879b0c1c LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 2e9e3cf97fa961d8d1d099bdbfaec27dc185d995 Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Wed, 25 Sep 2024 10:01:46 +0200 Subject: [PATCH 0921/1047] minor: remove unused endpointRegistry field from healthyEndpoints and fadeIn (#3248) Signed-off-by: Roman Zavodskikh --- proxy/fadein.go | 3 +-- proxy/fadein_internal_test.go | 2 +- proxy/healthy_endpoints.go | 1 - proxy/proxy.go | 4 +--- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/proxy/fadein.go b/proxy/fadein.go index 80857b41f7..c9d898dfa9 100644 --- a/proxy/fadein.go +++ b/proxy/fadein.go @@ -9,8 +9,7 @@ import ( ) type fadeIn struct { - rnd *rand.Rand - endpointRegistry *routing.EndpointRegistry + rnd *rand.Rand } func (f *fadeIn) fadeInScore(lifetime time.Duration, duration time.Duration, exponent float64) float64 { diff --git a/proxy/fadein_internal_test.go b/proxy/fadein_internal_test.go index 8ebf92a9f4..6fccb060b1 100644 --- a/proxy/fadein_internal_test.go +++ b/proxy/fadein_internal_test.go @@ -92,7 +92,7 @@ func initializeEndpoints(endpointAges []float64, algorithmName string, fadeInDur registry.GetMetrics(eps[i]).SetDetected(detectionTimes[i]) } - proxy := &Proxy{registry: registry, fadein: &fadeIn{rnd: rand.New(loadbalancer.NewLockedSource()), endpointRegistry: registry}, quit: make(chan struct{})} + proxy := &Proxy{registry: registry, fadein: &fadeIn{rnd: rand.New(loadbalancer.NewLockedSource())}, quit: make(chan struct{})} return route, proxy, eps } diff --git a/proxy/healthy_endpoints.go b/proxy/healthy_endpoints.go index 70c0366361..8a1c9c6092 100644 --- a/proxy/healthy_endpoints.go +++ b/proxy/healthy_endpoints.go @@ -9,7 +9,6 @@ import ( type healthyEndpoints struct { rnd *rand.Rand - endpointRegistry *routing.EndpointRegistry maxUnhealthyEndpointsRatio float64 } diff --git a/proxy/proxy.go b/proxy/proxy.go index 0babad8da5..5c4fe9faca 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -841,7 +841,6 @@ func WithParams(p Params) *Proxy { if p.EnablePassiveHealthCheck { healthyEndpointsChooser = &healthyEndpoints{ rnd: rand.New(loadbalancer.NewLockedSource()), - endpointRegistry: p.EndpointRegistry, maxUnhealthyEndpointsRatio: p.PassiveHealthCheck.MaxUnhealthyEndpointsRatio, } } @@ -849,8 +848,7 @@ func WithParams(p Params) *Proxy { routing: p.Routing, registry: p.EndpointRegistry, fadein: &fadeIn{ - rnd: rand.New(loadbalancer.NewLockedSource()), - endpointRegistry: p.EndpointRegistry, + rnd: rand.New(loadbalancer.NewLockedSource()), }, heathlyEndpoints: healthyEndpointsChooser, roundTripper: p.CustomHttpRoundTripperWrap(tr), From 08a295f2cbf443d5d87f680d6ba3335fba57daef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:29:40 +0200 Subject: [PATCH 0922/1047] build(deps): bump alpine from `7eccd4d` to `beefdbd` in /packaging (#3250) Bumps alpine from `7eccd4d` to `beefdbd`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index efddfd3d05..183cd71fc1 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:7eccd4d5d574a3c889b0a6d97b2cdd0308c8e1afc2bba8d467c2b87d879b0c1c +FROM --platform=linux/arm64 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index d805120dd0..af7aa9a356 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:7eccd4d5d574a3c889b0a6d97b2cdd0308c8e1afc2bba8d467c2b87d879b0c1c +FROM --platform=linux/arm/v7 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 05362a0b8d474441e65fb2fb425e170ca274be59 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 27 Sep 2024 21:29:07 +0200 Subject: [PATCH 0923/1047] packaging: add opencontainers image labels (#3251) Add [opencontainer image labels](https://github.com/opencontainers/image-spec/blob/main/annotations.md) (except org.opencontainers.image.created) to multiarch image. Decided not touch Dockerfile.arm* because it is better to unify them with Dockerfile (see e.g. #2566). Github action already adds opencontainer image labels: ```console docker inspect ghcr.io/zalando/skipper:v0.21.209 | jq .[0].Config.Labels { "maintainer": "Team Gateway&Proxy @ Zalando SE ", "org.opencontainers.image.created": "2024-09-26T09:40:09.452Z", "org.opencontainers.image.description": "An HTTP router and reverse proxy for service composition, including use cases like Kubernetes Ingress", "org.opencontainers.image.licenses": "NOASSERTION", "org.opencontainers.image.revision": "08a295f2cbf443d5d87f680d6ba3335fba57daef", "org.opencontainers.image.source": "https://github.com/zalando/skipper", "org.opencontainers.image.title": "skipper", "org.opencontainers.image.url": "https://github.com/zalando/skipper", "org.opencontainers.image.version": "v0.21.209" } ``` This change also fixes licenses and adds vendor label. Signed-off-by: Alexander Yastrebov --- .github/workflows/gh-packages.yaml | 3 +++ packaging/Dockerfile | 13 +++++++++++++ packaging/Makefile | 18 +++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 6fac0fb0f7..ad45e2e36e 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -68,6 +68,9 @@ jobs: tags: | type=semver,pattern=v{{version}} type=semver,pattern=v{{major}}.{{minor}} + labels: | + org.opencontainers.image.licenses=Apache-2.0 + org.opencontainers.image.vendor="Zalando SE" - name: Build and push uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 diff --git a/packaging/Dockerfile b/packaging/Dockerfile index 8f9918e87d..fa49806a2e 100644 --- a/packaging/Dockerfile +++ b/packaging/Dockerfile @@ -1,7 +1,20 @@ ARG BASE_IMAGE=default FROM registry.opensource.zalan.do/library/alpine-3:latest AS default FROM ${BASE_IMAGE} + +ARG VERSION +ARG REVISION + LABEL maintainer="Team Gateway&Proxy @ Zalando SE " +LABEL org.opencontainers.image.title=skipper +LABEL org.opencontainers.image.description="An HTTP router and reverse proxy for service composition, including use cases like Kubernetes Ingress" +LABEL org.opencontainers.image.version=$VERSION +LABEL org.opencontainers.image.url=https://github.com/zalando/skipper +LABEL org.opencontainers.image.licenses=Apache-2.0 +LABEL org.opencontainers.image.vendor="Zalando SE" +LABEL org.opencontainers.image.source=https://github.com/zalando/skipper +LABEL org.opencontainers.image.revision=$REVISION + RUN apk --no-cache add ca-certificates && update-ca-certificates RUN mkdir -p /usr/bin ARG BUILD_FOLDER=build diff --git a/packaging/Makefile b/packaging/Makefile index 91e30e423a..550d020bb0 100644 --- a/packaging/Makefile +++ b/packaging/Makefile @@ -13,6 +13,7 @@ GOARCH ?= amd64 GOARM ?= BUILD_FLAGS ?= -v -trimpath COMMIT_HASH = $(shell git rev-parse --short HEAD) +REVISION = $(shell git rev-parse HEAD) default: docker-build @@ -36,11 +37,19 @@ clean: rm -rf $(BINARIES) build/ docker.build: clean $(BINARIES) - docker build -t $(IMAGE) --build-arg BUILD_FOLDER=. --build-arg TARGETPLATFORM= . + docker build -t $(IMAGE) \ + --build-arg VERSION=$(VERSION) \ + --build-arg REVISION=$(REVISION) \ + --build-arg BUILD_FOLDER=. \ + --build-arg TARGETPLATFORM= \ + . docker-build: docker.build.amd64 docker.build.arm64 docker.build.armv7 docker.build.amd64: clean build.linux.amd64 docker.build.enable - docker buildx build -t $(IMAGE) --platform linux/amd64 -f Dockerfile --load . + docker buildx build -t $(IMAGE) --platform linux/amd64 \ + --build-arg VERSION=$(VERSION) \ + --build-arg REVISION=$(REVISION) \ + -f Dockerfile --load . docker.build.arm64: clean build.linux.arm64 docker.build.enable docker buildx build -t $(ARM64_IMAGE) --platform linux/arm64 -f Dockerfile.arm64 --load . docker.build.armv7: clean build.linux.armv7 docker.build.enable @@ -57,7 +66,10 @@ docker.push.armv7: docker.build.armv7 # build multi-arch container image using a trusted multi-arch base image docker.push.multiarch: clean build.linux docker.build.enable docker buildx build --rm -t $(MULTIARCH_IMAGE) --platform linux/amd64,linux/arm64 --push \ - --build-arg BASE_IMAGE=container-registry.zalando.net/library/alpine-3:latest . + --build-arg BASE_IMAGE=container-registry.zalando.net/library/alpine-3:latest \ + --build-arg VERSION=$(VERSION) \ + --build-arg REVISION=$(REVISION) \ + . # https://docs.docker.com/build/working-with-build/ # ~/.docker/config.json add: "experimental": "enabled", From 6c9130ea1ef09f80af6658a369ebb74b6f5ae1f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:31:09 +0200 Subject: [PATCH 0924/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3252) Bumps oss-fuzz-base/base-builder-go from `2347618` to `1f8967c`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 14f2130cf8..3431d529ff 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:2347618de7ba036a4822540f562636b6fb06c804be5381de24e2d3b2d5d70207 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:1f8967c219ee638e48ffb1e5193988a7c81c433e4c538073926ccec3e411ebc2 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From fd3366b00768b47283c93fe0d71eba435c9fa6bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:31:16 +0200 Subject: [PATCH 0925/1047] build(deps): bump amazonlinux from `03b3981` to `5fe11d1` in /fuzz (#3253) Bumps amazonlinux from `03b3981` to `5fe11d1`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 75f40f41c0..da1c1754ba 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:03b398175f863675186c1a8b38f45d702c5ff0c0faa3a3de9089a0fc58ed7269 +FROM amazonlinux:2023@sha256:5fe11d17e56571a26e20df3fa1493bdf9139fb9f2cb6d84fad13104c7b869217 WORKDIR /workspace From 5060558971d44e99c0dc490590804044167ec179 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:32:14 +0200 Subject: [PATCH 0926/1047] build(deps): bump docker/build-push-action from 6.7.0 to 6.8.0 (#3255) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.7.0 to 6.8.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/5cd11c3a4ced054e52742c5fd54dca954e0edd85...32945a339266b759abcbdc89316275140b0fc960) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ad45e2e36e..6aeaa16773 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor="Zalando SE" - name: Build and push - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 + uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From d0e936abd7f8b52f273e788355720978265e5e52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:32:28 +0200 Subject: [PATCH 0927/1047] build(deps): bump actions/checkout from 4.1.7 to 4.2.0 (#3254) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 4.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 591af73b1a..22b00debac 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 804b537592..0305947a1a 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 6aeaa16773..7360132c08 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 92b2a345b0..de10f275e1 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index c4de6de05b..a253d2be4e 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v3.1.0 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 2af6db590b..3713b17538 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From 1e9df04699ce5c0f4a99abbc0c7cfd4e20700d36 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 30 Sep 2024 11:48:13 +0200 Subject: [PATCH 0928/1047] github: fix org.opencontainers.image.vendor image label (#3256) Remove redundant quotes. Follow up on #3251 Signed-off-by: Alexander Yastrebov --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 7360132c08..812a05579f 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -70,7 +70,7 @@ jobs: type=semver,pattern=v{{major}}.{{minor}} labels: | org.opencontainers.image.licenses=Apache-2.0 - org.opencontainers.image.vendor="Zalando SE" + org.opencontainers.image.vendor=Zalando SE - name: Build and push uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 From 8f93ccd26c6af2bf56a87d9d39cce94b0ea60e45 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 1 Oct 2024 22:35:23 +0200 Subject: [PATCH 0929/1047] skipper: improve createDataClients errors (#3260) * add context message to returned errors * remove duplicate logging - errors will be logged by the caller (main) Signed-off-by: Alexander Yastrebov --- skipper.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/skipper.go b/skipper.go index bce6097b9d..7177373180 100644 --- a/skipper.go +++ b/skipper.go @@ -1038,8 +1038,7 @@ func createDataClients(o Options, cr *certregistry.CertRegistry) ([]routing.Data for _, rf := range strings.Split(o.RoutesFile, ",") { f, err := eskipfile.Open(rf) if err != nil { - log.Error("error while opening eskip file", err) - return nil, err + return nil, fmt.Errorf("error while opening eskip file: %w", err) } clients = append(clients, f) @@ -1060,8 +1059,7 @@ func createDataClients(o Options, cr *certregistry.CertRegistry) ([]routing.Data HTTPTimeout: o.SourcePollTimeout, }) if err != nil { - log.Errorf("error while loading routes from url %s: %s", url, err) - return nil, err + return nil, fmt.Errorf("error while loading routes from url %s: %w", url, err) } clients = append(clients, client) } @@ -1070,8 +1068,7 @@ func createDataClients(o Options, cr *certregistry.CertRegistry) ([]routing.Data if o.InlineRoutes != "" { ir, err := routestring.New(o.InlineRoutes) if err != nil { - log.Error("error while parsing inline routes", err) - return nil, err + return nil, fmt.Errorf("error while parsing inline routes: %w", err) } clients = append(clients, ir) @@ -1089,7 +1086,7 @@ func createDataClients(o Options, cr *certregistry.CertRegistry) ([]routing.Data }) if err != nil { - return nil, err + return nil, fmt.Errorf("error while creating etcd client: %w", err) } clients = append(clients, etcdClient) @@ -1101,7 +1098,7 @@ func createDataClients(o Options, cr *certregistry.CertRegistry) ([]routing.Data kubernetesClient, err := kubernetes.New(kops) if err != nil { - return nil, err + return nil, fmt.Errorf("error while creating kubernetes data client: %w", err) } clients = append(clients, kubernetesClient) } From e6f8a932f1f6fe1233241dbe3dcc9de24c83e4a3 Mon Sep 17 00:00:00 2001 From: Farasath Ahamed Date: Tue, 1 Oct 2024 22:54:31 +0200 Subject: [PATCH 0930/1047] Improvements to OPA filter unit test cases (#3259) - Fix test case that uses an invalid rego query in the test case - Include a test case to validate policy evaluation based on `input.attributes.request.http.path` (http path set in the envoy check request) Signed-off-by: Farasath Ahamed --- .../opaauthorizerequest_test.go | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 5256d663d8..89e8f3de50 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -88,6 +88,20 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Allow Request based on http path", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_with_http_path", + requestPath: "/some/api/path?q1=v1&msg=help%20me", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Allow Requests with query parameters", filterName: "opaAuthorizeRequest", @@ -120,10 +134,10 @@ func TestAuthorizeRequestFilter(t *testing.T) { msg: "Allow Requests with an empty query string", filterName: "opaAuthorizeRequest", bundleName: "somebundle.tar.gz", - regoQuery: "envoy/authz/allow_context_extensions", + regoQuery: "envoy/authz/allow_with_path_having_empty_query", requestPath: "/path-with-empty-query?", requestMethod: "GET", - contextExtensions: "com.mycompany.myprop: myvalue", + contextExtensions: "", expectedStatus: http.StatusOK, expectedBody: "Welcome!", expectedHeaders: make(http.Header), @@ -398,12 +412,16 @@ func TestAuthorizeRequestFilter(t *testing.T) { input.parsed_query = {} } + allow_with_http_path { + input.attributes.request.http.path == "/some/api/path?q1=v1&msg=help%20me" + } + allow { input.parsed_path = [ "my path" ] } - allow { - input.parsed_path = [ "/path-with-empty-query" ] + allow_with_path_having_empty_query { + input.parsed_path = [ "path-with-empty-query" ] input.parsed_query = {} } From dad8b02a3e5b52c9df9dbab3fc3ecd0eedcd1481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:41:15 +0200 Subject: [PATCH 0931/1047] build(deps): bump actions/cache from 4.0.2 to 4.1.0 (#3266) Bumps [actions/cache](https://github.com/actions/cache) from 4.0.2 to 4.1.0. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/0c45773b623bea8c8e75f6c82b208c3cf94ea4f9...2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 0305947a1a..fe63b29ec7 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x - - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + - uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 with: key: ${{ github.ref }} path: .cache From 261e3a8fe6c0333cdd328212eb6e3d2ff4918b1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:41:43 +0200 Subject: [PATCH 0932/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3263) Bumps oss-fuzz-base/base-builder-go from `1f8967c` to `5f8a362`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 3431d529ff..d8f4884a79 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:1f8967c219ee638e48ffb1e5193988a7c81c433e4c538073926ccec3e411ebc2 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:5f8a362c0f8972d0d3d73e7b56258b4f22152ddf54a0450428b05fec0a6dd2f8 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 7243dcd93e0f1a20dcdb20e3074f0b6adb40f910 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:42:25 +0200 Subject: [PATCH 0933/1047] build(deps): bump docker/build-push-action from 6.8.0 to 6.9.0 (#3264) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.8.0 to 6.9.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/32945a339266b759abcbdc89316275140b0fc960...4f58ea79222b3b9dc2c8bbdd6debcef730109a75) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 812a05579f..4498fa5f3f 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 0e80fe7db8cafc75d3a2a7f5cced58319a0e62ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:42:47 +0200 Subject: [PATCH 0934/1047] build(deps): bump docker/setup-buildx-action from 3.6.1 to 3.7.1 (#3265) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.6.1 to 3.7.1. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/988b5a0280414f521da01fcc63a27aeeb4b104db...c47758b77c9736f4b2ef4073d4d51994fabfe349) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 4498fa5f3f..f88d864707 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From 3d9c0207ca6572aaee5bd5ae2e20208eed1eb005 Mon Sep 17 00:00:00 2001 From: Magnus Jungsbluth Date: Mon, 7 Oct 2024 13:32:33 +0200 Subject: [PATCH 0935/1047] OPA filters: Reduce default buffer size for reading the requests' body (#3257) * OPA filters: Reduce default buffer size for reading the requests' body and expose it via command line and config Signed-off-by: Magnus Jungsbluth --- config/config.go | 31 ++++++++++--------- config/config_test.go | 1 + filters/openpolicyagent/openpolicyagent.go | 11 ++++--- .../openpolicyagent/openpolicyagent_test.go | 7 +++-- skipper.go | 16 +++++----- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/config/config.go b/config/config.go index 16b205fb7f..aaaf4f45c0 100644 --- a/config/config.go +++ b/config/config.go @@ -284,13 +284,14 @@ type Config struct { LuaModules *listFlag `yaml:"lua-modules"` LuaSources *listFlag `yaml:"lua-sources"` - EnableOpenPolicyAgent bool `yaml:"enable-open-policy-agent"` - OpenPolicyAgentConfigTemplate string `yaml:"open-policy-agent-config-template"` - OpenPolicyAgentEnvoyMetadata string `yaml:"open-policy-agent-envoy-metadata"` - OpenPolicyAgentCleanerInterval time.Duration `yaml:"open-policy-agent-cleaner-interval"` - OpenPolicyAgentStartupTimeout time.Duration `yaml:"open-policy-agent-startup-timeout"` - OpenPolicyAgentMaxRequestBodySize int64 `yaml:"open-policy-agent-max-request-body-size"` - OpenPolicyAgentMaxMemoryBodyParsing int64 `yaml:"open-policy-agent-max-memory-body-parsing"` + EnableOpenPolicyAgent bool `yaml:"enable-open-policy-agent"` + OpenPolicyAgentConfigTemplate string `yaml:"open-policy-agent-config-template"` + OpenPolicyAgentEnvoyMetadata string `yaml:"open-policy-agent-envoy-metadata"` + OpenPolicyAgentCleanerInterval time.Duration `yaml:"open-policy-agent-cleaner-interval"` + OpenPolicyAgentStartupTimeout time.Duration `yaml:"open-policy-agent-startup-timeout"` + OpenPolicyAgentRequestBodyBufferSize int64 `yaml:"open-policy-agent-request-body-buffer-size"` + OpenPolicyAgentMaxRequestBodySize int64 `yaml:"open-policy-agent-max-request-body-size"` + OpenPolicyAgentMaxMemoryBodyParsing int64 `yaml:"open-policy-agent-max-memory-body-parsing"` PassiveHealthCheck mapFlags `yaml:"passive-health-check"` } @@ -513,6 +514,7 @@ func NewConfig() *Config { flag.DurationVar(&cfg.OpenPolicyAgentCleanerInterval, "open-policy-agent-cleaner-interval", openpolicyagent.DefaultCleanIdlePeriod, "Duration in seconds to wait before cleaning up unused opa instances") flag.DurationVar(&cfg.OpenPolicyAgentStartupTimeout, "open-policy-agent-startup-timeout", openpolicyagent.DefaultOpaStartupTimeout, "Maximum duration in seconds to wait for the open policy agent to start up") flag.Int64Var(&cfg.OpenPolicyAgentMaxRequestBodySize, "open-policy-agent-max-request-body-size", openpolicyagent.DefaultMaxRequestBodySize, "Maximum number of bytes from a http request body that are passed as input to the policy") + flag.Int64Var(&cfg.OpenPolicyAgentRequestBodyBufferSize, "open-policy-agent-request-body-buffer-size", openpolicyagent.DefaultRequestBodyBufferSize, "Read buffer size for the request body") flag.Int64Var(&cfg.OpenPolicyAgentMaxMemoryBodyParsing, "open-policy-agent-max-memory-body-parsing", openpolicyagent.DefaultMaxMemoryBodyParsing, "Total number of bytes used to parse http request bodies across all requests. Once the limit is met, requests will be rejected.") // TLS client certs @@ -926,13 +928,14 @@ func (c *Config) ToOptions() skipper.Options { LuaModules: c.LuaModules.values, LuaSources: c.LuaSources.values, - EnableOpenPolicyAgent: c.EnableOpenPolicyAgent, - OpenPolicyAgentConfigTemplate: c.OpenPolicyAgentConfigTemplate, - OpenPolicyAgentEnvoyMetadata: c.OpenPolicyAgentEnvoyMetadata, - OpenPolicyAgentCleanerInterval: c.OpenPolicyAgentCleanerInterval, - OpenPolicyAgentStartupTimeout: c.OpenPolicyAgentStartupTimeout, - OpenPolicyAgentMaxRequestBodySize: c.OpenPolicyAgentMaxRequestBodySize, - OpenPolicyAgentMaxMemoryBodyParsing: c.OpenPolicyAgentMaxMemoryBodyParsing, + EnableOpenPolicyAgent: c.EnableOpenPolicyAgent, + OpenPolicyAgentConfigTemplate: c.OpenPolicyAgentConfigTemplate, + OpenPolicyAgentEnvoyMetadata: c.OpenPolicyAgentEnvoyMetadata, + OpenPolicyAgentCleanerInterval: c.OpenPolicyAgentCleanerInterval, + OpenPolicyAgentStartupTimeout: c.OpenPolicyAgentStartupTimeout, + OpenPolicyAgentMaxRequestBodySize: c.OpenPolicyAgentMaxRequestBodySize, + OpenPolicyAgentRequestBodyBufferSize: c.OpenPolicyAgentRequestBodyBufferSize, + OpenPolicyAgentMaxMemoryBodyParsing: c.OpenPolicyAgentMaxMemoryBodyParsing, PassiveHealthCheck: c.PassiveHealthCheck.values, } diff --git a/config/config_test.go b/config/config_test.go index f117546ea0..bad7f480ec 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -163,6 +163,7 @@ func defaultConfig(with func(*Config)) *Config { OpenPolicyAgentStartupTimeout: 30 * time.Second, OpenPolicyAgentMaxRequestBodySize: openpolicyagent.DefaultMaxRequestBodySize, OpenPolicyAgentMaxMemoryBodyParsing: openpolicyagent.DefaultMaxMemoryBodyParsing, + OpenPolicyAgentRequestBodyBufferSize: openpolicyagent.DefaultRequestBodyBufferSize, } with(cfg) return cfg diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 282f7613bb..1024208ec4 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -5,7 +5,6 @@ import ( "context" "errors" "fmt" - "google.golang.org/protobuf/proto" "io" "net/http" "os" @@ -14,6 +13,8 @@ import ( "text/template" "time" + "google.golang.org/protobuf/proto" + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" "github.com/google/uuid" "github.com/open-policy-agent/opa/ast" @@ -44,9 +45,9 @@ const ( defaultShutdownGracePeriod = 30 * time.Second DefaultOpaStartupTimeout = 30 * time.Second - DefaultMaxRequestBodySize = 1 << 20 // 1 MB - DefaultMaxMemoryBodyParsing = 100 * DefaultMaxRequestBodySize - defaultBodyBufferSize = 8192 * 1024 + DefaultMaxRequestBodySize = 1 << 20 // 1 MB + DefaultMaxMemoryBodyParsing = 100 * DefaultMaxRequestBodySize + DefaultRequestBodyBufferSize = 8 * 1024 // 8 KB spanNameEval = "open-policy-agent" ) @@ -129,7 +130,7 @@ func NewOpenPolicyAgentRegistry(opts ...func(*OpenPolicyAgentRegistry) error) *O lastused: make(map[*OpenPolicyAgentInstance]time.Time), quit: make(chan struct{}), maxRequestBodyBytes: DefaultMaxMemoryBodyParsing, - bodyReadBufferSize: defaultBodyBufferSize, + bodyReadBufferSize: DefaultRequestBodyBufferSize, } for _, opt := range opts { diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go index 6e15c859eb..6c6dc7d923 100644 --- a/filters/openpolicyagent/openpolicyagent_test.go +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "fmt" - pbstruct "google.golang.org/protobuf/types/known/structpb" "io" "net/http" "os" @@ -13,6 +12,8 @@ import ( "testing" "time" + pbstruct "google.golang.org/protobuf/types/known/structpb" + "github.com/open-policy-agent/opa/ast" ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" @@ -250,7 +251,7 @@ func TestOpaEngineStartFailureWithTimeout(t *testing.T) { cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config), WithStartupTimeout(1*time.Second)) assert.NoError(t, err) - engine, err := registry.new(inmem.New(), config, *cfg, "testfilter", "test", DefaultMaxRequestBodySize, defaultBodyBufferSize) + engine, err := registry.new(inmem.New(), config, *cfg, "testfilter", "test", DefaultMaxRequestBodySize, DefaultRequestBodyBufferSize) assert.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), cfg.startupTimeout) @@ -533,7 +534,7 @@ func TestBodyExtraction(t *testing.T) { msg: "Read body ", body: `{ "welcome": "world" }`, maxBodySize: 1024, - readBodyBuffer: defaultBodyBufferSize, + readBodyBuffer: DefaultRequestBodyBufferSize, bodyInPolicy: `{ "welcome": "world" }`, }, { diff --git a/skipper.go b/skipper.go index 7177373180..e0a3fc8f31 100644 --- a/skipper.go +++ b/skipper.go @@ -933,13 +933,14 @@ type Options struct { // filters. LuaSources []string - EnableOpenPolicyAgent bool - OpenPolicyAgentConfigTemplate string - OpenPolicyAgentEnvoyMetadata string - OpenPolicyAgentCleanerInterval time.Duration - OpenPolicyAgentStartupTimeout time.Duration - OpenPolicyAgentMaxRequestBodySize int64 - OpenPolicyAgentMaxMemoryBodyParsing int64 + EnableOpenPolicyAgent bool + OpenPolicyAgentConfigTemplate string + OpenPolicyAgentEnvoyMetadata string + OpenPolicyAgentCleanerInterval time.Duration + OpenPolicyAgentStartupTimeout time.Duration + OpenPolicyAgentMaxRequestBodySize int64 + OpenPolicyAgentRequestBodyBufferSize int64 + OpenPolicyAgentMaxMemoryBodyParsing int64 PassiveHealthCheck map[string]string } @@ -1877,6 +1878,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { opaRegistry = openpolicyagent.NewOpenPolicyAgentRegistry( openpolicyagent.WithMaxRequestBodyBytes(o.OpenPolicyAgentMaxRequestBodySize), openpolicyagent.WithMaxMemoryBodyParsing(o.OpenPolicyAgentMaxMemoryBodyParsing), + openpolicyagent.WithReadBodyBufferSize(o.OpenPolicyAgentRequestBodyBufferSize), openpolicyagent.WithCleanInterval(o.OpenPolicyAgentCleanerInterval), openpolicyagent.WithTracer(tracer)) defer opaRegistry.Close() From 385d1790a9f98524de616ff0a4b68ee234117ee8 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 11 Oct 2024 16:50:16 +0200 Subject: [PATCH 0936/1047] dataclients/kubernetes: add resource URI to error message (#3268) This is helpful for debugging k8s permissions. Signed-off-by: Alexander Yastrebov --- dataclients/kubernetes/clusterclient.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index 451109e060..dbd64e00a7 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -278,7 +278,7 @@ func (c *clusterClient) getJSON(uri string, a interface{}) error { if rsp.StatusCode != http.StatusOK { log.Tracef("request failed, status: %d, %s", rsp.StatusCode, rsp.Status) - return fmt.Errorf("request failed, status: %d, %s", rsp.StatusCode, rsp.Status) + return fmt.Errorf("request to %s failed, status: %d, %s", uri, rsp.StatusCode, rsp.Status) } b := bytes.NewBuffer(nil) From 45a8915a961f2966c15edd7f03dcaf8e140ffd23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:50:38 +0200 Subject: [PATCH 0937/1047] build(deps): bump actions/cache from 4.1.0 to 4.1.1 (#3273) Bumps [actions/cache](https://github.com/actions/cache) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2...3624ceb22c1c5a301c8db4169662070a689d9ea8) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index fe63b29ec7..f93e550719 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x - - uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 + - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 with: key: ${{ github.ref }} path: .cache From c00ed4cfa5c9655a5eb37db58521c1b1c59e4bb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:51:16 +0200 Subject: [PATCH 0938/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3275) Bumps oss-fuzz-base/base-builder-go from `5f8a362` to `b365850`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index d8f4884a79..ccfdc49864 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:5f8a362c0f8972d0d3d73e7b56258b4f22152ddf54a0450428b05fec0a6dd2f8 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:b365850884c556d66a55ffa5d56c198079ff28afef714cf47cce07c4841bbd42 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 1222c6e3a5e936df8ef66666a0f0c7595f907c4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:51:55 +0200 Subject: [PATCH 0939/1047] build(deps): bump amazonlinux from `5fe11d1` to `019cf20` in /fuzz (#3269) Bumps amazonlinux from `5fe11d1` to `019cf20`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index da1c1754ba..4a007aaa83 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:5fe11d17e56571a26e20df3fa1493bdf9139fb9f2cb6d84fad13104c7b869217 +FROM amazonlinux:2023@sha256:019cf20c5b98efcd548952d31df1768377916bf8d57d70d979c6c92bfd5e7446 WORKDIR /workspace From 2f115f5a12e4e56664e42d980a196f7ada92b301 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:52:34 +0200 Subject: [PATCH 0940/1047] build(deps): bump jandelgado/gcov2lcov-action from 1.0.9 to 1.1.0 (#3270) Bumps [jandelgado/gcov2lcov-action](https://github.com/jandelgado/gcov2lcov-action) from 1.0.9 to 1.1.0. - [Release notes](https://github.com/jandelgado/gcov2lcov-action/releases) - [Changelog](https://github.com/jandelgado/gcov2lcov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/jandelgado/gcov2lcov-action/compare/c680c0f7c7442485f1749eb2a13e54a686e76eb5...69ef3d59a24cc6e062516a73d8be123e85b15cc0) --- updated-dependencies: - dependency-name: jandelgado/gcov2lcov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index de10f275e1..ba39056405 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -29,7 +29,7 @@ jobs: - run: make capslock - run: make coverprofile - name: Convert coverage to lcov - uses: jandelgado/gcov2lcov-action@c680c0f7c7442485f1749eb2a13e54a686e76eb5 + uses: jandelgado/gcov2lcov-action@69ef3d59a24cc6e062516a73d8be123e85b15cc0 - name: Coveralls uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 with: From abb8569e70f7eaf2c68b91a26c7f41f6e1427eb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:53:57 +0200 Subject: [PATCH 0941/1047] build(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3 (#3272) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/50769540e7f4bd5e21e526ee35c689e35e0d6874...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index a253d2be4e..6ee3caea09 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: SARIF file path: results.sarif From 172348e77c4dcca5077872730f37399b1490785c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:54:50 +0200 Subject: [PATCH 0942/1047] build(deps): bump actions/checkout from 4.2.0 to 4.2.1 (#3274) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/d632683dd7b4114ad314bca15554477dd762a938...eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 22b00debac..2fbaadfba7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index f93e550719..c2a61fc764 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index f88d864707..32829c5059 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index ba39056405..a4033e7bde 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 6ee3caea09..3bdd644b1e 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v3.1.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 3713b17538..f5d2829f58 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From 1b4b2046088bc31d2fbbbcf776a2b4ccc9056022 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:29:17 +0200 Subject: [PATCH 0943/1047] build(deps): bump coverallsapp/github-action from 2.3.0 to 2.3.1 (#3271) Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/coverallsapp/github-action/releases) - [Commits](https://github.com/coverallsapp/github-action/compare/643bc377ffa44ace6394b2b5d0d3950076de9f63...1134c89e4bf10443bf8f0ec69640b667cfd91041) --- updated-dependencies: - dependency-name: coverallsapp/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index a4033e7bde..a7f8e15da9 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -31,7 +31,7 @@ jobs: - name: Convert coverage to lcov uses: jandelgado/gcov2lcov-action@69ef3d59a24cc6e062516a73d8be123e85b15cc0 - name: Coveralls - uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 + uses: coverallsapp/github-action@1134c89e4bf10443bf8f0ec69640b667cfd91041 with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage.lcov From 0bc65903047382bf27ce3c334322921a654e43bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:34:08 +0200 Subject: [PATCH 0944/1047] build(deps): bump coverallsapp/github-action from 2.3.1 to 2.3.3 (#3283) Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.1 to 2.3.3. - [Release notes](https://github.com/coverallsapp/github-action/releases) - [Commits](https://github.com/coverallsapp/github-action/compare/1134c89e4bf10443bf8f0ec69640b667cfd91041...4cdef0b2dbe0c9aa26bed48edb868db738625e79) --- updated-dependencies: - dependency-name: coverallsapp/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index a7f8e15da9..e3056de18d 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -31,7 +31,7 @@ jobs: - name: Convert coverage to lcov uses: jandelgado/gcov2lcov-action@69ef3d59a24cc6e062516a73d8be123e85b15cc0 - name: Coveralls - uses: coverallsapp/github-action@1134c89e4bf10443bf8f0ec69640b667cfd91041 + uses: coverallsapp/github-action@4cdef0b2dbe0c9aa26bed48edb868db738625e79 with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage.lcov From bdf2b92a6b886dd28cc169f4af9d8884d4d17491 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:34:18 +0200 Subject: [PATCH 0945/1047] build(deps): bump jandelgado/gcov2lcov-action from 1.1.0 to 1.1.1 (#3284) Bumps [jandelgado/gcov2lcov-action](https://github.com/jandelgado/gcov2lcov-action) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/jandelgado/gcov2lcov-action/releases) - [Changelog](https://github.com/jandelgado/gcov2lcov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/jandelgado/gcov2lcov-action/compare/69ef3d59a24cc6e062516a73d8be123e85b15cc0...4e1989767862652e6ca8d3e2e61aabe6d43be28b) --- updated-dependencies: - dependency-name: jandelgado/gcov2lcov-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index e3056de18d..e032963146 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -29,7 +29,7 @@ jobs: - run: make capslock - run: make coverprofile - name: Convert coverage to lcov - uses: jandelgado/gcov2lcov-action@69ef3d59a24cc6e062516a73d8be123e85b15cc0 + uses: jandelgado/gcov2lcov-action@4e1989767862652e6ca8d3e2e61aabe6d43be28b - name: Coveralls uses: coverallsapp/github-action@4cdef0b2dbe0c9aa26bed48edb868db738625e79 with: From 03c4af4482e01c25ce805a5dc5bb1deba5f2fc14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:34:31 +0200 Subject: [PATCH 0946/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3285) Bumps oss-fuzz-base/base-builder-go from `b365850` to `115dcf2`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index ccfdc49864..2563863061 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:b365850884c556d66a55ffa5d56c198079ff28afef714cf47cce07c4841bbd42 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:115dcf2d907a62cc2723b323e1f821b28e6e6bb5465b3065d7ce05f55fa2aaac COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 262c326dd25c5725fe24052be85784c9ec8c0852 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 21 Oct 2024 18:23:24 +0200 Subject: [PATCH 0947/1047] skipper: add TLS client authentication config (#3281) Add an option and a flag to configure TLS Client Authentication policy of the Server. Fixes https://github.com/zalando/skipper/issues/3280 Signed-off-by: Alexander Yastrebov --- config/config.go | 22 +++++++++++++++++++++- skipper.go | 5 +++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index aaaf4f45c0..ca22edfe35 100644 --- a/config/config.go +++ b/config/config.go @@ -217,7 +217,8 @@ type Config struct { Certificates []tls.Certificate `yaml:"-"` // TLS version - TLSMinVersion string `yaml:"tls-min-version"` + TLSMinVersion string `yaml:"tls-min-version"` + TLSClientAuth tls.ClientAuthType `yaml:"tls-client-auth"` // Exclude insecure cipher suites ExcludeInsecureCipherSuites bool `yaml:"exclude-insecure-cipher-suites"` @@ -523,6 +524,9 @@ func NewConfig() *Config { // TLS version flag.StringVar(&cfg.TLSMinVersion, "tls-min-version", defaultMinTLSVersion, "minimal TLS Version to be used in server, proxy and client connections") + flag.Func("tls-client-auth", "TLS client authentication policy for server, one of: "+ + "NoClientCert, RequestClientCert, RequireAnyClientCert, VerifyClientCertIfGiven or RequireAndVerifyClientCert. "+ + "See https://pkg.go.dev/crypto/tls#ClientAuthType for details.", cfg.setTLSClientAuth) // Exclude insecure cipher suites flag.BoolVar(&cfg.ExcludeInsecureCipherSuites, "exclude-insecure-cipher-suites", false, "excludes insecure cipher suites") @@ -727,6 +731,7 @@ func (c *Config) ToOptions() skipper.Options { DebugListener: c.DebugListener, CertPathTLS: c.CertPathTLS, KeyPathTLS: c.KeyPathTLS, + TLSClientAuth: c.TLSClientAuth, CipherSuites: c.filterCipherSuites(), MaxLoopbacks: c.MaxLoopbacks, DefaultHTTPStatus: c.DefaultHTTPStatus, @@ -1047,6 +1052,21 @@ func (c *Config) getMinTLSVersion() uint16 { return tlsVersionTable[defaultMinTLSVersion] } +func (c *Config) setTLSClientAuth(s string) error { + var ok bool + c.TLSClientAuth, ok = map[string]tls.ClientAuthType{ + "NoClientCert": tls.NoClientCert, + "RequestClientCert": tls.RequestClientCert, + "RequireAnyClientCert": tls.RequireAnyClientCert, + "VerifyClientCertIfGiven": tls.VerifyClientCertIfGiven, + "RequireAndVerifyClientCert": tls.RequireAndVerifyClientCert, + }[s] + if !ok { + return fmt.Errorf("unsupported TLS client authentication type") + } + return nil +} + func (c *Config) filterCipherSuites() []uint16 { if !c.ExcludeInsecureCipherSuites { return nil diff --git a/skipper.go b/skipper.go index e0a3fc8f31..19e24f027f 100644 --- a/skipper.go +++ b/skipper.go @@ -617,6 +617,10 @@ type Options struct { // multiple keys, the order must match the one given in CertPathTLS KeyPathTLS string + // TLSClientAuth sets the policy the server will follow for + // TLS Client Authentication, see [tls.ClientAuthType] + TLSClientAuth tls.ClientAuthType + // TLS Settings for Proxy Server ProxyTLS *tls.Config @@ -1198,6 +1202,7 @@ func (o *Options) tlsConfig(cr *certregistry.CertRegistry) (*tls.Config, error) config := &tls.Config{ MinVersion: o.TLSMinVersion, + ClientAuth: o.TLSClientAuth, } if o.CipherSuites != nil { From 2941626b7d299c563e7bee510fc14d3c1bdf16f8 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 29 Oct 2024 14:46:35 +0100 Subject: [PATCH 0948/1047] skipper: set server minimal TLS version (#3282) The tls-min-version flag specifies "minimal TLS Version to be used in server, proxy and client connections" but was not used to configure Server TLS. Follow up on: #1656 Signed-off-by: Alexander Yastrebov --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index ca22edfe35..0ed251200c 100644 --- a/config/config.go +++ b/config/config.go @@ -732,6 +732,7 @@ func (c *Config) ToOptions() skipper.Options { CertPathTLS: c.CertPathTLS, KeyPathTLS: c.KeyPathTLS, TLSClientAuth: c.TLSClientAuth, + TLSMinVersion: c.getMinTLSVersion(), CipherSuites: c.filterCipherSuites(), MaxLoopbacks: c.MaxLoopbacks, DefaultHTTPStatus: c.DefaultHTTPStatus, From b85a9a31c23121dd234c2594c782429cd9911908 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:27:18 +0100 Subject: [PATCH 0949/1047] build(deps): bump amazonlinux from `019cf20` to `5bf4cf4` in /fuzz (#3294) Bumps amazonlinux from `019cf20` to `5bf4cf4`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 4a007aaa83..d0d5c46890 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:019cf20c5b98efcd548952d31df1768377916bf8d57d70d979c6c92bfd5e7446 +FROM amazonlinux:2023@sha256:5bf4cf420ef7e50835911993c6a2ddb0e8f5101c0ef89ca20e9d02a03c8c3a8c WORKDIR /workspace From 6f0526d4cbfbb2a408bede8370fabc7ab7678c59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:27:44 +0100 Subject: [PATCH 0950/1047] build(deps): bump actions/checkout from 4.2.1 to 4.2.2 (#3293) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871...11bd71901bbe5b1630ceea73d27597364c9af683) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/openssf-scorecard.yaml | 2 +- .github/workflows/pr.yaml | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2fbaadfba7..c81f9103eb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index c2a61fc764..1f47ef1274 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -16,7 +16,7 @@ jobs: contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 32829c5059..f30be3e696 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -23,7 +23,7 @@ jobs: packages: write # to push packages steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index e032963146..5efe11adaa 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 3bdd644b1e..8a00e1aa85 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v3.1.0 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v3.1.0 with: persist-credentials: false diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index f5d2829f58..a7feffc336 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -12,7 +12,7 @@ jobs: image: returntocorp/semgrep steps: # Retrieve the source code for the repository - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # Fetch the semgrep rules - run: git clone https://github.com/dgryski/semgrep-go.git # Run the rule checker using the fetched rules @@ -21,7 +21,7 @@ jobs: check-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 @@ -33,7 +33,7 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 From f46367f63537c46ba03b86fae9dbd4aa38f2af07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:28:08 +0100 Subject: [PATCH 0951/1047] build(deps): bump actions/cache from 4.1.1 to 4.1.2 (#3292) Bumps [actions/cache](https://github.com/actions/cache) from 4.1.1 to 4.1.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/3624ceb22c1c5a301c8db4169662070a689d9ea8...6849a6489940f00c2f30c0fb92c6274307ccb58a) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 1f47ef1274..0f143e79af 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 with: python-version: 3.x - - uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 + - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a with: key: ${{ github.ref }} path: .cache From a31701079e340fa25b6803660d5be0434e0bf903 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:28:42 +0100 Subject: [PATCH 0952/1047] build(deps): bump coverallsapp/github-action from 2.3.3 to 2.3.4 (#3291) Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.3 to 2.3.4. - [Release notes](https://github.com/coverallsapp/github-action/releases) - [Commits](https://github.com/coverallsapp/github-action/compare/4cdef0b2dbe0c9aa26bed48edb868db738625e79...cfd0633edbd2411b532b808ba7a8b5e04f76d2c8) --- updated-dependencies: - dependency-name: coverallsapp/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 5efe11adaa..4262a6efe4 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -31,7 +31,7 @@ jobs: - name: Convert coverage to lcov uses: jandelgado/gcov2lcov-action@4e1989767862652e6ca8d3e2e61aabe6d43be28b - name: Coveralls - uses: coverallsapp/github-action@4cdef0b2dbe0c9aa26bed48edb868db738625e79 + uses: coverallsapp/github-action@cfd0633edbd2411b532b808ba7a8b5e04f76d2c8 with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage.lcov From 1a98f01d646302f492e066d60fd514bc80bb06e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:29:15 +0100 Subject: [PATCH 0953/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3288) Bumps oss-fuzz-base/base-builder-go from `115dcf2` to `fd6832a`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 2563863061..876b2075c2 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:115dcf2d907a62cc2723b323e1f821b28e6e6bb5465b3065d7ce05f55fa2aaac +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:fd6832a7a2b20111b2b792ac71184e9684c84652590f136141072a982e0fc26f COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 47c0e4b7f3ffa9d051d6622b8532f36b886a9b4d Mon Sep 17 00:00:00 2001 From: samihda Date: Mon, 4 Nov 2024 17:26:32 +0100 Subject: [PATCH 0954/1047] docs: Fix typo (#3300) --- docs/kubernetes/ingress-controller.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/kubernetes/ingress-controller.md b/docs/kubernetes/ingress-controller.md index 0754f11f7d..b74d833b5c 100644 --- a/docs/kubernetes/ingress-controller.md +++ b/docs/kubernetes/ingress-controller.md @@ -276,7 +276,7 @@ Prerequisites: manifests: `git clone https://github.com/zalando/skipper.git` 1. You should enter the cloned directory: `cd skipper` 1. You have to choose how to install skipper-ingress. You can install -it as [dameonset](#dameonset) or as [deployment](#deployment). +it as [daemonset](#daemonset) or as [deployment](#deployment). Beware, in order to get traffic from the internet, we would need to have a load balancer in front to direct all traffic to skipper. Skipper @@ -290,7 +290,7 @@ definition. ### Deployment style -Follow the deployment style you like: [dameonset](#dameonset) or [deployment](#deployment). +Follow the deployment style you like: [daemonset](#daemonset) or [deployment](#deployment). #### Daemonset From 715f02786a225c17794f212cddf9c943ed90f787 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:26:00 +0100 Subject: [PATCH 0955/1047] build(deps): bump github.com/golang-jwt/jwt/v4 in the go_modules group (#3301) Bumps the go_modules group with 1 update: [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt). Updates `github.com/golang-jwt/jwt/v4` from 4.5.0 to 4.5.1 - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.0...v4.5.1) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v4 dependency-type: direct:production dependency-group: go_modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ecaba54f5d..728ccb10c8 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/docker/go-connections v0.5.0 github.com/envoyproxy/go-control-plane v0.13.0 github.com/ghodss/yaml v1.0.0 - github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 diff --git a/go.sum b/go.sum index 9356ad0578..5836018b9f 100644 --- a/go.sum +++ b/go.sum @@ -179,8 +179,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -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-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= From f6f9efb2c7ff93ff940b6f5d60fda3e89e89cfec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:30:12 +0100 Subject: [PATCH 0956/1047] build(deps): bump actions/setup-python from 5.2.0 to 5.3.0 (#3290) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/f677139bbe7f9c59b41e40162b753c062f5d49a3...0b93645e9fea7318ecaed2b359559ac225c90a2b) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 0f143e79af..f73721d257 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b with: python-version: 3.x - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a From 8c6bc0af67ab211ff759c1420ead92cf94e07418 Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Thu, 5 Dec 2024 09:48:33 +0100 Subject: [PATCH 0957/1047] dataclients/kubernetes: append predicates to routes of Ingress/RouteGroup having specific annotation (#3328) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main purpose of this change is to allow skipper operators to dynamically add predicates to routes based on annotations key-value pairs in Kubernetes resources (metadata.annotation). This provides more flexibility in routing configuration without modifying the core Ingress or RouteGroup definitions. Key changes: - Added KubernetesAnnotationPredicates option & `-kubernetes-annotation-predicates` flag for public hostnames, which can be specified multiple times. - Added KubernetesEastWestRangeAnnotationPredicates option & `-kubernetes-east-west-range-annotation-predicates` flag for internal hostnames, which can be specified multiple times. - Modified Ingress and RouteGroup clients to apply annotation predicates depending on the host - Added test cases for new functionality Run & test with ```sh ➜ ./bin/skipper -kubernetes -kubernetes-annotation-predicates="zalando.org/traffic-disabled=true=False()" -kubernetes-east-west-range-annotation-predicates="zalando.org/traffic-disabled=true=False()" ➜ #or ➜ ./bin/routesrv -kubernetes -kubernetes-annotation-predicates="zalando.org/traffic-disabled=true=False()" -kubernetes-east-west-range-annotation-predicates="zalando.org/traffic-disabled=true=False()" ``` Having both flags with same value will append configured predicates to all routes of Ingress/RouteGroup resources annotated with `zalando.org/traffic-disabled: true`, routes defined in: * The `spec.rules` section of Ingress resources. * Routes specified using the `zalando.org/skipper-routes` annotation of Ingress resources. * The `spec.routes` section of RouteGroup resources. Signed-off-by: Mustafa Abdelrahman --- config/config.go | 168 ++++++++++++------ config/config_test.go | 119 +++++++++++++ dataclients/kubernetes/annotations.go | 21 +++ dataclients/kubernetes/hosts.go | 9 + dataclients/kubernetes/ingress.go | 63 ++++--- dataclients/kubernetes/ingress_test.go | 1 + dataclients/kubernetes/ingressv1.go | 15 +- dataclients/kubernetes/kube.go | 7 + .../kubernetes/kubernetestest/fixtures.go | 46 ++--- dataclients/kubernetes/routegroup.go | 7 + dataclients/kubernetes/routegroups_test.go | 4 + ...nnotation-predicates-with-ew-enabled.eskip | 43 +++++ ...annotation-predicates-with-ew-enabled.kube | 11 ++ ...annotation-predicates-with-ew-enabled.yaml | 65 +++++++ ...es-with-internal-hostname-predicates.eskip | 100 +++++++++++ ...tes-with-internal-hostname-predicates.kube | 21 +++ ...tes-with-internal-hostname-predicates.yaml | 155 ++++++++++++++++ ...on-predicates-with-internal-hostname.eskip | 23 +++ ...ion-predicates-with-internal-hostname.kube | 13 ++ ...ion-predicates-with-internal-hostname.yaml | 65 +++++++ .../annotation-predicates.eskip | 52 ++++++ .../annotation-predicates.kube | 26 +++ .../annotation-predicates.yaml | 163 +++++++++++++++++ ...nnotation-predicates-with-ew-enabled.eskip | 38 ++++ ...annotation-predicates-with-ew-enabled.kube | 9 + ...annotation-predicates-with-ew-enabled.yaml | 57 ++++++ ...icates-with-internal-host-predicates.eskip | 115 ++++++++++++ ...dicates-with-internal-host-predicates.kube | 19 ++ ...dicates-with-internal-host-predicates.yaml | 132 ++++++++++++++ ...tation-predicates-with-internal-host.eskip | 27 +++ ...otation-predicates-with-internal-host.kube | 11 ++ ...otation-predicates-with-internal-host.yaml | 57 ++++++ .../annotation-predicates.eskip | 36 ++++ .../annotation-predicates.kube | 16 ++ .../annotation-predicates.yaml | 113 ++++++++++++ skipper.go | 71 ++++---- 36 files changed, 1759 insertions(+), 139 deletions(-) create mode 100644 dataclients/kubernetes/annotations.go create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.yaml create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.yaml create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.kube create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.yaml create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.kube create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.yaml create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.kube create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.yaml create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.kube create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.yaml diff --git a/config/config.go b/config/config.go index 0ed251200c..e945af39f5 100644 --- a/config/config.go +++ b/config/config.go @@ -149,34 +149,38 @@ type Config struct { RefusePayload multiFlag `yaml:"refuse-payload"` // Kubernetes: - KubernetesIngress bool `yaml:"kubernetes"` - KubernetesInCluster bool `yaml:"kubernetes-in-cluster"` - KubernetesURL string `yaml:"kubernetes-url"` - KubernetesTokenFile string `yaml:"kubernetes-token-file"` - KubernetesHealthcheck bool `yaml:"kubernetes-healthcheck"` - KubernetesHTTPSRedirect bool `yaml:"kubernetes-https-redirect"` - KubernetesHTTPSRedirectCode int `yaml:"kubernetes-https-redirect-code"` - KubernetesDisableCatchAllRoutes bool `yaml:"kubernetes-disable-catchall-routes"` - KubernetesIngressClass string `yaml:"kubernetes-ingress-class"` - KubernetesRouteGroupClass string `yaml:"kubernetes-routegroup-class"` - WhitelistedHealthCheckCIDR string `yaml:"whitelisted-healthcheck-cidr"` - KubernetesPathModeString string `yaml:"kubernetes-path-mode"` - KubernetesPathMode kubernetes.PathMode `yaml:"-"` - KubernetesNamespace string `yaml:"kubernetes-namespace"` - KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` - KubernetesEnableEastWest bool `yaml:"enable-kubernetes-east-west"` - KubernetesEastWestDomain string `yaml:"kubernetes-east-west-domain"` - KubernetesEastWestRangeDomains *listFlag `yaml:"kubernetes-east-west-range-domains"` - KubernetesEastWestRangePredicatesString string `yaml:"kubernetes-east-west-range-predicates"` - KubernetesEastWestRangePredicates []*eskip.Predicate `yaml:"-"` - KubernetesOnlyAllowedExternalNames bool `yaml:"kubernetes-only-allowed-external-names"` - KubernetesAllowedExternalNames regexpListFlag `yaml:"kubernetes-allowed-external-names"` - KubernetesRedisServiceNamespace string `yaml:"kubernetes-redis-service-namespace"` - KubernetesRedisServiceName string `yaml:"kubernetes-redis-service-name"` - KubernetesRedisServicePort int `yaml:"kubernetes-redis-service-port"` - KubernetesBackendTrafficAlgorithmString string `yaml:"kubernetes-backend-traffic-algorithm"` - KubernetesBackendTrafficAlgorithm kubernetes.BackendTrafficAlgorithm `yaml:"-"` - KubernetesDefaultLoadBalancerAlgorithm string `yaml:"kubernetes-default-lb-algorithm"` + KubernetesIngress bool `yaml:"kubernetes"` + KubernetesInCluster bool `yaml:"kubernetes-in-cluster"` + KubernetesURL string `yaml:"kubernetes-url"` + KubernetesTokenFile string `yaml:"kubernetes-token-file"` + KubernetesHealthcheck bool `yaml:"kubernetes-healthcheck"` + KubernetesHTTPSRedirect bool `yaml:"kubernetes-https-redirect"` + KubernetesHTTPSRedirectCode int `yaml:"kubernetes-https-redirect-code"` + KubernetesDisableCatchAllRoutes bool `yaml:"kubernetes-disable-catchall-routes"` + KubernetesIngressClass string `yaml:"kubernetes-ingress-class"` + KubernetesRouteGroupClass string `yaml:"kubernetes-routegroup-class"` + WhitelistedHealthCheckCIDR string `yaml:"whitelisted-healthcheck-cidr"` + KubernetesPathModeString string `yaml:"kubernetes-path-mode"` + KubernetesPathMode kubernetes.PathMode `yaml:"-"` + KubernetesNamespace string `yaml:"kubernetes-namespace"` + KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` + KubernetesEnableEastWest bool `yaml:"enable-kubernetes-east-west"` + KubernetesEastWestDomain string `yaml:"kubernetes-east-west-domain"` + KubernetesEastWestRangeDomains *listFlag `yaml:"kubernetes-east-west-range-domains"` + KubernetesEastWestRangePredicatesString string `yaml:"kubernetes-east-west-range-predicates"` + KubernetesEastWestRangeAnnotationPredicatesString multiFlag `yaml:"kubernetes-east-west-range-annotation-predicates"` + KubernetesAnnotationPredicatesString multiFlag `yaml:"kubernetes-annotation-predicates"` + KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"-"` + KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"-"` + KubernetesEastWestRangePredicates []*eskip.Predicate `yaml:"-"` + KubernetesOnlyAllowedExternalNames bool `yaml:"kubernetes-only-allowed-external-names"` + KubernetesAllowedExternalNames regexpListFlag `yaml:"kubernetes-allowed-external-names"` + KubernetesRedisServiceNamespace string `yaml:"kubernetes-redis-service-namespace"` + KubernetesRedisServiceName string `yaml:"kubernetes-redis-service-name"` + KubernetesRedisServicePort int `yaml:"kubernetes-redis-service-port"` + KubernetesBackendTrafficAlgorithmString string `yaml:"kubernetes-backend-traffic-algorithm"` + KubernetesBackendTrafficAlgorithm kubernetes.BackendTrafficAlgorithm `yaml:"-"` + KubernetesDefaultLoadBalancerAlgorithm string `yaml:"kubernetes-default-lb-algorithm"` // Default filters DefaultFiltersDir string `yaml:"default-filters-dir"` @@ -470,6 +474,8 @@ func NewConfig() *Config { flag.StringVar(&cfg.KubernetesEastWestDomain, "kubernetes-east-west-domain", "", "*Deprecated*: use kubernetes-east-west-range feature. Sets the east-west domain, defaults to .skipper.cluster.local") flag.Var(cfg.KubernetesEastWestRangeDomains, "kubernetes-east-west-range-domains", "set the the cluster internal domains for east west traffic. Identified routes to such domains will include the -kubernetes-east-west-range-predicates") flag.StringVar(&cfg.KubernetesEastWestRangePredicatesString, "kubernetes-east-west-range-predicates", "", "set the predicates that will be appended to routes identified as to -kubernetes-east-west-range-domains") + flag.Var(&cfg.KubernetesAnnotationPredicatesString, "kubernetes-annotation-predicates", "configures predicates appended to non east-west routes of annotated resources. E.g. -kubernetes-annotation-predicates='zone-a=true=Foo() && Bar()' will add 'Foo() && Bar()' predicates to all non east-west routes of ingress or routegroup annotated with 'zone-a: true'. For east-west routes use -kubernetes-east-west-range-annotation-predicates.") + flag.Var(&cfg.KubernetesEastWestRangeAnnotationPredicatesString, "kubernetes-east-west-range-annotation-predicates", "similar to -kubernetes-annotation-predicates configures predicates appended to east-west routes of annotated resources. See also -kubernetes-east-west-range-domains.") flag.BoolVar(&cfg.KubernetesOnlyAllowedExternalNames, "kubernetes-only-allowed-external-names", false, "only accept external name services, route group network backends and route group explicit LB endpoints from an allow list defined by zero or more -kubernetes-allowed-external-name flags") flag.Var(&cfg.KubernetesAllowedExternalNames, "kubernetes-allowed-external-name", "set zero or more regular expressions from which at least one should be matched by the external name services, route group network addresses and explicit endpoints domain names") flag.StringVar(&cfg.KubernetesRedisServiceNamespace, "kubernetes-redis-service-namespace", "", "Sets namespace for redis to be used to lookup endpoints") @@ -611,6 +617,17 @@ func validate(c *Config) error { if err != nil { return fmt.Errorf("invalid east-west-range-predicates: %w", err) } + + _, err = parseAnnotationPredicates(c.KubernetesAnnotationPredicatesString) + if err != nil { + return fmt.Errorf("invalid annotation predicates: %q, %w", c.KubernetesAnnotationPredicatesString, err) + } + + _, err = parseAnnotationPredicates(c.KubernetesEastWestRangeAnnotationPredicatesString) + if err != nil { + return fmt.Errorf("invalid east-west annotation predicates: %q, %w", c.KubernetesEastWestRangeAnnotationPredicatesString, err) + } + _, err = kubernetes.ParseBackendTrafficAlgorithm(c.KubernetesBackendTrafficAlgorithmString) if err != nil { return err @@ -673,6 +690,8 @@ func (c *Config) ParseArgs(progname string, args []string) error { c.ApplicationLogLevel, _ = log.ParseLevel(c.ApplicationLogLevelString) c.KubernetesPathMode, _ = kubernetes.ParsePathMode(c.KubernetesPathModeString) c.KubernetesEastWestRangePredicates, _ = eskip.ParsePredicates(c.KubernetesEastWestRangePredicatesString) + c.KubernetesAnnotationPredicates, _ = parseAnnotationPredicates(c.KubernetesAnnotationPredicatesString) + c.KubernetesEastWestRangeAnnotationPredicates, _ = parseAnnotationPredicates(c.KubernetesEastWestRangeAnnotationPredicatesString) c.KubernetesBackendTrafficAlgorithm, _ = kubernetes.ParseBackendTrafficAlgorithm(c.KubernetesBackendTrafficAlgorithmString) c.HistogramMetricBuckets, _ = c.parseHistogramBuckets() @@ -815,31 +834,33 @@ func (c *Config) ToOptions() skipper.Options { WaitFirstRouteLoad: c.WaitFirstRouteLoad, // Kubernetes: - Kubernetes: c.KubernetesIngress, - KubernetesInCluster: c.KubernetesInCluster, - KubernetesURL: c.KubernetesURL, - KubernetesTokenFile: c.KubernetesTokenFile, - KubernetesHealthcheck: c.KubernetesHealthcheck, - KubernetesHTTPSRedirect: c.KubernetesHTTPSRedirect, - KubernetesHTTPSRedirectCode: c.KubernetesHTTPSRedirectCode, - KubernetesDisableCatchAllRoutes: c.KubernetesDisableCatchAllRoutes, - KubernetesIngressClass: c.KubernetesIngressClass, - KubernetesRouteGroupClass: c.KubernetesRouteGroupClass, - WhitelistedHealthCheckCIDR: whitelistCIDRS, - KubernetesPathMode: c.KubernetesPathMode, - KubernetesNamespace: c.KubernetesNamespace, - KubernetesEnableEndpointslices: c.KubernetesEnableEndpointSlices, - KubernetesEnableEastWest: c.KubernetesEnableEastWest, - KubernetesEastWestDomain: c.KubernetesEastWestDomain, - KubernetesEastWestRangeDomains: c.KubernetesEastWestRangeDomains.values, - KubernetesEastWestRangePredicates: c.KubernetesEastWestRangePredicates, - KubernetesOnlyAllowedExternalNames: c.KubernetesOnlyAllowedExternalNames, - KubernetesAllowedExternalNames: c.KubernetesAllowedExternalNames, - KubernetesRedisServiceNamespace: c.KubernetesRedisServiceNamespace, - KubernetesRedisServiceName: c.KubernetesRedisServiceName, - KubernetesRedisServicePort: c.KubernetesRedisServicePort, - KubernetesBackendTrafficAlgorithm: c.KubernetesBackendTrafficAlgorithm, - KubernetesDefaultLoadBalancerAlgorithm: c.KubernetesDefaultLoadBalancerAlgorithm, + Kubernetes: c.KubernetesIngress, + KubernetesInCluster: c.KubernetesInCluster, + KubernetesURL: c.KubernetesURL, + KubernetesTokenFile: c.KubernetesTokenFile, + KubernetesHealthcheck: c.KubernetesHealthcheck, + KubernetesHTTPSRedirect: c.KubernetesHTTPSRedirect, + KubernetesHTTPSRedirectCode: c.KubernetesHTTPSRedirectCode, + KubernetesDisableCatchAllRoutes: c.KubernetesDisableCatchAllRoutes, + KubernetesIngressClass: c.KubernetesIngressClass, + KubernetesRouteGroupClass: c.KubernetesRouteGroupClass, + WhitelistedHealthCheckCIDR: whitelistCIDRS, + KubernetesPathMode: c.KubernetesPathMode, + KubernetesNamespace: c.KubernetesNamespace, + KubernetesEnableEndpointslices: c.KubernetesEnableEndpointSlices, + KubernetesEnableEastWest: c.KubernetesEnableEastWest, + KubernetesEastWestDomain: c.KubernetesEastWestDomain, + KubernetesEastWestRangeDomains: c.KubernetesEastWestRangeDomains.values, + KubernetesEastWestRangePredicates: c.KubernetesEastWestRangePredicates, + KubernetesEastWestRangeAnnotationPredicates: c.KubernetesEastWestRangeAnnotationPredicates, + KubernetesAnnotationPredicates: c.KubernetesAnnotationPredicates, + KubernetesOnlyAllowedExternalNames: c.KubernetesOnlyAllowedExternalNames, + KubernetesAllowedExternalNames: c.KubernetesAllowedExternalNames, + KubernetesRedisServiceNamespace: c.KubernetesRedisServiceNamespace, + KubernetesRedisServiceName: c.KubernetesRedisServiceName, + KubernetesRedisServicePort: c.KubernetesRedisServicePort, + KubernetesBackendTrafficAlgorithm: c.KubernetesBackendTrafficAlgorithm, + KubernetesDefaultLoadBalancerAlgorithm: c.KubernetesDefaultLoadBalancerAlgorithm, // API Monitoring: ApiUsageMonitoringEnable: c.ApiUsageMonitoringEnable, @@ -1150,3 +1171,44 @@ func (c *Config) checkDeprecated(configKeys map[string]interface{}, options ...s } } } + +func parseAnnotationPredicates(s []string) ([]kubernetes.AnnotationPredicates, error) { + var annotationPredicates []kubernetes.AnnotationPredicates + + for _, annotationPredicate := range s { + if annotationPredicate == "" { + continue + } + + annotationKey, rest, found := strings.Cut(annotationPredicate, "=") + if !found { + return nil, fmt.Errorf("invalid annotation predicate flag: %q, failed to get annotation key", annotationPredicate) + } + + annotationValue, predicates, found := strings.Cut(rest, "=") + if !found { + return nil, fmt.Errorf("invalid annotation predicate flag: %q, faild to get annotation value", annotationPredicate) + } + + predicateList, err := eskip.ParsePredicates(predicates) + if err != nil { + return nil, fmt.Errorf("invalid annotation predicate flag: %q, %w", annotationPredicate, err) + } + + // We throw an err because having duplicate annotation keys will override each others + for _, ap := range annotationPredicates { + if ap.Key == annotationKey && ap.Value == annotationValue { + return nil, fmt.Errorf("invalid annotation predicate flag: %q, duplicate annotation key and value", annotationPredicate) + } + } + + annotationPredicates = append(annotationPredicates, kubernetes.AnnotationPredicates{ + Key: annotationKey, + Value: annotationValue, + Predicates: predicateList, + }) + } + + return annotationPredicates, nil + +} diff --git a/config/config_test.go b/config/config_test.go index bad7f480ec..cf49e2c42d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -11,6 +11,10 @@ import ( "time" log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zalando/skipper/dataclients/kubernetes" + "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters/openpolicyagent" "github.com/zalando/skipper/net" "github.com/zalando/skipper/proxy" @@ -490,3 +494,118 @@ func TestMultiFlagYamlErr(t *testing.T) { t.Error("Failed to get error on wrong yaml input") } } + +func TestAnnotationPredicatesParsingInvalid(t *testing.T) { + for _, tc := range []struct { + name string + input []string + }{ + { + name: "wrong predicate", + input: []string{`to-add-predicate=true="Fo_o("123")"`}, + }, + { + name: "wrong predicate and empty value", + input: []string{"", `to-add-predicate=true="Fo_o()"`}, + }, + { + name: "duplicate", + input: []string{`to-add-predicate=true=Foo("123")`, `to-add-predicate=true=Bar("456")`}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := parseAnnotationPredicates(tc.input) + require.Error(t, err) + }) + } +} + +func TestAnnotationPredicatesParsingValid(t *testing.T) { + for _, tc := range []struct { + name string + input []string + expected []kubernetes.AnnotationPredicates + }{ + { + name: "empty", + input: []string{}, + expected: nil, + }, + { + name: "empty string", + input: []string{""}, + expected: nil, + }, + { + name: "empty string and a valid value", + input: []string{ + "", + `to-add-predicate=true=Foo("123")`, + }, + expected: []kubernetes.AnnotationPredicates{ + { + Key: "to-add-predicate", + Value: "true", + Predicates: []*eskip.Predicate{ + { + Name: "Foo", + Args: []any{"123"}, + }, + }, + }, + }, + }, + { + name: "single", + input: []string{`to-add-predicate=true=Foo("123")`}, + expected: []kubernetes.AnnotationPredicates{ + { + Key: "to-add-predicate", + Value: "true", + Predicates: []*eskip.Predicate{ + { + Name: "Foo", + Args: []any{"123"}, + }, + }, + }, + }, + }, + { + name: "multiple", + input: []string{`to-add-predicate=true=Foo("123")`, `to-add-predicate=false=Bar("456") && Foo("789")`}, + expected: []kubernetes.AnnotationPredicates{ + { + Key: "to-add-predicate", + Value: "true", + Predicates: []*eskip.Predicate{ + { + Name: "Foo", + Args: []any{"123"}, + }, + }, + }, + { + Key: "to-add-predicate", + Value: "false", + Predicates: []*eskip.Predicate{ + { + Name: "Bar", + Args: []any{"456"}, + }, + { + Name: "Foo", + Args: []any{"789"}, + }, + }, + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + val, err := parseAnnotationPredicates(tc.input) + require.NoError(t, err) + assert.Equal(t, tc.expected, val) + }) + } +} diff --git a/dataclients/kubernetes/annotations.go b/dataclients/kubernetes/annotations.go new file mode 100644 index 0000000000..3e0dd95ea1 --- /dev/null +++ b/dataclients/kubernetes/annotations.go @@ -0,0 +1,21 @@ +package kubernetes + +import ( + "github.com/zalando/skipper/eskip" +) + +type AnnotationPredicates struct { + Key string + Value string + Predicates []*eskip.Predicate +} + +func addAnnotationPredicates(annotationPredicates []AnnotationPredicates, annotations map[string]string, r *eskip.Route) { + for _, ap := range annotationPredicates { + if objAnnotationVal, ok := annotations[ap.Key]; ok && ap.Value == objAnnotationVal { + // since this annotation is managed by skipper operator, we can safely assume that the predicate is valid + // and we can append it to the route + r.Predicates = append(r.Predicates, ap.Predicates...) + } + } +} diff --git a/dataclients/kubernetes/hosts.go b/dataclients/kubernetes/hosts.go index 60dfd4a37a..24712d5287 100644 --- a/dataclients/kubernetes/hosts.go +++ b/dataclients/kubernetes/hosts.go @@ -81,3 +81,12 @@ func isExternalAddressAllowed(allowedDomains []*regexp.Regexp, address string) b return isExternalDomainAllowed(allowedDomains, u.Hostname()) } + +func isEastWestHost(host string, eastWestRangeDomains []string) bool { + for _, domainSuffix := range eastWestRangeDomains { + if strings.HasSuffix(host, domainSuffix) { + return true + } + } + return false +} diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index d44520f5c1..27896408ec 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -46,18 +46,20 @@ type ingressContext struct { } type ingress struct { - eastWestRangeDomains []string - eastWestRangePredicates []*eskip.Predicate - allowedExternalNames []*regexp.Regexp - kubernetesEastWestDomain string - pathMode PathMode - httpsRedirectCode int - kubernetesEnableEastWest bool - provideHTTPSRedirect bool - disableCatchAllRoutes bool - forceKubernetesService bool - backendTrafficAlgorithm BackendTrafficAlgorithm - defaultLoadBalancerAlgorithm string + eastWestRangeDomains []string + eastWestRangePredicates []*eskip.Predicate + allowedExternalNames []*regexp.Regexp + kubernetesEastWestDomain string + pathMode PathMode + httpsRedirectCode int + kubernetesEnableEastWest bool + provideHTTPSRedirect bool + disableCatchAllRoutes bool + forceKubernetesService bool + backendTrafficAlgorithm BackendTrafficAlgorithm + defaultLoadBalancerAlgorithm string + kubernetesAnnotationPredicates []AnnotationPredicates + kubernetesEastWestRangeAnnotationPredicates []AnnotationPredicates } var nonWord = regexp.MustCompile(`\W`) @@ -70,18 +72,20 @@ func (ic *ingressContext) addHostRoute(host string, route *eskip.Route) { func newIngress(o Options) *ingress { return &ingress{ - provideHTTPSRedirect: o.ProvideHTTPSRedirect, - httpsRedirectCode: o.HTTPSRedirectCode, - disableCatchAllRoutes: o.DisableCatchAllRoutes, - pathMode: o.PathMode, - kubernetesEnableEastWest: o.KubernetesEnableEastWest, - kubernetesEastWestDomain: o.KubernetesEastWestDomain, - eastWestRangeDomains: o.KubernetesEastWestRangeDomains, - eastWestRangePredicates: o.KubernetesEastWestRangePredicates, - allowedExternalNames: o.AllowedExternalNames, - forceKubernetesService: o.ForceKubernetesService, - backendTrafficAlgorithm: o.BackendTrafficAlgorithm, - defaultLoadBalancerAlgorithm: o.DefaultLoadBalancerAlgorithm, + provideHTTPSRedirect: o.ProvideHTTPSRedirect, + httpsRedirectCode: o.HTTPSRedirectCode, + disableCatchAllRoutes: o.DisableCatchAllRoutes, + pathMode: o.PathMode, + kubernetesEnableEastWest: o.KubernetesEnableEastWest, + kubernetesEastWestDomain: o.KubernetesEastWestDomain, + eastWestRangeDomains: o.KubernetesEastWestRangeDomains, + eastWestRangePredicates: o.KubernetesEastWestRangePredicates, + allowedExternalNames: o.AllowedExternalNames, + forceKubernetesService: o.ForceKubernetesService, + backendTrafficAlgorithm: o.BackendTrafficAlgorithm, + defaultLoadBalancerAlgorithm: o.DefaultLoadBalancerAlgorithm, + kubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates, + kubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates, } } @@ -178,12 +182,14 @@ func applyAnnotationPredicates(m PathMode, r *eskip.Route, annotation string) er return nil } -func addExtraRoutes(ic *ingressContext, ruleHost, path, pathType, eastWestDomain string, enableEastWest bool) { +func (ing *ingress) addExtraRoutes(ic *ingressContext, ruleHost, path, pathType string) { hosts := []string{createHostRx(ruleHost)} var ns, name string name = ic.ingressV1.Metadata.Name ns = ic.ingressV1.Metadata.Namespace - + eastWestDomain := ing.kubernetesEastWestDomain + enableEastWest := ing.kubernetesEnableEastWest + ewHost := isEastWestHost(ruleHost, ing.eastWestRangeDomains) // add extra routes from optional annotation for extraIndex, r := range ic.extraRoutes { route := *r @@ -196,6 +202,11 @@ func addExtraRoutes(ic *ingressContext, ruleHost, path, pathType, eastWestDomain extraIndex) setPathV1(ic.pathMode, &route, pathType, path) if n := countPathPredicates(&route); n <= 1 { + if ewHost { + addAnnotationPredicates(ing.kubernetesEastWestRangeAnnotationPredicates, ic.ingressV1.Metadata.Annotations, &route) + } else { + addAnnotationPredicates(ing.kubernetesAnnotationPredicates, ic.ingressV1.Metadata.Annotations, &route) + } ic.addHostRoute(ruleHost, &route) ic.redirect.updateHost(ruleHost) } else { diff --git a/dataclients/kubernetes/ingress_test.go b/dataclients/kubernetes/ingress_test.go index bd06777740..9c7f5a1c68 100644 --- a/dataclients/kubernetes/ingress_test.go +++ b/dataclients/kubernetes/ingress_test.go @@ -20,5 +20,6 @@ func TestIngressV1Fixtures(t *testing.T) { "testdata/ingressV1/traffic", "testdata/ingressV1/traffic-segment", "testdata/ingressV1/loadbalancer-algorithm", + "testdata/ingressV1/annotation-predicates", ) } diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index ed2431ed5b..fed5caeed6 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -210,16 +210,11 @@ func (ing *ingress) addEndpointsRuleV1(ic *ingressContext, host string, prule *d if err != nil { ic.logger.Errorf("Failed to apply annotation predicates: %v", err) } + ic.addHostRoute(host, endpointsRoute) redirect := ic.redirect - ewRangeMatch := false - for _, s := range ing.eastWestRangeDomains { - if strings.HasSuffix(host, s) { - ewRangeMatch = true - break - } - } + ewRangeMatch := isEastWestHost(host, ing.eastWestRangeDomains) if !(ewRangeMatch || strings.HasSuffix(host, ing.kubernetesEastWestDomain) && ing.kubernetesEastWestDomain != "") { switch { case redirect.ignore: @@ -234,6 +229,10 @@ func (ing *ingress) addEndpointsRuleV1(ic *ingressContext, host string, prule *d ic.addHostRoute(host, createIngressEnableHTTPSRedirect(endpointsRoute, redirect.code)) redirect.setHost(host) } + + addAnnotationPredicates(ing.kubernetesAnnotationPredicates, meta.Annotations, endpointsRoute) + } else { + addAnnotationPredicates(ing.kubernetesEastWestRangeAnnotationPredicates, meta.Annotations, endpointsRoute) } if ing.kubernetesEnableEastWest { @@ -279,7 +278,7 @@ func (ing *ingress) addSpecRuleV1(ic *ingressContext, ru *definitions.RuleV1) er trafficPerPathRule := computeBackendWeightsV1(ic.calculateTraffic, ic.backendWeights, ru) for _, prule := range ru.Http.Paths { - addExtraRoutes(ic, ru.Host, prule.Path, prule.PathType, ing.kubernetesEastWestDomain, ing.kubernetesEnableEastWest) + ing.addExtraRoutes(ic, ru.Host, prule.Path, prule.PathType) if trafficPerPathRule[prule].allowed() { err := ing.addEndpointsRuleV1(ic, ru.Host, prule, trafficPerPathRule[prule]) if err != nil { diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 523b71fdd8..671c4e2788 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -208,6 +208,13 @@ type Options struct { // appended to routes identified as to KubernetesEastWestRangeDomains. KubernetesEastWestRangePredicates []*eskip.Predicate + // KubernetesEastWestRangeAnnotationPredicates same as KubernetesAnnotationPredicates but will append to + // routes that has KubernetesEastWestRangeDomains suffix. + KubernetesEastWestRangeAnnotationPredicates []AnnotationPredicates + + // KubernetesAnnotationPredicates set a list predicates for each annotation key and value + KubernetesAnnotationPredicates []AnnotationPredicates + // DefaultFiltersDir enables default filters mechanism and sets the location of the default filters. // The provided filters are then applied to all routes. DefaultFiltersDir string diff --git a/dataclients/kubernetes/kubernetestest/fixtures.go b/dataclients/kubernetes/kubernetestest/fixtures.go index 597b35a582..a17aa7ea40 100644 --- a/dataclients/kubernetes/kubernetestest/fixtures.go +++ b/dataclients/kubernetes/kubernetestest/fixtures.go @@ -32,27 +32,29 @@ type fixtureSet struct { } type kubeOptionsParser struct { - IngressV1 bool `yaml:"ingressv1"` - EastWest bool `yaml:"eastWest"` - EastWestDomain string `yaml:"eastWestDomain"` - EastWestRangeDomains []string `yaml:"eastWestRangeDomains"` - EastWestRangePredicates []*eskip.Predicate `yaml:"eastWestRangePredicatesAppend"` - HTTPSRedirect bool `yaml:"httpsRedirect"` - HTTPSRedirectCode int `yaml:"httpsRedirectCode"` - DisableCatchAllRoutes bool `yaml:"disableCatchAllRoutes"` - BackendNameTracingTag bool `yaml:"backendNameTracingTag"` - OnlyAllowedExternalNames bool `yaml:"onlyAllowedExternalNames"` - AllowedExternalNames []string `yaml:"allowedExternalNames"` - IngressClass string `yaml:"kubernetes-ingress-class"` - KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` - KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` - IngressesLabels map[string]string `yaml:"kubernetes-ingresses-label-selector"` - ServicesLabels map[string]string `yaml:"kubernetes-services-label-selector"` - EndpointsLabels map[string]string `yaml:"kubernetes-endpoints-label-selector"` - EndpointsliceLabels map[string]string `yaml:"kubernetes-endpointslice-label-selector"` - ForceKubernetesService bool `yaml:"force-kubernetes-service"` - BackendTrafficAlgorithm string `yaml:"backend-traffic-algorithm"` - DefaultLoadBalancerAlgorithm string `yaml:"default-lb-algorithm"` + IngressV1 bool `yaml:"ingressv1"` + EastWest bool `yaml:"eastWest"` + EastWestDomain string `yaml:"eastWestDomain"` + EastWestRangeDomains []string `yaml:"eastWestRangeDomains"` + EastWestRangePredicates []*eskip.Predicate `yaml:"eastWestRangePredicatesAppend"` + HTTPSRedirect bool `yaml:"httpsRedirect"` + HTTPSRedirectCode int `yaml:"httpsRedirectCode"` + DisableCatchAllRoutes bool `yaml:"disableCatchAllRoutes"` + BackendNameTracingTag bool `yaml:"backendNameTracingTag"` + OnlyAllowedExternalNames bool `yaml:"onlyAllowedExternalNames"` + AllowedExternalNames []string `yaml:"allowedExternalNames"` + IngressClass string `yaml:"kubernetes-ingress-class"` + KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` + KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` + IngressesLabels map[string]string `yaml:"kubernetes-ingresses-label-selector"` + ServicesLabels map[string]string `yaml:"kubernetes-services-label-selector"` + EndpointsLabels map[string]string `yaml:"kubernetes-endpoints-label-selector"` + EndpointsliceLabels map[string]string `yaml:"kubernetes-endpointslice-label-selector"` + ForceKubernetesService bool `yaml:"force-kubernetes-service"` + BackendTrafficAlgorithm string `yaml:"backend-traffic-algorithm"` + DefaultLoadBalancerAlgorithm string `yaml:"default-lb-algorithm"` + KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"kubernetesAnnotationPredicates"` + KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"kubernetesEastWestRangeAnnotationPredicates"` } func baseNoExt(n string) string { @@ -229,6 +231,8 @@ func testFixture(t *testing.T, f fixtureSet) { o.KubernetesEastWestDomain = kop.EastWestDomain o.KubernetesEastWestRangeDomains = kop.EastWestRangeDomains o.KubernetesEastWestRangePredicates = kop.EastWestRangePredicates + o.KubernetesAnnotationPredicates = kop.KubernetesAnnotationPredicates + o.KubernetesEastWestRangeAnnotationPredicates = kop.KubernetesEastWestRangeAnnotationPredicates o.ProvideHTTPSRedirect = kop.HTTPSRedirect o.HTTPSRedirectCode = kop.HTTPSRedirectCode o.DisableCatchAllRoutes = kop.DisableCatchAllRoutes diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index 4ec23928f9..6f4a3b3fa3 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -591,6 +591,10 @@ func (r *routeGroups) convert(s *clusterState, df defaultFilters, loggingEnabled } } + for _, route := range ri { + addAnnotationPredicates(r.options.KubernetesAnnotationPredicates, rg.Metadata.Annotations, route) + } + rs = append(rs, ri...) } @@ -629,6 +633,9 @@ func (r *routeGroups) convert(s *clusterState, df defaultFilters, loggingEnabled } applyEastWestRangePredicates(internalRi, r.options.KubernetesEastWestRangePredicates) + for _, route := range internalRi { + addAnnotationPredicates(r.options.KubernetesEastWestRangeAnnotationPredicates, rg.Metadata.Annotations, route) + } if internalCtx.certificateRegistry != nil { for _, ctxTls := range rg.Spec.TLS { diff --git a/dataclients/kubernetes/routegroups_test.go b/dataclients/kubernetes/routegroups_test.go index 4963fd434e..ac9f2c89bd 100644 --- a/dataclients/kubernetes/routegroups_test.go +++ b/dataclients/kubernetes/routegroups_test.go @@ -61,3 +61,7 @@ func TestRouteGroupDefaultLoadBalancerAlgorithm(t *testing.T) { func TestRouteGroupTLS(t *testing.T) { kubernetestest.FixturesToTest(t, "testdata/routegroups/tls") } + +func TestAnnotationPredicates(t *testing.T) { + kubernetestest.FixturesToTest(t, "testdata/routegroups/annotation-predicates") +} diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.eskip b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.eskip new file mode 100644 index 0000000000..792345968b --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.eskip @@ -0,0 +1,43 @@ +kube_default__myapp1__zone1_rule1_test____myapp: + Host("^(zone1[.]rule1[.]test[.]?(:[0-9]+)?)$") + && Zone("zone1") + && True() + -> ; + +kube_default__myapp1_r1_0__zone1_rule1_test____: + Host("^(zone1[.]rule1[.]test[.]?(:[0-9]+)?)$") + && Path("/zone1") + && Zone("zone1") + && True() + -> "https://zone1.route.test"; + +kube_default__myapp1__zone1_rule2_ingress_cluster_local____myapp: + Host("^(zone1-rule2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + -> ; + +kube_default__myapp1_r1_0__zone1_rule2_ingress_cluster_local____: + Host("^(zone1-rule2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && Path("/zone1") + -> "https://zone1.route.test"; + +kubeew_default__myapp1__zone1_rule1_test____myapp: + Host("^(myapp1[.]default[.]skipper[.]cluster[.]local[.]?(:[0-9]+)?)$") + && True() + && Zone("zone1") + -> ; + +kubeew_default__myapp1__zone1_rule2_ingress_cluster_local____myapp: + Host("^(myapp1[.]default[.]skipper[.]cluster[.]local[.]?(:[0-9]+)?)$") + -> ; + +kubeew_default__myapp1_r1_0__zone1_rule1_test____: + Host("^(myapp1[.]default[.]skipper[.]cluster[.]local[.]?(:[0-9]+)?)$") + && Path("/zone1") + && True() + && Zone("zone1") + -> "https://zone1.route.test"; + +kubeew_default__myapp1_r1_0__zone1_rule2_ingress_cluster_local____: + Host("^(myapp1[.]default[.]skipper[.]cluster[.]local[.]?(:[0-9]+)?)$") + && Path("/zone1") + -> "https://zone1.route.test"; diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.kube b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.kube new file mode 100644 index 0000000000..95eb525776 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.kube @@ -0,0 +1,11 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "Zone" + args: ["zone1"] + - name: "True" + args: [] +eastWestRangeDomains: + - "ingress.cluster.local" +eastWest: true diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.yaml b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.yaml new file mode 100644 index 0000000000..e749795fc7 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-ew-enabled.yaml @@ -0,0 +1,65 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp1 + namespace: default + annotations: + zalando.org/zone: "zone1" + zalando.org/skipper-routes: | + r1: Path("/zone1") -> "https://zone1.route.test"; +spec: + rules: + - host: zone1.rule1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: zone1-rule2.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: foo + port: 8080 + protocol: TCP + targetPort: web + - name: web + port: 80 + protocol: TCP + targetPort: foo + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: foo + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.eskip b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.eskip new file mode 100644 index 0000000000..34173d1d57 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.eskip @@ -0,0 +1,100 @@ +kube_default__myapp1__app1_alias1_test____myapp: + Host("^(app1[.]alias1[.]test[.]?(:[0-9]+)?)$") + && Zone("zone1") + && True() + -> ; + +kube_default__myapp1_r1_0__app1_alias1_test____: + Host("^(app1[.]alias1[.]test[.]?(:[0-9]+)?)$") + && Path("/zone1") + && Zone("zone1") + && True() + -> "https://app1.route.test"; + +kube_default__myapp1__app1_alias2_ingress_cluster_local____myapp: + Host("^(app1-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + -> ; + +kube_default__myapp1_r1_0__app1_alias2_ingress_cluster_local____: + Host("^(app1-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Path("/zone1") + -> "https://app1.route.test"; + + +kube_default__myapp2__app2_alias1_test____myapp: + Host("^(app2[.]alias1[.]test[.]?(:[0-9]+)?)$") + -> ; + +kube_default__myapp2__app2_alias2_ingress_cluster_local____myapp: + Host("^(app2-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_default__myapp2_r1_0__app2_alias1_test____: + Host("^(app2[.]alias1[.]test[.]?(:[0-9]+)?)$") + && Path("/internal-zone") + -> "https://app2.route.test"; + +kube_default__myapp2_r1_0__app2_alias2_ingress_cluster_local____: + Host("^(app2-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Path("/internal-zone") + && InternalZone("internal-zone") + && True() + -> "https://app2.route.test"; + +kube_default__myapp3__app3_alias1_ingress_cluster_local____myapp: + Host("^(app3-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_default__myapp3__app3_alias2_ingress_cluster_local____myapp: + Host("^(app3-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_default__myapp3_r1_0__app3_alias1_ingress_cluster_local____: + Host("^(app3-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && Path("/internal-zone") + && True() + -> "https://app3.route.test"; + +kube_default__myapp3_r1_0__app3_alias2_ingress_cluster_local____: + Host("^(app3-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && Path("/internal-zone") + && True() + -> "https://app3.route.test"; + +kube_default__myapp4__app4_alias1_ingress_cluster_local____myapp: + Host("^(app4-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + -> ; + +kube_default__myapp4__app4_alias2_ingress_cluster_local____myapp: + Host("^(app4-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + -> ; + +kube_default__myapp4_r1_0__app4_alias1_ingress_cluster_local____: + Host("^(app4-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Path("/internal-zone") + -> "https://app4.route.test"; + +kube_default__myapp4_r1_0__app4_alias2_ingress_cluster_local____: + Host("^(app4-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Path("/internal-zone") + -> "https://app4.route.test"; diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.kube b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.kube new file mode 100644 index 0000000000..e1fe0360c5 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.kube @@ -0,0 +1,21 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "Zone" + args: ["zone1"] + - name: "True" + args: [] +kubernetesEastWestRangeAnnotationPredicates: + - key: zalando.org/zone + value: internal + predicates: + - name: "InternalZone" + args: ["internal-zone"] + - name: "True" + args: [] +eastWestRangeDomains: + - "ingress.cluster.local" +eastWestRangePredicatesAppend: + - name: "ClientIP" + args: ["10.2.0.0/16"] diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.yaml b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.yaml new file mode 100644 index 0000000000..7d0c3311cf --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname-predicates.yaml @@ -0,0 +1,155 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp1 + namespace: default + annotations: + zalando.org/zone: "zone1" + zalando.org/skipper-routes: | + r1: Path("/zone1") -> "https://app1.route.test"; +spec: + rules: + - host: app1.alias1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: app1-alias2.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp2 + namespace: default + annotations: + zalando.org/zone: "internal" + zalando.org/skipper-routes: | + r1: Path("/internal-zone") -> "https://app2.route.test"; +spec: + rules: + - host: app2.alias1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: app2-alias2.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp3 + namespace: default + annotations: + zalando.org/zone: "internal" + zalando.org/skipper-routes: | + r1: Path("/internal-zone") -> "https://app3.route.test"; +spec: + rules: + - host: app3-alias1.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: app3-alias2.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp4 + namespace: default + annotations: + zalando.org/zone: "zone1" + zalando.org/skipper-routes: | + r1: Path("/internal-zone") -> "https://app4.route.test"; +spec: + rules: + - host: app4-alias1.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: app4-alias2.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: foo + port: 8080 + protocol: TCP + targetPort: web + - name: web + port: 80 + protocol: TCP + targetPort: foo + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: foo + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.eskip b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.eskip new file mode 100644 index 0000000000..5286b0a468 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.eskip @@ -0,0 +1,23 @@ +kube_default__myapp1__zone1_rule1_test____myapp: + Host("^(zone1[.]rule1[.]test[.]?(:[0-9]+)?)$") + && Zone("zone1") + && True() + -> ; + +kube_default__myapp1_r1_0__zone1_rule1_test____: + Host("^(zone1[.]rule1[.]test[.]?(:[0-9]+)?)$") + && Path("/zone1") + && Zone("zone1") + && True() + -> "https://zone1.route.test"; + +kube_default__myapp1__zone1_rule2_ingress_cluster_local____myapp: + Host("^(zone1-rule2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + -> ; + +kube_default__myapp1_r1_0__zone1_rule2_ingress_cluster_local____: + Host("^(zone1-rule2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Path("/zone1") + -> "https://zone1.route.test"; diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.kube b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.kube new file mode 100644 index 0000000000..d1101079ce --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.kube @@ -0,0 +1,13 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "Zone" + args: ["zone1"] + - name: "True" + args: [] +eastWestRangeDomains: + - "ingress.cluster.local" +eastWestRangePredicatesAppend: + - name: "ClientIP" + args: ["10.2.0.0/16"] diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.yaml b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.yaml new file mode 100644 index 0000000000..e749795fc7 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates-with-internal-hostname.yaml @@ -0,0 +1,65 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp1 + namespace: default + annotations: + zalando.org/zone: "zone1" + zalando.org/skipper-routes: | + r1: Path("/zone1") -> "https://zone1.route.test"; +spec: + rules: + - host: zone1.rule1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: zone1-rule2.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: foo + port: 8080 + protocol: TCP + targetPort: web + - name: web + port: 80 + protocol: TCP + targetPort: foo + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: foo + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.eskip b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.eskip new file mode 100644 index 0000000000..357883748a --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.eskip @@ -0,0 +1,52 @@ +kube_default__myapp1__zone1_test____myapp: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && False() + -> ; + +kube_default__myapp2__zone2_test____myapp: + Host("^(zone2[.]test[.]?(:[0-9]+)?)$") + -> ; + +kube_default__myapp3__zone3_test____myapp: + Host("^(zone3[.]test[.]?(:[0-9]+)?)$") + && True() + -> ; + +kube_default__myapp4__zone4_test____myapp: + Host("^(zone4[.]test[.]?(:[0-9]+)?)$") + && Foo("xyz") + && Bar("123") + -> ; + +kube_default__myapp5__zone5_test____myapp: + Host("^(zone5[.]test[.]?(:[0-9]+)?)$") + && Zone("zone5") + -> ; + +kube_default__myapp5_r1_0__zone5_test____: + Host("^(zone5[.]test[.]?(:[0-9]+)?)$") + && Path("/zone5") + && Zone("zone5") + -> "https://zone5.route.test"; + +kube_default__myapp6__zone6_rule1_test____myapp: + Host("^(zone6[.]rule1[.]test[.]?(:[0-9]+)?)$") + && Zone("zone6") + -> ; + +kube_default__myapp6_r1_0__zone6_rule1_test____: + Host("^(zone6[.]rule1[.]test[.]?(:[0-9]+)?)$") + && Path("/zone6") + && Zone("zone6") + -> "https://zone6.route.test"; + +kube_default__myapp6__zone6_rule2_test____myapp: + Host("^(zone6[.]rule2[.]test[.]?(:[0-9]+)?)$") + && Zone("zone6") + -> ; + +kube_default__myapp6_r1_0__zone6_rule2_test____: + Host("^(zone6[.]rule2[.]test[.]?(:[0-9]+)?)$") + && Path("/zone6") + && Zone("zone6") + -> "https://zone6.route.test"; diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.kube b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.kube new file mode 100644 index 0000000000..419bbdd81a --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.kube @@ -0,0 +1,26 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "False" + args: [] + - key: zalando.org/zone + value: zone3 + predicates: + - name: "True" + args: [] + - key: zalando.org/zone + value: zone4 + predicates: + - name: "Bar" + args: ["123"] + - key: zalando.org/zone + value: zone5 + predicates: + - name: "Zone" + args: ["zone5"] + - key: zalando.org/zone + value: zone6 + predicates: + - name: "Zone" + args: ["zone6"] diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.yaml b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.yaml new file mode 100644 index 0000000000..b69abc3316 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-predicates/annotation-predicates.yaml @@ -0,0 +1,163 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp1 + namespace: default + annotations: + zalando.org/zone: "zone1" +spec: + rules: + - host: zone1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp2 + namespace: default + annotations: + zalando.org/zone: "unknown" +spec: + rules: + - host: zone2.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp3 + namespace: default + annotations: + zalando.org/zone: "zone3" +spec: + rules: + - host: zone3.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp4 + namespace: default + annotations: + zalando.org/zone: "zone4" + zalando.org/skipper-predicate: Foo("xyz") +spec: + rules: + - host: zone4.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp5 + namespace: default + annotations: + zalando.org/zone: "zone5" + zalando.org/skipper-routes: | + r1: Path("/zone5") -> "https://zone5.route.test"; +spec: + rules: + - host: zone5.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp6 + namespace: default + annotations: + zalando.org/zone: "zone6" + zalando.org/skipper-routes: | + r1: Path("/zone6") -> "https://zone6.route.test"; +spec: + rules: + - host: zone6.rule1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: zone6.rule2.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: foo + port: 8080 + protocol: TCP + targetPort: web + - name: web + port: 80 + protocol: TCP + targetPort: foo + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: foo + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.eskip b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.eskip new file mode 100644 index 0000000000..9b1b657475 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.eskip @@ -0,0 +1,38 @@ +kube_rg__default__myapp1__all__0_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && False() + -> ; + +kube_rg__default__myapp1__all__1_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && False() + -> ; + +kube_rg____zone1_test__catchall__0_0: Host("^(zone1[.]test[.]?(:[0-9]+)?)$") && False() -> ; + +kube_rg__internal_default__myapp1__all__0_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + -> ; + +kube_rg__internal_default__myapp1__all__1_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + -> ; + +kube_rg__internal___zone1_ingress_cluster_local__catchall__0_0: Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") -> ; + + +kubeew_rg__default__myapp1__all__0_0: + Host("^(myapp1[.]default[.]skipper[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && False() + -> ; + +kubeew_rg__default__myapp1__all__1_0: + Host("^(myapp1[.]default[.]skipper[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && False() + -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.kube b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.kube new file mode 100644 index 0000000000..fe86a2da0f --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.kube @@ -0,0 +1,9 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "False" + args: [] +eastWest: true +eastWestRangeDomains: + - "ingress.cluster.local" diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.yaml b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.yaml new file mode 100644 index 0000000000..8de8bdf198 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-ew-enabled.yaml @@ -0,0 +1,57 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp1 + annotations: + zalando.org/zone: "zone1" +spec: + hosts: + - zone1.test + - zone1.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: main + port: 80 + protocol: TCP + targetPort: 7272 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp + namespace: default +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: main + port: 7272 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.eskip b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.eskip new file mode 100644 index 0000000000..4d55944ec0 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.eskip @@ -0,0 +1,115 @@ +kube_rg__default__myapp1__all__0_0: + Host("^(app1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && False() + -> ; + +kube_rg__default__myapp1__all__1_0: + Host("^(app1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && False() + -> ; + +kube_rg____app1_test__catchall__0_0: Host("^(app1[.]test[.]?(:[0-9]+)?)$") && False() -> ; + +kube_rg__internal_default__myapp1__all__0_0: + Host("^(app1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal_default__myapp1__all__1_0: + Host("^(app1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal___app1_ingress_cluster_local__catchall__0_0: Host("^(app1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") && ClientIP("10.2.0.0/16") -> ; + + +kube_rg__default__myapp2__all__0_0: + Host("^(app2[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + -> ; + +kube_rg__default__myapp2__all__1_0: + Host("^(app2[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + -> ; + +kube_rg____app2_test__catchall__0_0: Host("^(app2[.]test[.]?(:[0-9]+)?)$") -> ; + +kube_rg__internal_default__myapp2__all__0_0: + Host("^(app2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal_default__myapp2__all__1_0: + Host("^(app2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal___app2_ingress_cluster_local__catchall__0_0: + Host("^(app2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal_default__myapp3__all__0_0: + Host("^(app3-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?|app3-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal_default__myapp3__all__1_0: + Host("^(app3-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?|app3-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal___app3_alias1_ingress_cluster_local__catchall__0_0: + Host("^(app3-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal___app3_alias2_ingress_cluster_local__catchall__0_0: + Host("^(app3-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && InternalZone("internal-zone") + && True() + -> ; + +kube_rg__internal_default__myapp4__all__0_0: + Host("^(app4-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?|app4-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal_default__myapp4__all__1_0: + Host("^(app4-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?|app4-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal___app4_alias1_ingress_cluster_local__catchall__0_0: + Host("^(app4-alias1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal___app4_alias2_ingress_cluster_local__catchall__0_0: + Host("^(app4-alias2[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.kube b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.kube new file mode 100644 index 0000000000..9cdffb23a5 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.kube @@ -0,0 +1,19 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "False" + args: [] +eastWestRangeDomains: + - "ingress.cluster.local" +eastWestRangePredicatesAppend: + - name: "ClientIP" + args: ["10.2.0.0/16"] +kubernetesEastWestRangeAnnotationPredicates: + - key: zalando.org/zone + value: internal + predicates: + - name: "InternalZone" + args: ["internal-zone"] + - name: "True" + args: [] diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.yaml b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.yaml new file mode 100644 index 0000000000..86aef6af0c --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host-predicates.yaml @@ -0,0 +1,132 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp1 + annotations: + zalando.org/zone: "zone1" +spec: + hosts: + - app1.test + - app1.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp2 + annotations: + zalando.org/zone: "internal" +spec: + hosts: + - app2.test + - app2.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp3 + annotations: + zalando.org/zone: "internal" +spec: + hosts: + - app3-alias1.ingress.cluster.local + - app3-alias2.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp4 + annotations: + zalando.org/zone: "zone1" +spec: + hosts: + - app4-alias1.ingress.cluster.local + - app4-alias2.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: main + port: 80 + protocol: TCP + targetPort: 7272 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp + namespace: default +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: main + port: 7272 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.eskip b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.eskip new file mode 100644 index 0000000000..5d87096ab1 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.eskip @@ -0,0 +1,27 @@ +kube_rg__default__myapp1__all__0_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && False() + -> ; + +kube_rg__default__myapp1__all__1_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && False() + -> ; + +kube_rg____zone1_test__catchall__0_0: Host("^(zone1[.]test[.]?(:[0-9]+)?)$") && False() -> ; + +kube_rg__internal_default__myapp1__all__0_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal_default__myapp1__all__1_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && ClientIP("10.2.0.0/16") + -> ; + +kube_rg__internal___zone1_ingress_cluster_local__catchall__0_0: Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") && ClientIP("10.2.0.0/16") -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.kube b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.kube new file mode 100644 index 0000000000..cc523ba0d1 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.kube @@ -0,0 +1,11 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "False" + args: [] +eastWestRangeDomains: + - "ingress.cluster.local" +eastWestRangePredicatesAppend: + - name: "ClientIP" + args: ["10.2.0.0/16"] diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.yaml b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.yaml new file mode 100644 index 0000000000..8de8bdf198 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates-with-internal-host.yaml @@ -0,0 +1,57 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp1 + annotations: + zalando.org/zone: "zone1" +spec: + hosts: + - zone1.test + - zone1.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: main + port: 80 + protocol: TCP + targetPort: 7272 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp + namespace: default +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: main + port: 7272 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.eskip b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.eskip new file mode 100644 index 0000000000..af3f899640 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.eskip @@ -0,0 +1,36 @@ +kube_rg__default__myapp1__all__0_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && False() + -> ; + +kube_rg____zone1_test__catchall__0_0: Host("^(zone1[.]test[.]?(:[0-9]+)?)$") && False() -> ; + +kube_rg__default__myapp2__all__0_0: + Host("^(zone2[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + -> ; + +kube_rg____zone2_test__catchall__0_0: Host("^(zone2[.]test[.]?(:[0-9]+)?)$") -> ; + +kube_rg__default__myapp3__all__0_0: + Host("^(zone3[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && True() + -> ; + +kube_rg____zone3_test__catchall__0_0: Host("^(zone3[.]test[.]?(:[0-9]+)?)$") && True() -> ; + +kube_rg__default__myapp4__all__0_0: + Host("^(zone4[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && Traffic("10") + -> ; + +kube_rg__default__myapp4__all__1_0: + Host("^(zone4[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && Traffic("10") + -> ; + +kube_rg____zone4_test__catchall__0_0: Host("^(zone4[.]test[.]?(:[0-9]+)?)$") && Traffic("10") -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.kube b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.kube new file mode 100644 index 0000000000..f129208f5f --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.kube @@ -0,0 +1,16 @@ +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: "False" + args: [] + - key: zalando.org/zone + value: zone3 + predicates: + - name: "True" + args: [] + - key: zalando.org/zone + value: zone4 + predicates: + - name: "Traffic" + args: ["10"] diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.yaml b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.yaml new file mode 100644 index 0000000000..59f1d6d6e2 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-predicates/annotation-predicates.yaml @@ -0,0 +1,113 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp1 + annotations: + zalando.org/zone: "zone1" +spec: + hosts: + - zone1.test + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + routes: + - pathSubtree: / + backends: + - backendName: myapp +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp2 + annotations: + zalando.org/zone: "unknown" +spec: + hosts: + - zone2.test + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + routes: + - pathSubtree: / + backends: + - backendName: myapp +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp3 + annotations: + zalando.org/zone: "zone3" +spec: + hosts: + - zone3.test + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + routes: + - pathSubtree: / + backends: + - backendName: myapp +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp4 + annotations: + zalando.org/zone: "zone4" +spec: + hosts: + - zone4.test + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: main + port: 80 + protocol: TCP + targetPort: 7272 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp + namespace: default +subsets: +- addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: main + port: 7272 + protocol: TCP diff --git a/skipper.go b/skipper.go index 19e24f027f..5ee6238706 100644 --- a/skipper.go +++ b/skipper.go @@ -268,6 +268,13 @@ type Options struct { // appended to routes identified as to KubernetesEastWestRangeDomains. KubernetesEastWestRangePredicates []*eskip.Predicate + // KubernetesEastWestRangeAnnotationPredicates same as KubernetesAnnotationPredicates but will append to + // routes that has KubernetesEastWestRangeDomains suffix. + KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates + + // KubernetesAnnotationPredicates set a list predicates for each annotation key and value + KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates + // KubernetesOnlyAllowedExternalNames will enable validation of ingress external names and route groups network // backend addresses, explicit LB endpoints validation against the list of patterns in // AllowedExternalNames. @@ -951,37 +958,39 @@ type Options struct { func (o *Options) KubernetesDataClientOptions() kubernetes.Options { return kubernetes.Options{ - AllowedExternalNames: o.KubernetesAllowedExternalNames, - BackendNameTracingTag: o.OpenTracingBackendNameTag, - DefaultFiltersDir: o.DefaultFiltersDir, - KubernetesInCluster: o.KubernetesInCluster, - KubernetesURL: o.KubernetesURL, - TokenFile: o.KubernetesTokenFile, - KubernetesNamespace: o.KubernetesNamespace, - KubernetesEnableEastWest: o.KubernetesEnableEastWest, - KubernetesEnableEndpointslices: o.KubernetesEnableEndpointslices, - KubernetesEastWestDomain: o.KubernetesEastWestDomain, - KubernetesEastWestRangeDomains: o.KubernetesEastWestRangeDomains, - KubernetesEastWestRangePredicates: o.KubernetesEastWestRangePredicates, - HTTPSRedirectCode: o.KubernetesHTTPSRedirectCode, - DisableCatchAllRoutes: o.KubernetesDisableCatchAllRoutes, - IngressClass: o.KubernetesIngressClass, - IngressLabelSelectors: o.KubernetesIngressLabelSelectors, - ServicesLabelSelectors: o.KubernetesServicesLabelSelectors, - EndpointsLabelSelectors: o.KubernetesEndpointsLabelSelectors, - SecretsLabelSelectors: o.KubernetesSecretsLabelSelectors, - RouteGroupsLabelSelectors: o.KubernetesRouteGroupsLabelSelectors, - OnlyAllowedExternalNames: o.KubernetesOnlyAllowedExternalNames, - OriginMarker: o.EnableRouteCreationMetrics, - PathMode: o.KubernetesPathMode, - ProvideHealthcheck: o.KubernetesHealthcheck, - ProvideHTTPSRedirect: o.KubernetesHTTPSRedirect, - ReverseSourcePredicate: o.ReverseSourcePredicate, - RouteGroupClass: o.KubernetesRouteGroupClass, - WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR, - ForceKubernetesService: o.KubernetesForceService, - BackendTrafficAlgorithm: o.KubernetesBackendTrafficAlgorithm, - DefaultLoadBalancerAlgorithm: o.KubernetesDefaultLoadBalancerAlgorithm, + AllowedExternalNames: o.KubernetesAllowedExternalNames, + BackendNameTracingTag: o.OpenTracingBackendNameTag, + DefaultFiltersDir: o.DefaultFiltersDir, + KubernetesInCluster: o.KubernetesInCluster, + KubernetesURL: o.KubernetesURL, + TokenFile: o.KubernetesTokenFile, + KubernetesNamespace: o.KubernetesNamespace, + KubernetesEnableEastWest: o.KubernetesEnableEastWest, + KubernetesEnableEndpointslices: o.KubernetesEnableEndpointslices, + KubernetesEastWestDomain: o.KubernetesEastWestDomain, + KubernetesEastWestRangeDomains: o.KubernetesEastWestRangeDomains, + KubernetesEastWestRangePredicates: o.KubernetesEastWestRangePredicates, + KubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates, + KubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates, + HTTPSRedirectCode: o.KubernetesHTTPSRedirectCode, + DisableCatchAllRoutes: o.KubernetesDisableCatchAllRoutes, + IngressClass: o.KubernetesIngressClass, + IngressLabelSelectors: o.KubernetesIngressLabelSelectors, + ServicesLabelSelectors: o.KubernetesServicesLabelSelectors, + EndpointsLabelSelectors: o.KubernetesEndpointsLabelSelectors, + SecretsLabelSelectors: o.KubernetesSecretsLabelSelectors, + RouteGroupsLabelSelectors: o.KubernetesRouteGroupsLabelSelectors, + OnlyAllowedExternalNames: o.KubernetesOnlyAllowedExternalNames, + OriginMarker: o.EnableRouteCreationMetrics, + PathMode: o.KubernetesPathMode, + ProvideHealthcheck: o.KubernetesHealthcheck, + ProvideHTTPSRedirect: o.KubernetesHTTPSRedirect, + ReverseSourcePredicate: o.ReverseSourcePredicate, + RouteGroupClass: o.KubernetesRouteGroupClass, + WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR, + ForceKubernetesService: o.KubernetesForceService, + BackendTrafficAlgorithm: o.KubernetesBackendTrafficAlgorithm, + DefaultLoadBalancerAlgorithm: o.KubernetesDefaultLoadBalancerAlgorithm, } } From d04395886a1b42c2e7a492429faadf23e9f9d292 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:07:44 +0100 Subject: [PATCH 0958/1047] build(deps): bump docker/metadata-action from 5.5.1 to 5.6.1 (#3319) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.5.1 to 5.6.1. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/8e5442c4ef9f78752691e2d8f8d19755c6f78e81...369eb591f429131d6889c46b94e711f089e6ca96) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index f30be3e696..d1839b144f 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -61,7 +61,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Docker meta - uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 id: meta with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} From 564b112aa3a6ec3b2cd046bd72686890a38b13f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:08:23 +0100 Subject: [PATCH 0959/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3325) Bumps oss-fuzz-base/base-builder-go from `fd6832a` to `b3111d8`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 876b2075c2..74ad765330 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:fd6832a7a2b20111b2b792ac71184e9684c84652590f136141072a982e0fc26f +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:b3111d8c1f679c67e40b14fd839c5e1c5d0fc2b5bcb08f42c7ac9323599ce308 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From d20a0bf41bd516a73956e3b927fc3f5e9f0a4191 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:09:15 +0100 Subject: [PATCH 0960/1047] build(deps): bump alpine from `beefdbd` to `1e42bbe` in /packaging (#3310) Bumps alpine from `beefdbd` to `1e42bbe`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 183cd71fc1..4f6cb4853d 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d +FROM --platform=linux/arm64 alpine@sha256:1e42bbe2508154c9126d48c2b8a75420c3544343bf86fd041fb7527e017a4b4a LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index af7aa9a356..400242dfbb 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d +FROM --platform=linux/arm/v7 alpine@sha256:1e42bbe2508154c9126d48c2b8a75420c3544343bf86fd041fb7527e017a4b4a LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From c77f39465991892de39da13aaf5ab0ded5cc69c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:09:53 +0100 Subject: [PATCH 0961/1047] build(deps): bump amazonlinux from `5bf4cf4` to `3dfca23` in /fuzz (#3307) Bumps amazonlinux from `5bf4cf4` to `3dfca23`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index d0d5c46890..7131e784fd 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:5bf4cf420ef7e50835911993c6a2ddb0e8f5101c0ef89ca20e9d02a03c8c3a8c +FROM amazonlinux:2023@sha256:3dfca23bb3b578c2807053e1a7b2db6a3622e9c147be79e5c2ea3a4569caafb3 WORKDIR /workspace From 93abbf41f8beee353e5a639e3905b2e5c45a414d Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Thu, 5 Dec 2024 12:23:38 +0300 Subject: [PATCH 0962/1047] Move testTracer struct in tracingtest package and reuse it (#3322) Signed-off-by: Roman Zavodskikh --- filters/auth/jwt_metrics_test.go | 8 ++--- filters/scheduler/fifo_test.go | 13 ++------ filters/scheduler/lifo_test.go | 38 ++-------------------- filters/tracing/tag_test.go | 3 +- net/httpclient_example_test.go | 30 +++-------------- net/httpclient_test.go | 4 +-- proxy/tracing_test.go | 38 ++++++++-------------- tracing/tracingtest/mocktracer.go | 53 +++++++++++++++++++++++++++++++ 8 files changed, 82 insertions(+), 105 deletions(-) create mode 100644 tracing/tracingtest/mocktracer.go diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index 833a8bc82f..3c6b324084 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -8,9 +8,7 @@ import ( "net/http/httptest" "net/url" "testing" - "time" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zalando/skipper/eskip" @@ -20,6 +18,7 @@ import ( "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/proxy/proxytest" "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/tracing/tracingtest" ) func TestJwtMetrics(t *testing.T) { @@ -275,7 +274,7 @@ func TestJwtMetrics(t *testing.T) { t.Run(tc.name, func(t *testing.T) { m := &metricstest.MockMetrics{} defer m.Close() - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() fr := builtin.MakeRegistry() fr.Register(auth.NewJwtMetrics()) @@ -304,8 +303,7 @@ func TestJwtMetrics(t *testing.T) { resp.Body.Close() require.Equal(t, tc.status, resp.StatusCode) - // wait for the span to be finished - require.Eventually(t, func() bool { return len(tracer.FinishedSpans()) == 1 }, time.Second, 100*time.Millisecond) + require.Equal(t, 1, len(tracer.FinishedSpans())) span := tracer.FinishedSpans()[0] if tc.expectedTag == "" { diff --git a/filters/scheduler/fifo_test.go b/filters/scheduler/fifo_test.go index c9f0093f20..3ba743deb5 100644 --- a/filters/scheduler/fifo_test.go +++ b/filters/scheduler/fifo_test.go @@ -11,7 +11,6 @@ import ( "testing/iotest" "time" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/sirupsen/logrus" "github.com/zalando/skipper/eskip" @@ -296,10 +295,8 @@ func TestFifoWithBody(t *testing.T) { rt := routing.New(ro) defer rt.Close() <-rt.FirstLoad() - tracer := &testTracer{MockTracer: mocktracer.New()} pr := proxy.WithParams(proxy.Params{ - Routing: rt, - OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + Routing: rt, }) defer pr.Close() ts := stdlibhttptest.NewServer(pr) @@ -508,10 +505,8 @@ func TestFifo(t *testing.T) { defer rt.Close() <-rt.FirstLoad() - tracer := &testTracer{MockTracer: mocktracer.New()} pr := proxy.WithParams(proxy.Params{ - Routing: rt, - OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + Routing: rt, }) defer pr.Close() @@ -636,10 +631,8 @@ func TestFifoConstantRouteUpdates(t *testing.T) { defer rt.Close() <-rt.FirstLoad() - tracer := &testTracer{MockTracer: mocktracer.New()} pr := proxy.WithParams(proxy.Params{ - Routing: rt, - OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + Routing: rt, }) defer pr.Close() diff --git a/filters/scheduler/lifo_test.go b/filters/scheduler/lifo_test.go index ac30921066..e3d59f7b7f 100644 --- a/filters/scheduler/lifo_test.go +++ b/filters/scheduler/lifo_test.go @@ -6,7 +6,6 @@ import ( "net/http/httptest" "net/url" "sync" - "sync/atomic" "testing" "time" @@ -14,14 +13,13 @@ import ( "github.com/stretchr/testify/require" "github.com/aryszka/jobqueue" - "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/routing" "github.com/zalando/skipper/routing/testdataclient" "github.com/zalando/skipper/scheduler" + "github.com/zalando/skipper/tracing/tracingtest" ) func TestNewLIFO(t *testing.T) { @@ -442,38 +440,6 @@ func TestNewLIFO(t *testing.T) { } } -type testTracer struct { - *mocktracer.MockTracer - spans int32 -} - -func (t *testTracer) Reset() { - atomic.StoreInt32(&t.spans, 0) - t.MockTracer.Reset() -} - -func (t *testTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { - atomic.AddInt32(&t.spans, 1) - return t.MockTracer.StartSpan(operationName, opts...) -} - -func (t *testTracer) FinishedSpans() []*mocktracer.MockSpan { - timeout := time.After(1 * time.Second) - retry := time.NewTicker(100 * time.Millisecond) - defer retry.Stop() - for { - finished := t.MockTracer.FinishedSpans() - if len(finished) == int(atomic.LoadInt32(&t.spans)) { - return finished - } - select { - case <-retry.C: - case <-timeout: - return nil - } - } -} - func TestLifoErrors(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { time.Sleep(time.Second) @@ -508,7 +474,7 @@ func TestLifoErrors(t *testing.T) { <-rt.FirstLoad() - tracer := &testTracer{MockTracer: mocktracer.New()} + tracer := tracingtest.NewTracer() pr := proxy.WithParams(proxy.Params{ Routing: rt, OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, diff --git a/filters/tracing/tag_test.go b/filters/tracing/tag_test.go index 266b2158d3..529b7000ef 100644 --- a/filters/tracing/tag_test.go +++ b/filters/tracing/tag_test.go @@ -9,6 +9,7 @@ import ( "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/filtertest" + "github.com/zalando/skipper/tracing/tracingtest" ) func TestTracingTagNil(t *testing.T) { @@ -196,7 +197,7 @@ func TestTagCreateFilter(t *testing.T) { } func TestTracingTag(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() for _, ti := range []struct { name string diff --git a/net/httpclient_example_test.go b/net/httpclient_example_test.go index 7af71a2183..f6f02a3695 100644 --- a/net/httpclient_example_test.go +++ b/net/httpclient_example_test.go @@ -11,23 +11,12 @@ import ( "github.com/lightstep/lightstep-tracer-go" "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/sirupsen/logrus" "github.com/zalando/skipper/net" "github.com/zalando/skipper/secrets" + "github.com/zalando/skipper/tracing/tracingtest" ) -func waitForSpanViaMockTracer(mockTracer *mocktracer.MockTracer) { - for i := 0; i < 20; i++ { - if n := len(mockTracer.FinishedSpans()); n > 0 { - logrus.Printf("found %d spans", n) - return - } - time.Sleep(100 * time.Millisecond) - } - logrus.Println("no span found") -} - func ExampleTransport() { tracer := lightstep.NewTracer(lightstep.Options{}) @@ -221,7 +210,7 @@ func (t *customTracer) StartSpan(operationName string, opts ...opentracing.Start } func ExampleClient_customTracer() { - mockTracer := mocktracer.New() + mockTracer := tracingtest.NewTracer() cli := net.NewClient(net.Options{ Tracer: &customTracer{mockTracer}, OpentracingSpanName: "clientSpan", @@ -232,10 +221,6 @@ func ExampleClient_customTracer() { defer srv.Close() cli.Get("http://" + srv.Listener.Addr().String() + "/") - - // wait for the span to be finished - waitForSpanViaMockTracer(mockTracer) - fmt.Printf("customtag: %s", mockTracer.FinishedSpans()[0].Tags()["customtag"]) // Output: @@ -343,7 +328,7 @@ func ExampleClient_hostSecret() { } func ExampleClient_withBeforeSendHook() { - mockTracer := mocktracer.New() + mockTracer := tracingtest.NewTracer() peerService := "my-peer-service" cli := net.NewClient(net.Options{ Tracer: &customTracer{mockTracer}, @@ -369,10 +354,6 @@ func ExampleClient_withBeforeSendHook() { defer srv.Close() cli.Get("http://" + srv.Listener.Addr().String() + "/") - - // wait for the span to be finished - waitForSpanViaMockTracer(mockTracer) - fmt.Printf("request tag %q set to %q", string(ext.PeerService), mockTracer.FinishedSpans()[0].Tags()[string(ext.PeerService)]) // Output: @@ -381,7 +362,7 @@ func ExampleClient_withBeforeSendHook() { } func ExampleClient_withAfterResponseHook() { - mockTracer := mocktracer.New() + mockTracer := tracingtest.NewTracer() cli := net.NewClient(net.Options{ Tracer: &customTracer{mockTracer}, OpentracingComponentTag: "testclient", @@ -411,9 +392,6 @@ func ExampleClient_withAfterResponseHook() { log.Fatalf("Failed to get: %v", err) } - // wait for the span to be finished - waitForSpanViaMockTracer(mockTracer) - fmt.Printf("response code: %d\n", rsp.StatusCode) fmt.Printf("span status.code: %d", mockTracer.FinishedSpans()[0].Tags()["status.code"]) diff --git a/net/httpclient_test.go b/net/httpclient_test.go index 980bff2da8..cef3d01ad1 100644 --- a/net/httpclient_test.go +++ b/net/httpclient_test.go @@ -13,9 +13,9 @@ import ( "github.com/AlexanderYastrebov/noleak" "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/secrets" "github.com/zalando/skipper/tracing/tracers/basic" + "github.com/zalando/skipper/tracing/tracingtest" ) var testToken = []byte("mytoken1") @@ -208,7 +208,7 @@ func TestClient(t *testing.T) { } func TestTransport(t *testing.T) { - mtracer := mocktracer.New() + mtracer := tracingtest.NewTracer() tracer, err := basic.InitTracer(nil) if err != nil { t.Fatalf("Failed to get a tracer: %v", err) diff --git a/proxy/tracing_test.go b/proxy/tracing_test.go index 10c55b96eb..6e1ba1ae85 100644 --- a/proxy/tracing_test.go +++ b/proxy/tracing_test.go @@ -87,7 +87,7 @@ func TestTracingIngressSpan(t *testing.T) { routeID := "ingressRoute" doc := fmt.Sprintf(`%s: Path("/hello") -> setPath("/bye") -> setQuery("void") -> "%s"`, routeID, s.URL) - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() params := Params{ OpenTracing: &OpenTracingParams{ Tracer: tracer, @@ -117,9 +117,6 @@ func TestTracingIngressSpan(t *testing.T) { t.Fatal(err) } - // client may get response before proxy finishes span - time.Sleep(10 * time.Millisecond) - span, ok := findSpan(tracer, "ingress") if !ok { t.Fatal("ingress span not found") @@ -143,7 +140,7 @@ func TestTracingIngressSpanShunt(t *testing.T) { routeID := "ingressShuntRoute" doc := fmt.Sprintf(`%s: Path("/hello") -> setPath("/bye") -> setQuery("void") -> status(205) -> `, routeID) - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() params := Params{ OpenTracing: &OpenTracingParams{ Tracer: tracer, @@ -175,9 +172,6 @@ func TestTracingIngressSpanShunt(t *testing.T) { defer rsp.Body.Close() io.Copy(io.Discard, rsp.Body) - // client may get response before proxy finishes span - time.Sleep(10 * time.Millisecond) - span, ok := findSpan(tracer, "ingress") if !ok { t.Fatal("ingress span not found") @@ -214,7 +208,7 @@ func TestTracingIngressSpanLoopback(t *testing.T) { %s: Path("/loop2") -> setPath("/loop1") -> ; `, shuntRouteID, loop1RouteID, loop2RouteID) - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() params := Params{ OpenTracing: &OpenTracingParams{ Tracer: tracer, @@ -247,9 +241,6 @@ func TestTracingIngressSpanLoopback(t *testing.T) { io.Copy(io.Discard, rsp.Body) t.Logf("got response %d", rsp.StatusCode) - // client may get response before proxy finishes span - time.Sleep(10 * time.Millisecond) - sp, ok := findSpanByRouteID(tracer, loop2RouteID) if !ok { t.Fatalf("span for route %q not found", loop2RouteID) @@ -375,7 +366,7 @@ func TestTracingProxySpan(t *testing.T) { defer s.Close() doc := fmt.Sprintf(`hello: Path("/hello") -> setPath("/bye") -> setQuery("void") -> "%s"`, s.URL) - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() t.Setenv("HOSTNAME", "proxy.tracing.test") @@ -399,9 +390,6 @@ func TestTracingProxySpan(t *testing.T) { t.Fatal(err) } - // client may get response before proxy finishes span - time.Sleep(10 * time.Millisecond) - span, ok := findSpan(tracer, "proxy") if !ok { t.Fatal("proxy span not found") @@ -532,7 +520,7 @@ func TestFilterTracing(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tc.params.Tracer = tracer tracing := newProxyTracing(tc.params) @@ -574,7 +562,7 @@ func spanLogs(span *mocktracer.MockSpan) string { } func TestEnabledLogStreamEvents(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tracing := newProxyTracing(&OpenTracingParams{ Tracer: tracer, LogStreamEvents: true, @@ -593,7 +581,7 @@ func TestEnabledLogStreamEvents(t *testing.T) { } func TestDisabledLogStreamEvents(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tracing := newProxyTracing(&OpenTracingParams{ Tracer: tracer, LogStreamEvents: false, @@ -612,7 +600,7 @@ func TestDisabledLogStreamEvents(t *testing.T) { } func TestSetEnabledTags(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tracing := newProxyTracing(&OpenTracingParams{ Tracer: tracer, ExcludeTags: []string{}, @@ -636,7 +624,7 @@ func TestSetEnabledTags(t *testing.T) { } func TestSetDisabledTags(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tracing := newProxyTracing(&OpenTracingParams{ Tracer: tracer, ExcludeTags: []string{ @@ -668,7 +656,7 @@ func TestSetDisabledTags(t *testing.T) { } func TestLogEventWithEmptySpan(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tracing := newProxyTracing(&OpenTracingParams{ Tracer: tracer, }) @@ -679,7 +667,7 @@ func TestLogEventWithEmptySpan(t *testing.T) { } func TestSetTagWithEmptySpan(t *testing.T) { - tracer := mocktracer.New() + tracer := tracingtest.NewTracer() tracing := newProxyTracing(&OpenTracingParams{ Tracer: tracer, }) @@ -688,7 +676,7 @@ func TestSetTagWithEmptySpan(t *testing.T) { tracing.setTag(nil, "test", "val") } -func findSpan(tracer *mocktracer.MockTracer, name string) (*mocktracer.MockSpan, bool) { +func findSpan(tracer *tracingtest.MockTracer, name string) (*mocktracer.MockSpan, bool) { for _, s := range tracer.FinishedSpans() { if s.OperationName == name { return s, true @@ -697,7 +685,7 @@ func findSpan(tracer *mocktracer.MockTracer, name string) (*mocktracer.MockSpan, return nil, false } -func findSpanByRouteID(tracer *mocktracer.MockTracer, routeID string) (*mocktracer.MockSpan, bool) { +func findSpanByRouteID(tracer *tracingtest.MockTracer, routeID string) (*mocktracer.MockSpan, bool) { for _, s := range tracer.FinishedSpans() { if s.Tag(SkipperRouteIDTag) == routeID { return s, true diff --git a/tracing/tracingtest/mocktracer.go b/tracing/tracingtest/mocktracer.go new file mode 100644 index 0000000000..9082c81a9f --- /dev/null +++ b/tracing/tracingtest/mocktracer.go @@ -0,0 +1,53 @@ +package tracingtest + +import ( + "sync/atomic" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" +) + +type MockTracer struct { + mockTracer *mocktracer.MockTracer + spans atomic.Int32 +} + +func NewTracer() *MockTracer { + return &MockTracer{mockTracer: mocktracer.New()} +} + +func (t *MockTracer) Reset() { + t.spans.Store(0) + t.mockTracer.Reset() +} + +func (t *MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { + t.spans.Add(1) + return t.mockTracer.StartSpan(operationName, opts...) +} + +func (t *MockTracer) FinishedSpans() []*mocktracer.MockSpan { + timeout := time.After(1 * time.Second) + retry := time.NewTicker(100 * time.Millisecond) + defer retry.Stop() + for { + finished := t.mockTracer.FinishedSpans() + if len(finished) == int(t.spans.Load()) { + return finished + } + select { + case <-retry.C: + case <-timeout: + return nil + } + } +} + +func (t *MockTracer) Inject(sm opentracing.SpanContext, format any, carrier any) error { + return t.mockTracer.Inject(sm, format, carrier) +} + +func (t *MockTracer) Extract(format any, carrier any) (opentracing.SpanContext, error) { + return t.mockTracer.Extract(format, carrier) +} From 5bf1c64d75697a6db720c5b24d6c051a09fe0681 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:49:50 +0100 Subject: [PATCH 0963/1047] build(deps): bump docker/build-push-action from 6.9.0 to 6.10.0 (#3326) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.9.0 to 6.10.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/4f58ea79222b3b9dc2c8bbdd6debcef730109a75...48aba3b46d1b1fec4febb7c5d0c644b249a11355) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index d1839b144f..d1585ad9e1 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From e2973de900a5bafbb9eb1dc4b435a2714ae90f7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:50:23 +0100 Subject: [PATCH 0964/1047] build(deps): bump actions/setup-go from 5.0.2 to 5.1.0 (#3289) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.2 to 5.1.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32...41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c81f9103eb..dbed0eb085 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: fetch-depth: 2 - name: Setup Go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 + uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed with: go-version: '^1.21' check-latest: true diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index d1585ad9e1..0ee8d8df1b 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -25,7 +25,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.21' diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 4262a6efe4..449339710f 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,7 +13,7 @@ jobs: contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index a7feffc336..87ca2ca35f 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" From c7fbb80082e349b6542ebe71fcd8e53a117605c3 Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Fri, 6 Dec 2024 09:57:23 +0100 Subject: [PATCH 0965/1047] ingress docs: add a note to highlight how `zalando.org/skipper-predicate` & `zalando.org/skipper-routes` work together (#3323) Signed-off-by: Mustafa Abdelrahman --- docs/kubernetes/ingress-usage.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/kubernetes/ingress-usage.md b/docs/kubernetes/ingress-usage.md index d8d96fea7b..99d6f0dd8a 100644 --- a/docs/kubernetes/ingress-usage.md +++ b/docs/kubernetes/ingress-usage.md @@ -270,6 +270,8 @@ You will get an error in Skipper logs, similar to: [APP]time="2019-01-02T13:30:16Z" level=error msg="Failed to add route having 2 path routes: Path(\"/foo/bar\") -> inlineContent(\"custom route\") -> status(200) -> " ``` +> **Note:** Predicates coming from `zalando.org/skipper-predicate` won't be appended to routes generated from `zalando.org/skipper-routes` + ### Redirects #### Overwrite the current ingress with a redirect From d8d1bbf249cf3f0cdcfcc5efb0678dc024754bd2 Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Fri, 6 Dec 2024 15:42:19 +0100 Subject: [PATCH 0966/1047] dependabot: ingore OPA updates (#3334) We decided to ignore automatically updating OPA dependencies. It was already done in https://github.com/zalando/skipper/pull/3332#issuecomment-2522944619 & https://github.com/zalando/skipper/pull/3320#issuecomment-2522931582 This PR is to add visibility and we will run `@dependabot unignore ...` after merging. See [`ignore` docs](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#ignore) Signed-off-by: Mustafa Abdelrahman --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a32cd454e0..045892bd1d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,9 @@ updates: all-go-mod-patch-and-minor: patterns: ["*"] update-types: ["patch", "minor"] + ignore: + - dependency-name: "github.com/open-policy-agent/opa" + - dependency-name: "github.com/open-policy-agent/opa-envoy-plugin" - package-ecosystem: "github-actions" directory: "/" # For GitHub Actions, set the directory to / to check for workflow files in .github/workflows schedule: From 010c033accc33e8a79e8eca9a583e74e0d480db3 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 10 Dec 2024 17:09:23 +0100 Subject: [PATCH 0967/1047] predicates/traffic: deflake TestTrafficSegmentSplit (#3330) Use fixed random sequence to deflake TestTrafficSegmentSplit. Fixes #2665 Signed-off-by: Alexander Yastrebov --- predicates/traffic/export_test.go | 11 +++++++++++ predicates/traffic/rand_test.go | 22 ++++++++++++++++++++++ predicates/traffic/segment.go | 17 +++++++++++------ predicates/traffic/segment_test.go | 4 +++- 4 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 predicates/traffic/rand_test.go diff --git a/predicates/traffic/export_test.go b/predicates/traffic/export_test.go index d1d32b2be2..b09d373e0e 100644 --- a/predicates/traffic/export_test.go +++ b/predicates/traffic/export_test.go @@ -1,3 +1,14 @@ package traffic +import "github.com/zalando/skipper/routing" + var ExportRandomValue = randomValue + +func WithRandFloat64(ps routing.PredicateSpec, randFloat64 func() float64) routing.PredicateSpec { + if s, ok := ps.(*segmentSpec); ok { + s.randFloat64 = randFloat64 + } else { + panic("invalid predicate spec, expected *segmentSpec") + } + return ps +} diff --git a/predicates/traffic/rand_test.go b/predicates/traffic/rand_test.go new file mode 100644 index 0000000000..d7ede7b11a --- /dev/null +++ b/predicates/traffic/rand_test.go @@ -0,0 +1,22 @@ +package traffic_test + +import ( + "math/rand/v2" + "sync" +) + +// newTestRandFloat64 returns a function that generates fixed sequence of random float64 values for testing. +func newTestRandFloat64() func() float64 { + return rand.New(&lockedSource{s: rand.NewPCG(0x5EED_1, 0x5EED_2)}).Float64 +} + +type lockedSource struct { + mu sync.Mutex + s rand.Source +} + +func (s *lockedSource) Uint64() uint64 { + s.mu.Lock() + defer s.mu.Unlock() + return s.s.Uint64() +} diff --git a/predicates/traffic/segment.go b/predicates/traffic/segment.go index f9dc323860..799fd10c0a 100644 --- a/predicates/traffic/segment.go +++ b/predicates/traffic/segment.go @@ -9,8 +9,13 @@ import ( ) type ( - segmentSpec struct{} - segmentPredicate struct{ min, max float64 } + segmentSpec struct { + randFloat64 func() float64 + } + segmentPredicate struct { + randFloat64 func() float64 + min, max float64 + } ) type contextKey struct{} @@ -19,7 +24,7 @@ var randomValue contextKey // NewSegment creates a new traffic segment predicate specification func NewSegment() routing.WeightedPredicateSpec { - return &segmentSpec{} + return &segmentSpec{rand.Float64} } func (*segmentSpec) Name() string { @@ -42,12 +47,12 @@ func (*segmentSpec) Name() string { // r50: Path("/test") && TrafficSegment(0.0, 0.5) -> ; // r30: Path("/test") && TrafficSegment(0.5, 0.8) -> ; // r20: Path("/test") && TrafficSegment(0.8, 1.0) -> ; -func (*segmentSpec) Create(args []any) (routing.Predicate, error) { +func (s *segmentSpec) Create(args []any) (routing.Predicate, error) { if len(args) != 2 { return nil, predicates.ErrInvalidPredicateParameters } - p, ok := &segmentPredicate{}, false + p, ok := &segmentPredicate{randFloat64: s.randFloat64}, false if p.min, ok = args[0].(float64); !ok || p.min < 0 || p.min > 1 { return nil, predicates.ErrInvalidPredicateParameters @@ -72,7 +77,7 @@ func (*segmentSpec) Weight() int { } func (p *segmentPredicate) Match(req *http.Request) bool { - r := routing.FromContext(req.Context(), randomValue, rand.Float64) + r := routing.FromContext(req.Context(), randomValue, p.randFloat64) // min == max defines a never-matching interval and always yields false return p.min <= r && r < p.max } diff --git a/predicates/traffic/segment_test.go b/predicates/traffic/segment_test.go index 87e93397a8..78e005f205 100644 --- a/predicates/traffic/segment_test.go +++ b/predicates/traffic/segment_test.go @@ -148,7 +148,9 @@ func TestTrafficSegmentSplit(t *testing.T) { RoutingOptions: routing.Options{ FilterRegistry: builtin.MakeRegistry(), Predicates: []routing.PredicateSpec{ - traffic.NewSegment(), + // Use fixed random sequence to deflake the test, + // see https://github.com/zalando/skipper/issues/2665 + traffic.WithRandFloat64(traffic.NewSegment(), newTestRandFloat64()), }, }, Routes: eskip.MustParse(` From f5afdbabcc4fa4b534cc3519b4cdf99410e80906 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 12 Dec 2024 12:01:00 +0100 Subject: [PATCH 0968/1047] proxy: always count route lookup failures (#3341) Remove debug mode check for clarity and because other metrics are updated without checking for debug mode. Signed-off-by: Alexander Yastrebov --- proxy/proxy.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 5c4fe9faca..a024a2cc12 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1211,10 +1211,7 @@ func (p *Proxy) do(ctx *context, parentSpan ot.Span) (err error) { route, params := p.lookupRoute(ctx) p.metrics.MeasureRouteLookup(lookupStart) if route == nil { - if !p.flags.Debug() { - p.metrics.IncRoutingFailures() - } - + p.metrics.IncRoutingFailures() ctx.Logger().Debugf("could not find a route for %v", ctx.request.URL) p.makeErrorResponse(ctx, errRouteLookupFailed) return errRouteLookupFailed From 4cfec0324ad4e3d1398d266867f018a8aa83d7a0 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 13 Dec 2024 09:42:30 +0100 Subject: [PATCH 0969/1047] docs: add a note about -proxy-preserve-host flag (#3344) Add a note about the flag to `preserveHost` filter docs. Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 015f35d185..3f2e2d5548 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -115,6 +115,8 @@ Example: route1: * -> preserveHost("true") -> "http://backend.example.org"; ``` +Default `proxyPreserveHost` behavior can be configured by `-proxy-preserve-host` flag which is `false` by default. + ### modRequestHeader Replace all matched regex expressions in the given header. From b1326ffd06ee6f24c9ce416ffaa3ae540f190f3b Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 16 Dec 2024 21:41:09 +0100 Subject: [PATCH 0970/1047] docs: add example for tracingTagFromResponseIfStatus (#3348) Add an example using template placeholder for the tag value. Follow up on #3202 Signed-off-by: Alexander Yastrebov --- docs/reference/filters.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/reference/filters.md b/docs/reference/filters.md index 3f2e2d5548..46d58ebddd 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -3361,16 +3361,26 @@ tracingTag("http.flow_id", "${request.header.X-Flow-Id}") ### tracingTagFromResponse -This filter works just like [tracingTag](#tracingtag), but is applied after the request was processed. In particular, [template placeholders](#template-placeholders) referencing the response can be used in the parameters. +This filter works just like [tracingTag](#tracingtag), but is applied after the request was processed. +In particular, [template placeholders](#template-placeholders) referencing the response can be used in the tag value. ### tracingTagFromResponseIfStatus -Example: set error tag to true in case response status code is `>= 500` and `<= 599` +This filter works like [tracingTagFromResponse](#tracingtagfromresponse) +but only for responses having status code from the specified range. + +Example: set error tag to true in case response status code is `>= 500` and `<= 599`: ``` tracingTagFromResponseIfStatus("error", "true", 500, 599) ``` +Example: set user id tag for ratelimited requests: + +``` +tracingTagFromResponseIfStatus("user.id", "${request.header.X-User-Id}", 429, 429) -> clusterClientRatelimit("user-id", 10, "1m", "X-User-Id") +``` + ### tracingSpanName From 0e1938af03659b8d462fe32d9cceb74597d4396f Mon Sep 17 00:00:00 2001 From: Roman Zavodskikh Date: Tue, 17 Dec 2024 14:28:26 +0100 Subject: [PATCH 0971/1047] Add more tags to the admissionControl span to increase visibility (#3318) Updates https://github.com/zalando/skipper/issues/3317 Signed-off-by: Roman Zavodskikh --- filters/shedder/admission.go | 9 ++++ filters/shedder/admission_test.go | 90 +++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/filters/shedder/admission.go b/filters/shedder/admission.go index 80d63e49d0..95fd0bffd6 100644 --- a/filters/shedder/admission.go +++ b/filters/shedder/admission.go @@ -370,6 +370,13 @@ func sum(a []int64) int64 { return result } +func (ac *admissionControl) setCommonTags(span opentracing.Span) { + span.SetTag("admissionControl.group", ac.metricSuffix) + span.SetTag("admissionControl.mode", ac.mode.String()) + span.SetTag("admissionControl.duration", ac.d.String()) + span.SetTag("admissionControl.windowSize", ac.windowSize) +} + // calculates P_{reject} see https://opensource.zalando.com/skipper/reference/filters/#admissioncontrol func (ac *admissionControl) pReject() float64 { var rejectP float64 @@ -412,6 +419,8 @@ func (ac *admissionControl) shouldReject() bool { func (ac *admissionControl) Request(ctx filters.FilterContext) { span := ac.startSpan(ctx.Request().Context()) defer span.Finish() + + ac.setCommonTags(span) ac.metrics.IncCounter(counterPrefix + "total." + ac.metricSuffix) if ac.shouldReject() { diff --git a/filters/shedder/admission_test.go b/filters/shedder/admission_test.go index 0377feedf7..6563319ba5 100644 --- a/filters/shedder/admission_test.go +++ b/filters/shedder/admission_test.go @@ -13,15 +13,18 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/builtin" "github.com/zalando/skipper/metrics" "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/net" + "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/proxy/proxytest" "github.com/zalando/skipper/routing" "github.com/zalando/skipper/routing/testdataclient" + "github.com/zalando/skipper/tracing/tracingtest" "golang.org/x/time/rate" ) @@ -594,6 +597,93 @@ func TestAdmissionControlCleanupMultipleFilters(t *testing.T) { } } +func TestAdmissionControlSetsSpansTags(t *testing.T) { + tracer := tracingtest.NewTracer() + + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer backend.Close() + + fspec := NewAdmissionControl(Options{Tracer: tracer}) + spec, ok := fspec.(*AdmissionControlSpec) + if !ok { + t.Fatal("FilterSpec is not a AdmissionControlSpec") + } + preProcessor := spec.PreProcessor() + postProcessor := spec.PostProcessor() + + args := make([]interface{}, 0, 6) + args = append(args, "testmetric", "active", "10ms", 5, 1, 0.1, 0.95, 0.5) + f, err := spec.CreateFilter(args) + if err != nil { + t.Fatalf("error creating filter: %v", err) + return + } + f.(*admissionControl).Close() + + r := &eskip.Route{Filters: []*eskip.Filter{{Name: spec.Name(), Args: args}}, Backend: backend.URL} + + fr := make(filters.Registry) + fr.Register(fspec) + + dc := testdataclient.New([]*eskip.Route{r}) + defer dc.Close() + + proxy := proxytest.WithParamsAndRoutingOptions(fr, + proxy.Params{ + OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, + }, + routing.Options{ + DataClients: []routing.DataClient{dc}, + PostProcessors: []routing.PostProcessor{postProcessor}, + PreProcessors: []routing.PreProcessor{preProcessor}, + }) + defer proxy.Close() + + client := proxy.Client() + req, err := http.NewRequest("GET", proxy.URL, nil) + if err != nil { + t.Error(err) + return + } + + _, err = client.Do(req) + if err != nil { + t.Error(err) + } + + acSpanFound := false + finishedSpans := tracer.FinishedSpans() + for _, span := range finishedSpans { + if span.OperationName == "admission_control" { + acSpanFound = true + tags := span.Tags() + + duration, found := tags["admissionControl.duration"] + assert.True(t, found) + assert.Equal(t, "10ms", duration) + + windowSize, found := tags["admissionControl.windowSize"] + assert.True(t, found) + assert.Equal(t, 5, windowSize) + + group, found := tags["admissionControl.group"] + assert.True(t, found) + assert.Equal(t, "testmetric", group) + + mode, found := tags["admissionControl.mode"] + assert.True(t, found) + assert.Equal(t, "active", mode) + + mode, found = tags["mode"] + assert.True(t, found) + assert.Equal(t, "active", mode) + } + } + assert.True(t, acSpanFound) +} + type validationPostProcessorNumberOfFilters struct { ch chan []*routing.Route } From 10820e2f21307149baf9fed0c8bfae9f5bb40058 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:42:29 +0100 Subject: [PATCH 0972/1047] build(deps): bump actions/setup-go from 5.1.0 to 5.2.0 (#3347) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.1.0 to 5.2.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed...3041bf56c941b39c61721a86cd11f3bb1338122a) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index dbed0eb085..5a7462e3e8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: fetch-depth: 2 - name: Setup Go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed + uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a with: go-version: '^1.21' check-latest: true diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 0ee8d8df1b..1a73954f9d 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -25,7 +25,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed + - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.21' diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 449339710f..5a7607171c 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,7 +13,7 @@ jobs: contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed + - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 87ca2ca35f..df7ea8351c 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed + - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed + - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" From a2c819392a0ba04c277d107d9087fd0dfb0b3edb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:42:50 +0100 Subject: [PATCH 0973/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3345) Bumps oss-fuzz-base/base-builder-go from `b3111d8` to `9c585d0`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 74ad765330..8152188532 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:b3111d8c1f679c67e40b14fd839c5e1c5d0fc2b5bcb08f42c7ac9323599ce308 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:9c585d0d258966044f98638fc18812b007400ca14c6a610a92af2aade12f3cb6 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From a882d48e876d9ef480dce22966aa902bbcc43c20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:43:58 +0100 Subject: [PATCH 0974/1047] build(deps): bump amazonlinux from `3dfca23` to `5cb6ab1` in /fuzz (#3337) Bumps amazonlinux from `3dfca23` to `5cb6ab1`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 7131e784fd..1ff2bf3c2b 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:3dfca23bb3b578c2807053e1a7b2db6a3622e9c147be79e5c2ea3a4569caafb3 +FROM amazonlinux:2023@sha256:5cb6ab1a1a13f33425b6c660a45f06298e204a22df1f9eeefe69cda3735d9757 WORKDIR /workspace From 01893e16d01db6a627be41ba4074bdc112fbe323 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:44:45 +0100 Subject: [PATCH 0975/1047] build(deps): bump golang.org/x/crypto in the go_modules group (#3342) Bumps the go_modules group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto). Updates `golang.org/x/crypto` from 0.27.0 to 0.31.0 - [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production dependency-group: go_modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 728ccb10c8..5a331cc3af 100644 --- a/go.mod +++ b/go.mod @@ -48,12 +48,12 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.29.0 golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.8.0 - golang.org/x/term v0.24.0 + golang.org/x/sync v0.10.0 + golang.org/x/term v0.27.0 golang.org/x/time v0.6.0 google.golang.org/protobuf v1.34.2 gopkg.in/go-jose/go-jose.v2 v2.6.3 @@ -171,8 +171,8 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect diff --git a/go.sum b/go.sum index 5836018b9f..1a3296fae3 100644 --- a/go.sum +++ b/go.sum @@ -532,8 +532,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -584,8 +584,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -613,15 +613,15 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 0958647bba7dbc23824d32721a89459dd146020d Mon Sep 17 00:00:00 2001 From: Mustafa Abdelrahman Date: Thu, 19 Dec 2024 12:40:37 +0100 Subject: [PATCH 0976/1047] dependabot: ignore another OPA dependency update (#3351) follow up on https://github.com/zalando/skipper/pull/3334 - [ ] unignore `github.com/envoyproxy/go-control-plane` Signed-off-by: Mustafa Abdelrahman --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 045892bd1d..8f00f48a3a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,7 @@ updates: ignore: - dependency-name: "github.com/open-policy-agent/opa" - dependency-name: "github.com/open-policy-agent/opa-envoy-plugin" + - dependency-name: "github.com/envoyproxy/go-control-plane" - package-ecosystem: "github-actions" directory: "/" # For GitHub Actions, set the directory to / to check for workflow files in .github/workflows schedule: From b2f0aa4db26a4ffb6ebd379abb0a59bb438908bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:10:31 +0100 Subject: [PATCH 0977/1047] build(deps): bump alpine from `1e42bbe` to `21dc606` in /packaging (#3335) --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 4f6cb4853d..5248307777 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:1e42bbe2508154c9126d48c2b8a75420c3544343bf86fd041fb7527e017a4b4a +FROM --platform=linux/arm64 alpine@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index 400242dfbb..8fe85748e6 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:1e42bbe2508154c9126d48c2b8a75420c3544343bf86fd041fb7527e017a4b4a +FROM --platform=linux/arm/v7 alpine@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 1a0906d08bb4b2dfa38125e21a76ca297516f81c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:11:43 +0100 Subject: [PATCH 0978/1047] build(deps): bump actions/cache from 4.1.2 to 4.2.0 (#3338) --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index f73721d257..9323ed8771 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b with: python-version: 3.x - - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: key: ${{ github.ref }} path: .cache From d7a6cea6ddafae763361b945169dd28c86674a10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:57:48 +0100 Subject: [PATCH 0979/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 11 updates (#3350) Bumps the all-go-mod-patch-and-minor group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/andybalholm/brotli](https://github.com/andybalholm/brotli) | `1.1.0` | `1.1.1` | | [github.com/instana/go-sensor](https://github.com/instana/go-sensor) | `1.65.0` | `1.66.0` | | [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) | `1.20.4` | `1.20.5` | | [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) | `9.6.1` | `9.7.0` | | [github.com/stretchr/testify](https://github.com/stretchr/testify) | `1.9.0` | `1.10.0` | | [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) | `0.33.0` | `0.34.0` | | [github.com/tidwall/gjson](https://github.com/tidwall/gjson) | `1.17.3` | `1.18.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.29.0` | `0.33.0` | | [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.23.0` | `0.24.0` | | [golang.org/x/time](https://github.com/golang/time) | `0.6.0` | `0.8.0` | | google.golang.org/protobuf | `1.34.2` | `1.36.0` | Updates `github.com/andybalholm/brotli` from 1.1.0 to 1.1.1 - [Commits](https://github.com/andybalholm/brotli/compare/v1.1.0...v1.1.1) Updates `github.com/instana/go-sensor` from 1.65.0 to 1.66.0 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/CHANGELOG.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.65.0...v1.66.0) Updates `github.com/prometheus/client_golang` from 1.20.4 to 1.20.5 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.4...v1.20.5) Updates `github.com/redis/go-redis/v9` from 9.6.1 to 9.7.0 - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.6.1...v9.7.0) Updates `github.com/stretchr/testify` from 1.9.0 to 1.10.0 - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0) Updates `github.com/testcontainers/testcontainers-go` from 0.33.0 to 0.34.0 - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.33.0...v0.34.0) Updates `github.com/tidwall/gjson` from 1.17.3 to 1.18.0 - [Commits](https://github.com/tidwall/gjson/compare/v1.17.3...v1.18.0) Updates `golang.org/x/net` from 0.29.0 to 0.33.0 - [Commits](https://github.com/golang/net/compare/v0.29.0...v0.33.0) Updates `golang.org/x/oauth2` from 0.23.0 to 0.24.0 - [Commits](https://github.com/golang/oauth2/compare/v0.23.0...v0.24.0) Updates `golang.org/x/time` from 0.6.0 to 0.8.0 - [Commits](https://github.com/golang/time/compare/v0.6.0...v0.8.0) Updates `google.golang.org/protobuf` from 1.34.2 to 1.36.0 --- updated-dependencies: - dependency-name: github.com/andybalholm/brotli dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/tidwall/gjson dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/time dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 24 ++++++++++++------------ go.sum | 50 ++++++++++++++++++++++++++------------------------ 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/go.mod b/go.mod index 5a331cc3af..88b1674948 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( github.com/AlexanderYastrebov/noleak v0.0.0-20230711175737-345842f874fb github.com/MicahParks/keyfunc v1.9.0 github.com/abbot/go-http-auth v0.4.0 - github.com/andybalholm/brotli v1.1.0 + github.com/andybalholm/brotli v1.1.1 github.com/aryszka/jobqueue v0.0.3 github.com/cenkalti/backoff v2.2.1+incompatible github.com/cespare/xxhash/v2 v2.3.0 @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.1 - github.com/instana/go-sensor v1.65.0 + github.com/instana/go-sensor v1.66.0 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 @@ -31,17 +31,17 @@ require ( github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4 github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.20.4 + github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.6.1 + github.com/redis/go-redis/v9 v9.7.0 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/szuecs/rate-limit-buffer v0.9.0 - github.com/testcontainers/testcontainers-go v0.33.0 - github.com/tidwall/gjson v1.17.3 + github.com/testcontainers/testcontainers-go v0.34.0 + github.com/tidwall/gjson v1.18.0 github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible @@ -50,12 +50,12 @@ require ( go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.29.0 - golang.org/x/oauth2 v0.23.0 + golang.org/x/net v0.33.0 + golang.org/x/oauth2 v0.24.0 golang.org/x/sync v0.10.0 golang.org/x/term v0.27.0 - golang.org/x/time v0.6.0 - google.golang.org/protobuf v1.34.2 + golang.org/x/time v0.8.0 + google.golang.org/protobuf v1.36.0 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf @@ -82,7 +82,7 @@ require ( github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect - github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/badger/v3 v3.2103.5 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect diff --git a/go.sum b/go.sum index 1a3296fae3..dbad187363 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -91,8 +91,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= -github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -257,8 +257,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.65.0 h1:tvghnyNsSE2Bj1nsARLwLFc7GIR9+NXN7QAjMRw6EHg= -github.com/instana/go-sensor v1.65.0/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= +github.com/instana/go-sensor v1.66.0 h1:4ugORxo+iNmmSh6a3zmpAdXOJTOWvywSN0zRgLbhAts= +github.com/instana/go-sensor v1.66.0/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -383,8 +383,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -402,8 +402,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= -github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -455,16 +455,16 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/szuecs/rate-limit-buffer v0.9.0 h1:65fBCVsaJFh0E1G5C6/sInEPlYR6dXtF9J9bAv48lLg= github.com/szuecs/rate-limit-buffer v0.9.0/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= -github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= -github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= -github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= @@ -486,6 +486,8 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yookoala/gofast v0.8.0 h1:UmGTeBj2EF5gvS58ByE9HFdQ9MeYSUIwf7JN9aFno3Y= @@ -571,11 +573,11 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -622,8 +624,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -682,8 +684,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From 95ef24f6c2d0e134e879d0011e18c9c22e1eca2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:58:16 +0100 Subject: [PATCH 0980/1047] build(deps): bump actions/upload-artifact from 4.4.3 to 4.5.0 (#3354) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.3 to 4.5.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882...6f51ac03b9356f520e9adb1b1b7802705f340c2b) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 8a00e1aa85..d1c2636337 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: SARIF file path: results.sarif From 42d86a3b07a38e82009b0f40a5beb0d7718d74a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:58:41 +0100 Subject: [PATCH 0981/1047] build(deps): bump docker/setup-buildx-action from 3.7.1 to 3.8.0 (#3353) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.7.1 to 3.8.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/c47758b77c9736f4b2ef4073d4d51994fabfe349...6524bf65af31da8d45b59e8c27de4bd072b392f5) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 1a73954f9d..4907fd1ffe 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From 4d5c3ecdd370d2adc7380f3e245d8497919a9b8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:04:01 +0100 Subject: [PATCH 0982/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3352) Bumps oss-fuzz-base/base-builder-go from `9c585d0` to `bab7704`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 8152188532..9ab821c273 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:9c585d0d258966044f98638fc18812b007400ca14c6a610a92af2aade12f3cb6 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:bab77046ede6fae6f9ab931ada4b17a5adaf71842ac93e32300d06d5b9829891 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From f2514bdaf5d4c2ca9d757b6b96f25196dea9a518 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:55:58 +0100 Subject: [PATCH 0983/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3358) Bumps oss-fuzz-base/base-builder-go from `bab7704` to `9a47ebe`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 9ab821c273..9c779f62f6 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:bab77046ede6fae6f9ab931ada4b17a5adaf71842ac93e32300d06d5b9829891 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:9a47ebe4dc1d194ed95889081b5802d77616151da508cb1f62f7857d7f3c4f00 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 81eea5bd86ec117f37c15b8e3fc10d70b1788b38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:09:27 +0100 Subject: [PATCH 0984/1047] build(deps): bump amazonlinux from `5cb6ab1` to `1b09180` in /fuzz (#3357) Bumps amazonlinux from `5cb6ab1` to `1b09180`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 1ff2bf3c2b..b86df9f9a5 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:5cb6ab1a1a13f33425b6c660a45f06298e204a22df1f9eeefe69cda3735d9757 +FROM amazonlinux:2023@sha256:1b091808a98a0a1b0570073421866f5a76adfa43e8d7a42b2f5dd0c60053a567 WORKDIR /workspace From 44139f3a87d0fb455d9564af5207546f25ee622a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:10:01 +0100 Subject: [PATCH 0985/1047] build(deps): bump google.golang.org/protobuf (#3359) Bumps the all-go-mod-patch-and-minor group with 1 update: google.golang.org/protobuf. Updates `google.golang.org/protobuf` from 1.36.0 to 1.36.1 --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 88b1674948..dca2cfeb24 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/term v0.27.0 golang.org/x/time v0.8.0 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf diff --git a/go.sum b/go.sum index dbad187363..64968e8bbb 100644 --- a/go.sum +++ b/go.sum @@ -684,8 +684,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From b54f0f128a544f64a7c0a94affd1402f7b208ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Sur=C3=A9e?= Date: Mon, 6 Jan 2025 19:26:30 +0100 Subject: [PATCH 0986/1047] oid_introspection: add SetOIDCClaims (#3311) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method allows third-party filters to set the oidcClaimsCacheKey which enables the use of the oidcClaimsQuery filter. Signed-off-by: Adrien Surée Co-authored-by: Adrien Surée --- filters/auth/grant.go | 6 +----- filters/auth/grant_test.go | 2 ++ filters/auth/oidc_introspection.go | 8 ++++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/filters/auth/grant.go b/filters/auth/grant.go index a828caab78..9344082326 100644 --- a/filters/auth/grant.go +++ b/filters/auth/grant.go @@ -175,11 +175,7 @@ func (f *grantFilter) setupToken(token *oauth2.Token, tokeninfo map[string]inter // By piggy-backing on the OIDC token container, // we gain downstream compatibility with the oidcClaimsQuery filter. - ctx.StateBag()[oidcClaimsCacheKey] = tokenContainer{ - OAuth2Token: token, - Subject: subject, - Claims: tokeninfo, - } + SetOIDCClaims(ctx, tokeninfo) // Set the tokeninfo also in the tokeninfoCacheKey state bag, so we // can reuse e.g. the forwardToken() filter. diff --git a/filters/auth/grant_test.go b/filters/auth/grant_test.go index 70270e73ca..18818f96cb 100644 --- a/filters/auth/grant_test.go +++ b/filters/auth/grant_test.go @@ -210,6 +210,7 @@ func newAuthProxy(t *testing.T, config *auth.OAuthConfig, routes []*eskip.Route, fr.Register(config.NewGrantCallback()) fr.Register(config.NewGrantClaimsQuery()) fr.Register(config.NewGrantLogout()) + fr.Register(auth.NewOIDCQueryClaimsFilter()) pc := proxytest.Config{ RoutingOptions: routing.Options{ @@ -331,6 +332,7 @@ func TestGrantFlow(t *testing.T) { config := newGrantTestConfig(tokeninfo.URL, provider.URL) routes := eskip.MustParse(`* -> oauthGrant() + -> oidcClaimsQuery("/:sub") -> status(204) -> setResponseHeader("Backend-Request-Cookie", "${request.header.Cookie}") -> diff --git a/filters/auth/oidc_introspection.go b/filters/auth/oidc_introspection.go index a32752a7c0..8d21dc4ec1 100644 --- a/filters/auth/oidc_introspection.go +++ b/filters/auth/oidc_introspection.go @@ -42,6 +42,14 @@ func NewOIDCQueryClaimsFilter() filters.Spec { } } +// Sets OIDC claims in the state bag. +// Intended for use with the oidcClaimsQuery filter. +func SetOIDCClaims(ctx filters.FilterContext, claims map[string]interface{}) { + ctx.StateBag()[oidcClaimsCacheKey] = tokenContainer{ + Claims: claims, + } +} + func (spec *oidcIntrospectionSpec) Name() string { switch spec.typ { case checkOIDCQueryClaims: From 0d46d424b7d6e84c869f5147c8550ea4da78539d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 00:45:52 +0100 Subject: [PATCH 0987/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3361) Bumps oss-fuzz-base/base-builder-go from `9a47ebe` to `9bf7fad`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 9c779f62f6..f143bd87b9 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:9a47ebe4dc1d194ed95889081b5802d77616151da508cb1f62f7857d7f3c4f00 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:9bf7fad8ca02443224c7518392d80c97a62b8cb0822f03aadf9193a7e27346f0 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 94719ea8828cb523637d17d79c7fff2ca961ab6a Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 7 Jan 2025 10:48:54 +0100 Subject: [PATCH 0988/1047] filters/auth: use sync.Map for tokeninfo cache (#3267) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use sync.Map for tokeninfo cache to avoid synchronizing all callers on a single mutex * evict stale entries periodically instead of least recently used * store token expiration time instead of creation time ``` │ master │ HEAD │ │ sec/op │ sec/op vs base │ TokeninfoCache/tokens=1,cacheSize=1,p=0-8 275.5n ± 6% 170.1n ± 4% -38.26% (p=0.000 n=10) TokeninfoCache/tokens=2,cacheSize=2,p=0-8 492.9n ± 21% 176.8n ± 2% -64.12% (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=100,p=0-8 455.9n ± 7% 165.5n ± 1% -63.70% (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=100,p=10000-8 593.4n ± 4% 179.8n ± 4% -69.71% (p=0.000 n=10) TokeninfoCache/tokens=4,cacheSize=2,p=0-8 2571424.0n ± 0% 149.7n ± 3% -99.99% (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=10,p=0-8 2579227.5n ± 0% 139.3n ± 1% -99.99% (p=0.000 n=10) geomean 7.903µ 162.9n -97.94% │ master │ HEAD │ │ B/op │ B/op vs base │ TokeninfoCache/tokens=1,cacheSize=1,p=0-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=2,cacheSize=2,p=0-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=0-8 344.0 ± 0% 344.0 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=10000-8 368.0 ± 1% 350.0 ± 0% -4.89% (p=0.000 n=10) TokeninfoCache/tokens=4,cacheSize=2,p=0-8 27.00 ± 0% 344.00 ± 0% +1174.07% (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=10,p=0-8 27.00 ± 7% 344.00 ± 0% +1174.07% (p=0.000 n=10) geomean 149.0 345.0 +131.62% ¹ all samples are equal │ master │ HEAD │ │ allocs/op │ allocs/op vs base │ TokeninfoCache/tokens=1,cacheSize=1,p=0-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=2,cacheSize=2,p=0-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=0-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=100,cacheSize=100,p=10000-8 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TokeninfoCache/tokens=4,cacheSize=2,p=0-8 0.000 ± 0% 3.000 ± 0% ? (p=0.000 n=10) TokeninfoCache/tokens=100,cacheSize=10,p=0-8 0.000 ± 0% 3.000 ± 0% ? (p=0.000 n=10) geomean ² 3.000 ? ¹ all samples are equal ² summaries must be >0 to compute geomean ``` Signed-off-by: Alexander Yastrebov --- filters/auth/authclient.go | 1 + filters/auth/main_test.go | 6 +- filters/auth/tokeninfo.go | 6 +- filters/auth/tokeninfocache.go | 149 +++++++++++++++------------- filters/auth/tokeninfocache_test.go | 34 +++++-- skipper.go | 1 + 6 files changed, 114 insertions(+), 83 deletions(-) diff --git a/filters/auth/authclient.go b/filters/auth/authclient.go index ca5463eade..ccc71a1ef5 100644 --- a/filters/auth/authclient.go +++ b/filters/auth/authclient.go @@ -32,6 +32,7 @@ type authClient struct { type tokeninfoClient interface { getTokeninfo(token string, ctx filters.FilterContext) (map[string]any, error) + Close() } var _ tokeninfoClient = &authClient{} diff --git a/filters/auth/main_test.go b/filters/auth/main_test.go index 4da87fe2a4..3df902c5e3 100644 --- a/filters/auth/main_test.go +++ b/filters/auth/main_test.go @@ -18,11 +18,7 @@ func TestMain(m *testing.M) { func cleanupAuthClients() { for _, c := range tokeninfoAuthClient { - if ac, ok := c.(*authClient); ok { - ac.Close() - } else if cc, ok := c.(*tokeninfoCache); ok { - cc.client.(*authClient).Close() - } + c.Close() } for _, c := range issuerAuthClient { diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index 53323012cb..f12f24d868 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -12,6 +12,7 @@ import ( "github.com/opentracing/opentracing-go" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/annotate" + "github.com/zalando/skipper/metrics" ) const ( @@ -32,9 +33,10 @@ type TokeninfoOptions struct { Timeout time.Duration MaxIdleConns int Tracer opentracing.Tracer + Metrics metrics.Metrics // CacheSize configures the maximum number of cached tokens. - // The cache evicts least recently used items first. + // The cache periodically evicts random items when number of cached tokens exceeds CacheSize. // Zero value disables tokeninfo cache. CacheSize int @@ -100,7 +102,7 @@ func (o *TokeninfoOptions) newTokeninfoClient() (tokeninfoClient, error) { } if o.CacheSize > 0 { - c = newTokeninfoCache(c, o.CacheSize, o.CacheTTL) + c = newTokeninfoCache(c, o.Metrics, o.CacheSize, o.CacheTTL) } return c, nil } diff --git a/filters/auth/tokeninfocache.go b/filters/auth/tokeninfocache.go index c925ae5834..df31bc62f6 100644 --- a/filters/auth/tokeninfocache.go +++ b/filters/auth/tokeninfocache.go @@ -1,32 +1,32 @@ package auth import ( - "container/list" + "maps" "sync" + "sync/atomic" "time" "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/metrics" ) type ( tokeninfoCache struct { - client tokeninfoClient - size int - ttl time.Duration - now func() time.Time - - mu sync.Mutex - cache map[string]*entry - // least recently used token at the end - history *list.List + client tokeninfoClient + metrics metrics.Metrics + size int + ttl time.Duration + now func() time.Time + + cache sync.Map // map[string]*entry + count atomic.Int64 // estimated number of cached entries, see https://github.com/golang/go/issues/20680 + quit chan struct{} } entry struct { - cachedAt time.Time - expiresAt time.Time - info map[string]any - // reference in the history - href *list.Element + expiresAt time.Time + info map[string]any + infoExpiresAt time.Time } ) @@ -34,15 +34,22 @@ var _ tokeninfoClient = &tokeninfoCache{} const expiresInField = "expires_in" -func newTokeninfoCache(client tokeninfoClient, size int, ttl time.Duration) *tokeninfoCache { - return &tokeninfoCache{ +func newTokeninfoCache(client tokeninfoClient, metrics metrics.Metrics, size int, ttl time.Duration) *tokeninfoCache { + c := &tokeninfoCache{ client: client, + metrics: metrics, size: size, ttl: ttl, now: time.Now, - cache: make(map[string]*entry, size), - history: list.New(), + quit: make(chan struct{}), } + go c.evictLoop() + return c +} + +func (c *tokeninfoCache) Close() { + c.client.Close() + close(c.quit) } func (c *tokeninfoCache) getTokeninfo(token string, ctx filters.FilterContext) (map[string]any, error) { @@ -58,35 +65,21 @@ func (c *tokeninfoCache) getTokeninfo(token string, ctx filters.FilterContext) ( } func (c *tokeninfoCache) cached(token string) map[string]any { - now := c.now() - - c.mu.Lock() - - if e, ok := c.cache[token]; ok { + if v, ok := c.cache.Load(token); ok { + now := c.now() + e := v.(*entry) if now.Before(e.expiresAt) { - c.history.MoveToFront(e.href) - cachedInfo := e.info - c.mu.Unlock() - // It might be ok to return cached value // without adjusting "expires_in" to avoid copy // if caller never modifies the result and // when "expires_in" did not change (same second) // or for small TTL values - info := shallowCopyOf(cachedInfo) + info := maps.Clone(e.info) - elapsed := now.Sub(e.cachedAt).Truncate(time.Second).Seconds() - info[expiresInField] = info[expiresInField].(float64) - elapsed + info[expiresInField] = e.infoExpiresAt.Sub(now).Truncate(time.Second).Seconds() return info - } else { - // remove expired - delete(c.cache, token) - c.history.Remove(e.href) } } - - c.mu.Unlock() - return nil } @@ -95,38 +88,62 @@ func (c *tokeninfoCache) tryCache(token string, info map[string]any) { if expiresIn <= 0 { return } - if c.ttl > 0 && expiresIn > c.ttl { - expiresIn = c.ttl - } now := c.now() - expiresAt := now.Add(expiresIn) + e := &entry{ + info: info, + infoExpiresAt: now.Add(expiresIn), + } + + if c.ttl > 0 && expiresIn > c.ttl { + e.expiresAt = now.Add(c.ttl) + } else { + e.expiresAt = e.infoExpiresAt + } - c.mu.Lock() - defer c.mu.Unlock() + if _, loaded := c.cache.Swap(token, e); !loaded { + c.count.Add(1) + } +} - if e, ok := c.cache[token]; ok { - // update - e.cachedAt = now - e.expiresAt = expiresAt - e.info = info - c.history.MoveToFront(e.href) - return +func (c *tokeninfoCache) evictLoop() { + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + for { + select { + case <-c.quit: + return + case <-ticker.C: + c.evict() + } } +} - // create - c.cache[token] = &entry{ - cachedAt: now, - expiresAt: expiresAt, - info: info, - href: c.history.PushFront(token), +func (c *tokeninfoCache) evict() { + now := c.now() + // Evict expired entries + c.cache.Range(func(key, value any) bool { + e := value.(*entry) + if now.After(e.expiresAt) { + if c.cache.CompareAndDelete(key, value) { + c.count.Add(-1) + } + } + return true + }) + + // Evict random entries until the cache size is within limits + if c.count.Load() > int64(c.size) { + c.cache.Range(func(key, value any) bool { + if c.cache.CompareAndDelete(key, value) { + c.count.Add(-1) + } + return c.count.Load() > int64(c.size) + }) } - // remove least used - if len(c.cache) > c.size { - leastUsed := c.history.Back() - delete(c.cache, leastUsed.Value.(string)) - c.history.Remove(leastUsed) + if c.metrics != nil { + c.metrics.UpdateGauge("tokeninfocache.count", float64(c.count.Load())) } } @@ -141,11 +158,3 @@ func expiresIn(info map[string]any) time.Duration { } return 0 } - -func shallowCopyOf(info map[string]any) map[string]any { - m := make(map[string]any, len(info)) - for k, v := range info { - m[k] = v - } - return m -} diff --git a/filters/auth/tokeninfocache_test.go b/filters/auth/tokeninfocache_test.go index a5cfbf4a0e..86162207ac 100644 --- a/filters/auth/tokeninfocache_test.go +++ b/filters/auth/tokeninfocache_test.go @@ -12,6 +12,7 @@ import ( "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/filtertest" + "github.com/zalando/skipper/metrics/metricstest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -22,6 +23,7 @@ type tokeninfoClientFunc func(string, filters.FilterContext) (map[string]any, er func (f tokeninfoClientFunc) getTokeninfo(token string, ctx filters.FilterContext) (map[string]any, error) { return f(token, ctx) } +func (f tokeninfoClientFunc) Close() {} type testTokeninfoToken string @@ -58,6 +60,7 @@ func (c *testClock) now() time.Time { func TestTokeninfoCache(t *testing.T) { const ( TokenTTLSeconds = 600 + CacheSize = 1 CacheTTL = 300 * time.Second // less than TokenTTLSeconds ) @@ -79,15 +82,19 @@ func TestTokeninfoCache(t *testing.T) { })) defer authServer.Close() + m := &metricstest.MockMetrics{} + defer m.Close() + o := TokeninfoOptions{ URL: authServer.URL + "/oauth2/tokeninfo", - CacheSize: 1, + CacheSize: CacheSize, CacheTTL: CacheTTL, + Metrics: m, } c, err := o.newTokeninfoClient() require.NoError(t, err) - defer c.(*tokeninfoCache).client.(*authClient).Close() + defer c.Close() c.(*tokeninfoCache).now = clock.now ctx := &filtertest.Context{FRequest: &http.Request{}} @@ -111,7 +118,7 @@ func TestTokeninfoCache(t *testing.T) { assert.Equal(t, int32(1), authRequests, "expected no request to auth sever") assert.Equal(t, token, info["uid"]) - assert.Equal(t, float64(595), info["expires_in"], "expected TokenTTLSeconds - truncate(delay)") + assert.Equal(t, float64(594), info["expires_in"], "expected truncate(TokenTTLSeconds - delay)") // Third request after "sleeping" longer than cache TTL clock.add(CacheTTL) @@ -123,7 +130,7 @@ func TestTokeninfoCache(t *testing.T) { assert.Equal(t, token, info["uid"]) assert.Equal(t, float64(294), info["expires_in"], "expected truncate(TokenTTLSeconds - CacheTTL - delay)") - // Fourth request with a new token evicts cached value + // Fourth request with a new token token = newTestTokeninfoToken(clock.now()).String() info, err = c.getTokeninfo(token, ctx) @@ -132,6 +139,19 @@ func TestTokeninfoCache(t *testing.T) { assert.Equal(t, int32(3), authRequests, "expected new request to auth sever") assert.Equal(t, token, info["uid"]) assert.Equal(t, float64(600), info["expires_in"], "expected TokenTTLSeconds") + + // Force eviction and verify cache size is within limits + c.(*tokeninfoCache).evict() + m.WithGauges(func(g map[string]float64) { + assert.Equal(t, float64(CacheSize), g["tokeninfocache.count"]) + }) + + // Force eviction after all entries expired and verify cache is empty + clock.add(CacheTTL + time.Second) + c.(*tokeninfoCache).evict() + m.WithGauges(func(g map[string]float64) { + assert.Equal(t, float64(0), g["tokeninfocache.count"]) + }) } // Tests race between reading and writing cache for the same token @@ -152,7 +172,8 @@ func TestTokeninfoCacheUpdateRace(t *testing.T) { return map[string]any{"requestNumber": requestNumber, "uid": token, "expires_in": float64(600)}, nil }) - c := newTokeninfoCache(mc, 1, time.Hour) + c := newTokeninfoCache(mc, nil, 1, time.Hour) + defer c.Close() const token = "atoken" @@ -234,7 +255,8 @@ func BenchmarkTokeninfoCache(b *testing.B) { return tokenValues[token], nil }) - c := newTokeninfoCache(mc, bi.cacheSize, time.Hour) + c := newTokeninfoCache(mc, nil, bi.cacheSize, time.Hour) + defer c.Close() var tokens []string for i := 0; i < bi.tokens; i++ { diff --git a/skipper.go b/skipper.go index 5ee6238706..0bdfd9e541 100644 --- a/skipper.go +++ b/skipper.go @@ -1629,6 +1629,7 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { Timeout: o.OAuthTokeninfoTimeout, MaxIdleConns: o.IdleConnectionsPerHost, Tracer: tracer, + Metrics: mtr, CacheSize: o.OAuthTokeninfoCacheSize, CacheTTL: o.OAuthTokeninfoCacheTTL, } From 21903e635e22313be6500ffe69ae506b1d219c57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:24:59 +0100 Subject: [PATCH 0989/1047] build(deps): bump alpine from `21dc606` to `56fa17d` in /packaging (#3363) Bumps alpine from `21dc606` to `56fa17d`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index 5248307777..d722f2f7e9 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 +FROM --platform=linux/arm64 alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index 8fe85748e6..d2ec131224 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 +FROM --platform=linux/arm/v7 alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From 8d4721fb0d1787eb6d4c7159d2d22fa5341969f7 Mon Sep 17 00:00:00 2001 From: Pushpalanka Jayawardhana Date: Thu, 9 Jan 2025 14:06:50 +0100 Subject: [PATCH 0990/1047] Refactor the tests for better readability on rules. (#3313) Signed-off-by: Pushpalanka Jayawardhana --- .../opaauthorizerequest_test.go | 61 +++++++++++++------ .../opaserveresponse/opaserveresponse_test.go | 44 ++++++------- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 89e8f3de50..20d6f3bf29 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -63,7 +63,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { msg: "Allow Requests with spaces in path", filterName: "opaAuthorizeRequest", bundleName: "somebundle.tar.gz", - regoQuery: "envoy/authz/allow", + regoQuery: "envoy/authz/allow_with_space_in_path", requestPath: "/my%20path", requestMethod: "GET", contextExtensions: "", @@ -106,7 +106,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { msg: "Allow Requests with query parameters", filterName: "opaAuthorizeRequest", bundleName: "somebundle.tar.gz", - regoQuery: "envoy/authz/allow", + regoQuery: "envoy/authz/allow_with_query", requestPath: "/allow-with-query?pass=yes&id=1&id=2&msg=help%20me", requestMethod: "GET", contextExtensions: "", @@ -173,8 +173,8 @@ func TestAuthorizeRequestFilter(t *testing.T) { msg: "Simple Forbidden with Query Parameters", filterName: "opaAuthorizeRequest", bundleName: "somebundle.tar.gz", - regoQuery: "envoy/authz/allow", - requestPath: "/allow-with-query?tofail=true", + regoQuery: "envoy/authz/deny_with_query", + requestPath: "/allow-me?tofail=true", requestMethod: "GET", contextExtensions: "", expectedStatus: http.StatusForbidden, @@ -384,6 +384,20 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Allow Requests ignoring fragment", + filterName: "opaAuthorizeRequest", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_with_path_having_fragment", + requestPath: "/path-with-empty-query#fragment?", + requestMethod: "GET", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, } { t.Run(ti.msg, func(t *testing.T) { t.Logf("Running test for %v", ti) @@ -405,33 +419,44 @@ func TestAuthorizeRequestFilter(t *testing.T) { "main.rego": ` package envoy.authz - default allow = false + default allow := false + default deny_with_query := false allow { - input.parsed_path = [ "allow" ] - input.parsed_query = {} + input.parsed_path == [ "allow" ] + input.parsed_query == {} } allow_with_http_path { input.attributes.request.http.path == "/some/api/path?q1=v1&msg=help%20me" } - allow { - input.parsed_path = [ "my path" ] + allow_with_space_in_path { + input.parsed_path == [ "my path" ] } allow_with_path_having_empty_query { - input.parsed_path = [ "path-with-empty-query" ] - input.parsed_query = {} + input.parsed_path == [ "path-with-empty-query" ] + input.parsed_query == {} } - allow { - input.parsed_path = [ "allow-with-query" ] + allow_with_query { + input.parsed_path == [ "allow-with-query" ] input.parsed_query.pass == ["yes"] input.parsed_query.id == ["1", "2"] input.parsed_query.msg == ["help me"] } + deny_with_query { + input.attributes.request.http.path == "/allow-me?tofail=true" + not input.parsed_query.tofail == ["true"] + } + + allow_with_path_having_fragment { + input.parsed_path == [ "path-with-empty-query" ] + input.attributes.request.http.path == "/path-with-empty-query" + } + allow_context_extensions { input.attributes.contextExtensions["com.mycompany.myprop"] == "myvalue" } @@ -440,15 +465,15 @@ func TestAuthorizeRequestFilter(t *testing.T) { opa.runtime().config.labels.environment == "test" } - default allow_object = { + default allow_object := { "allowed": false, "headers": {"x-ext-auth-allow": "no"}, "body": "Unauthorized Request", "http_status": 401 } - allow_object = response { - input.parsed_path = [ "allow", "structured" ] + allow_object := response { + input.parsed_path == [ "allow", "structured" ] response := { "allowed": true, "headers": { @@ -477,7 +502,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { "headers": "bogus string instead of object" } - default allow_body = false + default allow_body := false allow_body { input.parsed_body.target_id == "123456" @@ -485,7 +510,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { decision_id := input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id - allow_object_decision_id_in_header = response { + allow_object_decision_id_in_header := response { input.parsed_path = ["allow", "structured"] decision_id response := { diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go index 89c4e611d8..6809ceb9b3 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -77,7 +77,7 @@ func TestServerResponseFilter(t *testing.T) { regoQuery: "envoy/authz/allow_object", requestPath: "/allow/structured/with-empty-query-string?", expectedStatus: http.StatusOK, - expectedBody: "Welcome from policy!", + expectedBody: "Welcome from policy with empty query string!", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, }, { @@ -87,7 +87,7 @@ func TestServerResponseFilter(t *testing.T) { regoQuery: "envoy/authz/allow_object", requestPath: "/allow/structured/with-query?pass=yes", expectedStatus: http.StatusOK, - expectedBody: "Welcome from policy!", + expectedBody: "Welcome from policy with query params!", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, }, { @@ -172,21 +172,21 @@ func TestServerResponseFilter(t *testing.T) { "main.rego": ` package envoy.authz - default allow = false + default allow := false allow { - input.parsed_path = [ "allow" ] + input.parsed_path == [ "allow" ] } - default allow_object = { + default allow_object := { "allowed": false, "headers": {"x-ext-auth-allow": "no"}, "body": "Unauthorized Request", "http_status": 403 } - allow_object = response { - input.parsed_path = [ "allow", "structured" ] + allow_object := response { + input.parsed_path == [ "allow", "structured" ] response := { "allowed": true, "headers": {"x-ext-auth-allow": "yes"}, @@ -195,30 +195,30 @@ func TestServerResponseFilter(t *testing.T) { } } - allow_object = response { - input.parsed_path = [ "allow", "structured", "with-empty-query-string" ] + allow_object := response { + input.parsed_path == [ "allow", "structured", "with-empty-query-string" ] input.parsed_query == {} response := { "allowed": true, "headers": {"x-ext-auth-allow": "yes"}, - "body": "Welcome from policy!", + "body": "Welcome from policy with empty query string!", "http_status": 200 } } - allow_object = response { - input.parsed_path = [ "allow", "structured", "with-query" ] + allow_object := response { + input.parsed_path == [ "allow", "structured", "with-query" ] input.parsed_query.pass == ["yes"] response := { "allowed": true, "headers": {"x-ext-auth-allow": "yes"}, - "body": "Welcome from policy!", + "body": "Welcome from policy with query params!", "http_status": 200 } } - allow_object = response { - input.parsed_path = [ "allow", "production" ] + allow_object := response { + input.parsed_path == [ "allow", "production" ] opa.runtime().config.labels.environment == "production" response := { "allowed": true, @@ -228,8 +228,8 @@ func TestServerResponseFilter(t *testing.T) { } } - allow_object = response { - input.parsed_path = [ "allow", "test" ] + allow_object := response { + input.parsed_path == [ "allow", "test" ] opa.runtime().config.labels.environment == "test" response := { "allowed": true, @@ -239,8 +239,8 @@ func TestServerResponseFilter(t *testing.T) { } } - allow_object_structured_body = response { - input.parsed_path = [ "allow", "structured" ] + allow_object_structured_body := response { + input.parsed_path == [ "allow", "structured" ] response := { "allowed": true, "headers": {"x-ext-auth-allow": "yes"}, @@ -249,8 +249,8 @@ func TestServerResponseFilter(t *testing.T) { } } - allow_object_contextextensions = response { - input.parsed_path = [ "allow", "structured" ] + allow_object_contextextensions := response { + input.parsed_path == [ "allow", "structured" ] response := { "allowed": true, "headers": {"x-ext-auth-allow": "yes"}, @@ -259,7 +259,7 @@ func TestServerResponseFilter(t *testing.T) { } } - allow_object_req_body = response { + allow_object_req_body := response { response := { "allowed": true, "headers": {}, From 76f8f745f4a6c7e3417a2b328da435049068d04b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:17:51 +0100 Subject: [PATCH 0991/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 9 updates (#3371) Bumps the all-go-mod-patch-and-minor group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) | `3.11.0` | `3.12.0` | | [github.com/hashicorp/memberlist](https://github.com/hashicorp/memberlist) | `0.5.1` | `0.5.2` | | [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) | `0.34.0` | `0.35.0` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.31.0` | `0.32.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.33.0` | `0.34.0` | | [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.24.0` | `0.25.0` | | [golang.org/x/time](https://github.com/golang/time) | `0.8.0` | `0.9.0` | | google.golang.org/protobuf | `1.36.1` | `1.36.2` | Updates `github.com/coreos/go-oidc/v3` from 3.11.0 to 3.12.0 - [Release notes](https://github.com/coreos/go-oidc/releases) - [Commits](https://github.com/coreos/go-oidc/compare/v3.11.0...v3.12.0) Updates `github.com/hashicorp/memberlist` from 0.5.1 to 0.5.2 - [Release notes](https://github.com/hashicorp/memberlist/releases) - [Commits](https://github.com/hashicorp/memberlist/compare/v0.5.1...v0.5.2) Updates `github.com/testcontainers/testcontainers-go` from 0.34.0 to 0.35.0 - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.34.0...v0.35.0) Updates `golang.org/x/crypto` from 0.31.0 to 0.32.0 - [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.32.0) Updates `golang.org/x/net` from 0.33.0 to 0.34.0 - [Commits](https://github.com/golang/net/compare/v0.33.0...v0.34.0) Updates `golang.org/x/oauth2` from 0.24.0 to 0.25.0 - [Commits](https://github.com/golang/oauth2/compare/v0.24.0...v0.25.0) Updates `golang.org/x/term` from 0.27.0 to 0.28.0 - [Commits](https://github.com/golang/term/compare/v0.27.0...v0.28.0) Updates `golang.org/x/time` from 0.8.0 to 0.9.0 - [Commits](https://github.com/golang/time/compare/v0.8.0...v0.9.0) Updates `google.golang.org/protobuf` from 1.36.1 to 1.36.2 --- updated-dependencies: - dependency-name: github.com/coreos/go-oidc/v3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/hashicorp/memberlist dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/time dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 21 +++++++++--------- go.sum | 70 +++++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index dca2cfeb24..277228032f 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 - github.com/coreos/go-oidc/v3 v3.11.0 + github.com/coreos/go-oidc/v3 v3.12.0 github.com/dchest/siphash v1.2.3 github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1 @@ -22,7 +22,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 - github.com/hashicorp/memberlist v0.5.1 + github.com/hashicorp/memberlist v0.5.2 github.com/instana/go-sensor v1.66.0 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 @@ -40,7 +40,7 @@ require ( github.com/sony/gobreaker v1.0.0 github.com/stretchr/testify v1.10.0 github.com/szuecs/rate-limit-buffer v0.9.0 - github.com/testcontainers/testcontainers-go v0.34.0 + github.com/testcontainers/testcontainers-go v0.35.0 github.com/tidwall/gjson v1.18.0 github.com/tsenart/vegeta v12.7.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible @@ -48,14 +48,14 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.31.0 + golang.org/x/crypto v0.32.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.33.0 - golang.org/x/oauth2 v0.24.0 + golang.org/x/net v0.34.0 + golang.org/x/oauth2 v0.25.0 golang.org/x/sync v0.10.0 - golang.org/x/term v0.27.0 - golang.org/x/time v0.8.0 - google.golang.org/protobuf v1.36.1 + golang.org/x/term v0.28.0 + golang.org/x/time v0.9.0 + google.golang.org/protobuf v1.36.2 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf @@ -111,6 +111,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-sockaddr v1.0.0 // indirect @@ -171,7 +172,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect diff --git a/go.sum b/go.sum index 64968e8bbb..00472655c3 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= @@ -33,6 +34,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -88,8 +90,8 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= -github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= +github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= +github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= @@ -160,8 +162,10 @@ github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0 github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -217,6 +221,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/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.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -238,6 +243,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= github.com/hashicorp/go-msgpack/v2 v2.1.1 h1:xQEY9yB2wnHitoSzk/B9UjXWRQ67QKu5AOm8aFp8N3I= github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -251,8 +258,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/memberlist v0.5.1 h1:mk5dRuzeDNis2bi6LLoQIXfMH7JQvAzt3mQD0vNZZUo= -github.com/hashicorp/memberlist v0.5.1/go.mod h1:zGDXV6AqbDTKTM6yxW0I4+JtFzZAJVoIPvss4hV8F24= +github.com/hashicorp/memberlist v0.5.2 h1:rJoNPWZ0juJBgqn48gjy59K5H4rNgvUoM1kUD7bXiuI= +github.com/hashicorp/memberlist v0.5.2/go.mod h1:Ri9p/tRShbjYnpNf4FFPXG7wxEGY4Nrcn6E7jrVa//4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= @@ -261,10 +268,14 @@ github.com/instana/go-sensor v1.66.0 h1:4ugORxo+iNmmSh6a3zmpAdXOJTOWvywSN0zRgLbh github.com/instana/go-sensor v1.66.0/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -273,6 +284,7 @@ github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -330,6 +342,7 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -383,6 +396,8 @@ github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -393,11 +408,15 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -421,6 +440,7 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -461,8 +481,8 @@ github.com/szuecs/rate-limit-buffer v0.9.0 h1:65fBCVsaJFh0E1G5C6/sInEPlYR6dXtF9J github.com/szuecs/rate-limit-buffer v0.9.0/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= -github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -534,8 +554,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -562,6 +582,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -570,14 +591,16 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -586,6 +609,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -602,12 +626,17 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -615,17 +644,17 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -684,8 +713,9 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From f4fc225af61c6c6a52668f86ee565fac943911e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:18:24 +0100 Subject: [PATCH 0992/1047] build(deps): bump docker/build-push-action from 6.10.0 to 6.11.0 (#3368) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.10.0 to 6.11.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/48aba3b46d1b1fec4febb7c5d0c644b249a11355...b32b51a8eda65d6793cd0494a773d4f6bcef32dc) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 4907fd1ffe..17e0e3675a 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 + uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 67b782bfb93e23e97ccb0094ea530c09d55dddc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:19:10 +0100 Subject: [PATCH 0993/1047] build(deps): bump docker/setup-qemu-action from 3.2.0 to 3.3.0 (#3369) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...53851d14592bedcffcf25ea515637cff71ef929a) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 17e0e3675a..bdaa4d48f2 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -47,7 +47,7 @@ jobs: make build.linux - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf + uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a - name: Set up Docker Buildx uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 From a8f52fbf950758c4fa1016967ef04244d971b0e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:19:31 +0100 Subject: [PATCH 0994/1047] build(deps): bump actions/upload-artifact from 4.5.0 to 4.6.0 (#3370) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/6f51ac03b9356f520e9adb1b1b7802705f340c2b...65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index d1c2636337..ef0e6732aa 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: SARIF file path: results.sarif From a73353b69454568682d13603bcb0d3aac4fd2912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:19:54 +0100 Subject: [PATCH 0995/1047] build(deps): bump amazonlinux from `1b09180` to `6e7b74e` in /fuzz (#3372) Bumps amazonlinux from `1b09180` to `6e7b74e`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index b86df9f9a5..96dbbc2d41 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:1b091808a98a0a1b0570073421866f5a76adfa43e8d7a42b2f5dd0c60053a567 +FROM amazonlinux:2023@sha256:6e7b74e958e8601f6e51cc59603544ea9946e69831525cb987c1a7ae3a7a1447 WORKDIR /workspace From 35195d81e9b2e032756705394533ac53092a01ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:20:17 +0100 Subject: [PATCH 0996/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3373) Bumps oss-fuzz-base/base-builder-go from `9bf7fad` to `f4b5582`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index f143bd87b9..60ced38433 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:9bf7fad8ca02443224c7518392d80c97a62b8cb0822f03aadf9193a7e27346f0 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:f4b55824218c62ca44a0fb3985c2ca528c3fd64d52987b72ce45b5ec520123dd COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 0e80a77f82f9f1f409e1f4c07b4ca17a8a8a90f6 Mon Sep 17 00:00:00 2001 From: Pushpalanka Jayawardhana Date: Wed, 15 Jan 2025 10:34:59 +0100 Subject: [PATCH 0997/1047] OPA: Upgrade OPA to 0.70.0 addressing breaking changes (#3374) The EvalContext interface is added with new CreatePreparedQueryOnce function and the signature of envoyauth.Eval(ctx, opa, inputValue, result) method used by Skipper were changed with in OPA v0.70.0 as a result of open-policy-agent/opa-envoy-plugin#604. The interface change is absorbed with no functional addition and just addressing successful compilation. Signed-off-by: Pushpalanka Jayawardhana --- filters/openpolicyagent/evaluation.go | 6 +- filters/openpolicyagent/openpolicyagent.go | 27 +++++---- go.mod | 34 +++++------ go.sum | 68 +++++++++++----------- 4 files changed, 70 insertions(+), 65 deletions(-) diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go index 2f38958617..f9129cb9e4 100644 --- a/filters/openpolicyagent/evaluation.go +++ b/filters/openpolicyagent/evaluation.go @@ -3,17 +3,17 @@ package openpolicyagent import ( "context" "fmt" + "time" + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" "github.com/open-policy-agent/opa-envoy-plugin/opa/decisionlog" "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" "github.com/open-policy-agent/opa/server" "github.com/open-policy-agent/opa/topdown" "github.com/opentracing/opentracing-go" pbstruct "google.golang.org/protobuf/types/known/structpb" - "time" ) func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3.CheckRequest) (*envoyauth.EvalResult, error) { @@ -70,7 +70,7 @@ func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3. return nil, err } - err = envoyauth.Eval(ctx, opa, inputValue, result, rego.DistributedTracingOpts(opa.DistributedTracing())) + err = envoyauth.Eval(ctx, opa, inputValue, result) if err != nil { return nil, err } diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 1024208ec4..c24d5d0e2f 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -17,6 +17,7 @@ import ( ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" "github.com/google/uuid" + "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/config" "github.com/open-policy-agent/opa/logging" @@ -369,6 +370,7 @@ type OpenPolicyAgentInstance struct { bundleName string preparedQuery *rego.PreparedEvalQuery preparedQueryDoOnce *sync.Once + preparedQueryErr error interQueryBuiltinCache iCache.InterQueryCache once sync.Once stopped bool @@ -740,22 +742,11 @@ func (opa *OpenPolicyAgentInstance) Runtime() *ast.Term { return opa.manager.Inf // Logger is an implementation of the envoyauth.EvalContext interface func (opa *OpenPolicyAgentInstance) Logger() logging.Logger { return opa.manager.Logger() } -// PreparedQueryDoOnce is an implementation of the envoyauth.EvalContext interface -func (opa *OpenPolicyAgentInstance) PreparedQueryDoOnce() *sync.Once { return opa.preparedQueryDoOnce } - // InterQueryBuiltinCache is an implementation of the envoyauth.EvalContext interface func (opa *OpenPolicyAgentInstance) InterQueryBuiltinCache() iCache.InterQueryCache { return opa.interQueryBuiltinCache } -// PreparedQuery is an implementation of the envoyauth.EvalContext interface -func (opa *OpenPolicyAgentInstance) PreparedQuery() *rego.PreparedEvalQuery { return opa.preparedQuery } - -// SetPreparedQuery is an implementation of the envoyauth.EvalContext interface -func (opa *OpenPolicyAgentInstance) SetPreparedQuery(q *rego.PreparedEvalQuery) { - opa.preparedQuery = q -} - // Config is an implementation of the envoyauth.EvalContext interface func (opa *OpenPolicyAgentInstance) Config() *config.Config { return opa.opaConfig } @@ -764,6 +755,20 @@ func (opa *OpenPolicyAgentInstance) DistributedTracing() opatracing.Options { return buildTracingOptions(opa.registry.tracer, opa.bundleName, opa.manager) } +// CreatePreparedQueryOnce is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) CreatePreparedQueryOnce(opts envoyauth.PrepareQueryOpts) (*rego.PreparedEvalQuery, error) { + opa.preparedQueryDoOnce.Do(func() { + regoOpts := append(opts.Opts, rego.DistributedTracingOpts(opa.DistributedTracing())) + + pq, err := rego.New(regoOpts...).PrepareForEval(context.Background()) + + opa.preparedQuery = &pq + opa.preparedQueryErr = err + }) + + return opa.preparedQuery, opa.preparedQueryErr +} + // logging.Logger that does not pollute info with debug logs type QuietLogger struct { target logging.Logger diff --git a/go.mod b/go.mod index 277228032f..184ae22483 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 github.com/docker/go-connections v0.5.0 - github.com/envoyproxy/go-control-plane v0.13.0 + github.com/envoyproxy/go-control-plane v0.13.1 github.com/ghodss/yaml v1.0.0 github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/go-cmp v0.6.0 @@ -27,8 +27,8 @@ require ( github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 - github.com/open-policy-agent/opa v0.68.0 - github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4 + github.com/open-policy-agent/opa v0.70.0 + github.com/open-policy-agent/opa-envoy-plugin v0.70.0-envoy-1 github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.20.5 @@ -63,23 +63,23 @@ require ( ) require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect - github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/agnivade/levenshtein v1.2.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect - github.com/containerd/containerd v1.7.21 // indirect - github.com/containerd/errdefs v0.1.0 // indirect + github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 // indirect + github.com/containerd/containerd v1.7.23 // indirect + github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect @@ -91,7 +91,7 @@ require ( github.com/docker/docker v27.1.1+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ini/ini v1.67.0 // indirect @@ -101,7 +101,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.1 // indirect + github.com/golang/glog v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -170,18 +170,18 @@ require ( go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/automaxprocs v1.5.3 // indirect - golang.org/x/mod v0.20.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/tools v0.26.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.66.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/grpc v1.67.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.3.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) -go 1.22 +go 1.22.0 diff --git a/go.sum b/go.sum index 00472655c3..08a0e91ddd 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -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 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -27,8 +27,8 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= -github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= -github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= +github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -74,16 +74,16 @@ github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 h1:cJ1E8ZwZLfercT github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199/go.mod h1:jC+zrjHA5CaxJzn+tojIoIOzSp/6BlkRWXnMlxNkB+g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -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 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg= +github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.21 h1:USGXRK1eOC/SX0L195YgxTHb0a00anxajOzgfN0qrCA= -github.com/containerd/containerd v1.7.21/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= -github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= +github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -120,8 +120,8 @@ github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1 h1:De28BM16VaAD github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1/go.mod h1:ut6ck43wTobSb/Jvxo/5uzEEtrfU4gj4m0vh02v7GuQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -137,11 +137,11 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= -github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= +github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -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 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= 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.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -187,8 +187,8 @@ github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQg github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -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 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -361,10 +361,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/open-policy-agent/opa v0.68.0 h1:Jl3U2vXRjwk7JrHmS19U3HZO5qxQRinQbJ2eCJYSqJQ= -github.com/open-policy-agent/opa v0.68.0/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w= -github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4 h1:yq9TvPBS1qA301q88k8MbGEwba3yu14FaeGVriCvALU= -github.com/open-policy-agent/opa-envoy-plugin v0.68.0-envoy-4/go.mod h1:vRdAOKLNgXitws+CdhIAND6TT0wkFc2IVzAHB3tK4qw= +github.com/open-policy-agent/opa v0.70.0 h1:B3cqCN2iQAyKxK6+GI+N40uqkin+wzIrM7YA60t9x1U= +github.com/open-policy-agent/opa v0.70.0/go.mod h1:Y/nm5NY0BX0BqjBriKUiV81sCl8XOjjvqQG7dXrggtI= +github.com/open-policy-agent/opa-envoy-plugin v0.70.0-envoy-1 h1:nWKvCOUAKYpnvRmjbdcL32dGVPG/eyRa/MeQa3Es+hY= +github.com/open-policy-agent/opa-envoy-plugin v0.70.0-envoy-1/go.mod h1:S+CuCmO3Wk+SnAmjPl69tUB3G8iUgXUH+Jrl56Wje/U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -542,8 +542,8 @@ go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeX go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q= @@ -576,8 +576,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -671,8 +671,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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= @@ -691,10 +691,10 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -702,8 +702,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= 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= From 7c15e716cf4706ad70d80794651d0cfb7d0b2dcd Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 15 Jan 2025 15:38:34 +0100 Subject: [PATCH 0998/1047] tracing/tracingtest: deprecate Tracer and Span types (#3375) Follow up on #684 (https://github.com/zalando/skipper/pull/684#issuecomment-399030290) and deprecate hand-rolled test tracer implementation in favour of MockTracer added by #3322 that wraps github.com/opentracing/opentracing-go/mocktracer and waits for finished spans. Fix MockTracer: * return MockSpan that has proper Tracer() implementation * return MockSpan wrappers from FinishedSpans * panic on timeout waiting for finished spans Closes #2084 Updates #2104 Signed-off-by: Alexander Yastrebov --- .../opaauthorizerequest_test.go | 26 ++-- .../opaserveresponse/opaserveresponse_test.go | 12 +- .../openpolicyagent/openpolicyagent_test.go | 25 ++-- filters/openpolicyagent/tracing_test.go | 50 ++++--- filters/tracing/baggagetotag_test.go | 14 +- filters/tracing/statebagtotag_test.go | 19 ++- filters/tracing/tag_test.go | 3 +- proxy/tracing_test.go | 138 ++++-------------- redis_test.go | 4 +- skipper_test.go | 4 +- tracing/tracingtest/mocktracer.go | 31 +++- tracing/tracingtest/mocktracer_test.go | 17 +++ tracing/tracingtest/testtracer.go | 3 + 13 files changed, 165 insertions(+), 181 deletions(-) create mode 100644 tracing/tracingtest/mocktracer_test.go diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 20d6f3bf29..4498cde081 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -464,14 +464,14 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow_runtime_environment { opa.runtime().config.labels.environment == "test" } - + default allow_object := { "allowed": false, "headers": {"x-ext-auth-allow": "no"}, "body": "Unauthorized Request", "http_status": 401 } - + allow_object := response { input.parsed_path == [ "allow", "structured" ] response := { @@ -506,13 +506,13 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow_body { input.parsed_body.target_id == "123456" - } - + } + decision_id := input.attributes.metadataContext.filterMetadata.open_policy_agent.decision_id allow_object_decision_id_in_header := response { input.parsed_path = ["allow", "structured"] - decision_id + decision_id response := { "allowed": true, "response_headers_to_add": { @@ -541,9 +541,9 @@ func TestAuthorizeRequestFilter(t *testing.T) { "environment": "test" }, "plugins": { - "envoy_ext_authz_grpc": { + "envoy_ext_authz_grpc": { "path": %q, - "dry-run": false + "dry-run": false } } }`, opaControlPlane.URL(), ti.regoQuery)) @@ -561,7 +561,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { openpolicyagent.WithConfigTemplate(config), openpolicyagent.WithEnvoyMetadataBytes(envoyMetaDataConfig)) - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(tracingtest.NewTracer())) ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, opts...) fr.Register(ftSpec) ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, opts...) @@ -769,11 +769,11 @@ func BenchmarkAuthorizeRequest(b *testing.B) { "cert": public_key_cert, "aud": "nqz3xhorr5" }) - + valid - + payload.sub == "5974934733" - } + } `, publicKey), }), ) @@ -863,9 +863,9 @@ func generateConfig(opaControlPlane *opasdktest.Server, path string) []byte { "environment": "test" }, "plugins": { - "envoy_ext_authz_grpc": { + "envoy_ext_authz_grpc": { "path": %q, - "dry-run": false + "dry-run": false } } }`, opaControlPlane.URL(), path)) diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go index 6809ceb9b3..1a35f50b3b 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -176,15 +176,15 @@ func TestServerResponseFilter(t *testing.T) { allow { input.parsed_path == [ "allow" ] - } - + } + default allow_object := { "allowed": false, "headers": {"x-ext-auth-allow": "no"}, "body": "Unauthorized Request", "http_status": 403 } - + allow_object := response { input.parsed_path == [ "allow", "structured" ] response := { @@ -216,7 +216,7 @@ func TestServerResponseFilter(t *testing.T) { "http_status": 200 } } - + allow_object := response { input.parsed_path == [ "allow", "production" ] opa.runtime().config.labels.environment == "production" @@ -289,7 +289,7 @@ func TestServerResponseFilter(t *testing.T) { } }, "plugins": { - "envoy_ext_authz_grpc": { + "envoy_ext_authz_grpc": { "path": %q, "dry-run": false, "skip-request-body-parse": false @@ -300,7 +300,7 @@ func TestServerResponseFilter(t *testing.T) { } }`, opaControlPlane.URL(), ti.regoQuery)) - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(&tracingtest.Tracer{})) + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(tracingtest.NewTracer())) ftSpec := NewOpaServeResponseSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) fr.Register(ftSpec) ftSpec = NewOpaServeResponseWithReqBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go index 6c6dc7d923..c6625041fe 100644 --- a/filters/openpolicyagent/openpolicyagent_test.go +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -24,6 +24,7 @@ import ( "github.com/open-policy-agent/opa/storage/inmem" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/filtertest" "github.com/zalando/skipper/filters/openpolicyagent/internal/envoy" @@ -385,16 +386,16 @@ func TestTracing(t *testing.T) { inst, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") assert.NoError(t, err) - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() parent := tracer.StartSpan("start_span") ctx := opentracing.ContextWithSpan(context.Background(), parent) span, _ := inst.StartSpanFromContext(ctx) span.Finish() parent.Finish() - recspan, ok := tracer.FindSpan("open-policy-agent") - assert.True(t, ok, "No span was created for open policy agent") - assert.Equal(t, map[string]interface{}{"opa.bundle_name": "test", "opa.label.id": inst.manager.Labels()["id"], "opa.label.version": inst.manager.Labels()["version"]}, recspan.Tags) + recspan := tracer.FindSpan("open-policy-agent") + require.NotNil(t, recspan, "No span was created for open policy agent") + assert.Equal(t, map[string]interface{}{"opa.bundle_name": "test", "opa.label.id": inst.manager.Labels()["id"], "opa.label.version": inst.manager.Labels()["version"]}, recspan.Tags()) } func TestEval(t *testing.T) { @@ -408,7 +409,7 @@ func TestEval(t *testing.T) { inst, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") assert.NoError(t, err) - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() span := tracer.StartSpan("open-policy-agent") ctx := opentracing.ContextWithSpan(context.Background(), span) @@ -426,9 +427,9 @@ func TestEval(t *testing.T) { assert.False(t, allowed) span.Finish() - testspan, ok := tracer.FindSpan("open-policy-agent") - assert.True(t, ok) - assert.Equal(t, result.DecisionID, testspan.Tags["opa.decision_id"]) + testspan := tracer.FindSpan("open-policy-agent") + require.NotNil(t, testspan) + assert.Equal(t, result.DecisionID, testspan.Tag("opa.decision_id")) } func TestResponses(t *testing.T) { @@ -442,7 +443,7 @@ func TestResponses(t *testing.T) { inst, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") assert.NoError(t, err) - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() span := tracer.StartSpan("open-policy-agent") metrics := &metricstest.MockMetrics{} @@ -455,9 +456,9 @@ func TestResponses(t *testing.T) { assert.Equal(t, int64(1), counters["decision.err.test"]) }) span.Finish() - testspan, ok := tracer.FindSpan("open-policy-agent") - assert.True(t, ok, "span not found") - assert.Contains(t, testspan.Tags, "error") + testspan := tracer.FindSpan("open-policy-agent") + require.NotNil(t, testspan, "span not found") + assert.Equal(t, true, testspan.Tag("error")) fc = &filtertest.Context{FMetrics: metrics} inst.ServeInvalidDecisionError(fc, span, nil, fmt.Errorf("something happened")) diff --git a/filters/openpolicyagent/tracing_test.go b/filters/openpolicyagent/tracing_test.go index 92f295eea3..3d73301b59 100644 --- a/filters/openpolicyagent/tracing_test.go +++ b/filters/openpolicyagent/tracing_test.go @@ -10,6 +10,7 @@ import ( "github.com/open-policy-agent/opa/plugins" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/tracing/tracingtest" ) @@ -24,13 +25,13 @@ func (t *MockTransport) RoundTrip(*http.Request) (*http.Response, error) { } func TestTracingFactory(t *testing.T) { - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() testCases := []struct { name string req *http.Request tracer opentracing.Tracer - parentSpan opentracing.Span + parentSpan string resp *http.Response resperr error }{ @@ -43,11 +44,11 @@ func TestTracingFactory(t *testing.T) { URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, }, tracer: nil, - parentSpan: tracer.StartSpan("open-policy-agent"), + parentSpan: "open-policy-agent", resp: &http.Response{StatusCode: http.StatusOK}, }, { - name: "Sub-span created with parent span without tracer set", + name: "Sub-span created with parent span with tracer set", req: &http.Request{ Header: map[string][]string{}, Method: "GET", @@ -55,7 +56,7 @@ func TestTracingFactory(t *testing.T) { URL: &url.URL{Path: "/test", Scheme: "http", Host: "example.com"}, }, tracer: tracer, - parentSpan: tracer.StartSpan("open-policy-agent"), + parentSpan: "open-policy-agent", resp: &http.Response{StatusCode: http.StatusOK}, }, { @@ -96,7 +97,7 @@ func TestTracingFactory(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { f := &tracingFactory{} - tracer.Reset("") + tracer.Reset() tr := f.NewTransport(&MockTransport{tc.resp, tc.resperr}, buildTracingOptions(tc.tracer, "bundle", &plugins.Manager{ ID: "manager-id", @@ -105,41 +106,44 @@ func TestTracingFactory(t *testing.T) { }, })) - if tc.parentSpan != nil { - ctx := opentracing.ContextWithSpan(context.Background(), tc.parentSpan) + var parentSpan opentracing.Span + if tc.parentSpan != "" { + parentSpan = tracer.StartSpan(tc.parentSpan) + + ctx := opentracing.ContextWithSpan(context.Background(), parentSpan) tc.req = tc.req.WithContext(ctx) } resp, err := tr.RoundTrip(tc.req) - if tc.parentSpan != nil { - tc.parentSpan.Finish() + if parentSpan != nil { + parentSpan.Finish() } - createdSpan, ok := tracer.FindSpan("open-policy-agent.http") - assert.True(t, ok, "No span was created") + createdSpan := tracer.FindSpan("open-policy-agent.http") + require.NotNil(t, createdSpan, "No span was created") if tc.resperr == nil { assert.NoError(t, err) if tc.resp.StatusCode > 399 { - assert.Equal(t, true, createdSpan.Tags["error"], "Error tag was not set") + assert.Equal(t, true, createdSpan.Tag("error"), "Error tag was not set") } - assert.Equal(t, tc.resp.StatusCode, createdSpan.Tags[proxy.HTTPStatusCodeTag], "http status tag was not set") + assert.Equal(t, tc.resp.StatusCode, createdSpan.Tag(proxy.HTTPStatusCodeTag), "http status tag was not set") } else { - assert.Equal(t, true, createdSpan.Tags["error"], "Error tag was not set") + assert.Equal(t, true, createdSpan.Tag("error"), "Error tag was not set") assert.Equal(t, tc.resperr, err, "Error was not propagated") } assert.Equal(t, tc.resp, resp, "Response was not propagated") - assert.Equal(t, tc.req.Method, createdSpan.Tags["http.method"]) - assert.Equal(t, tc.req.URL.String(), createdSpan.Tags["http.url"]) - assert.Equal(t, tc.req.Host, createdSpan.Tags["hostname"]) - assert.Equal(t, tc.req.URL.Path, createdSpan.Tags["http.path"]) - assert.Equal(t, "skipper", createdSpan.Tags["component"]) - assert.Equal(t, "client", createdSpan.Tags["span.kind"]) - assert.Equal(t, "bundle", createdSpan.Tags["opa.bundle_name"]) - assert.Equal(t, "value", createdSpan.Tags["opa.label.label"]) + assert.Equal(t, tc.req.Method, createdSpan.Tag("http.method")) + assert.Equal(t, tc.req.URL.String(), createdSpan.Tag("http.url")) + assert.Equal(t, tc.req.Host, createdSpan.Tag("hostname")) + assert.Equal(t, tc.req.URL.Path, createdSpan.Tag("http.path")) + assert.Equal(t, "skipper", createdSpan.Tag("component")) + assert.Equal(t, "client", createdSpan.Tag("span.kind")) + assert.Equal(t, "bundle", createdSpan.Tag("opa.bundle_name")) + assert.Equal(t, "value", createdSpan.Tag("opa.label.label")) }) } } diff --git a/filters/tracing/baggagetotag_test.go b/filters/tracing/baggagetotag_test.go index a6909c6f15..99a39add83 100644 --- a/filters/tracing/baggagetotag_test.go +++ b/filters/tracing/baggagetotag_test.go @@ -25,7 +25,8 @@ func TestBaggageItemNameToTag(t *testing.T) { t.Run(ti.msg, func(t *testing.T) { req := &http.Request{Header: http.Header{}} - span := tracingtest.NewSpan("start_span") + tracer := tracingtest.NewTracer() + span := tracer.StartSpan("start_span").(*tracingtest.MockSpan) span.SetBaggageItem(ti.baggageItemName, ti.baggageItemValue) req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) ctx := &filtertest.Context{FRequest: req} @@ -38,7 +39,9 @@ func TestBaggageItemNameToTag(t *testing.T) { f.Request(ctx) - if tagValue := span.Tags[ti.tagName]; ti.baggageItemValue != tagValue { + span.Finish() + + if tagValue := span.Tag(ti.tagName); ti.baggageItemValue != tagValue { t.Error("couldn't set span tag from baggage item") } }) @@ -99,7 +102,8 @@ func TestFallbackToBaggageNameForTag(t *testing.T) { t.Run(ti.msg, func(t *testing.T) { req := &http.Request{Header: http.Header{}} - span := tracingtest.NewSpan("start_span") + tracer := tracingtest.NewTracer() + span := tracer.StartSpan("start_span").(*tracingtest.MockSpan) span.SetBaggageItem(ti.baggageItemName, ti.baggageItemValue) req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) ctx := &filtertest.Context{FRequest: req} @@ -112,7 +116,9 @@ func TestFallbackToBaggageNameForTag(t *testing.T) { f.Request(ctx) - if tagValue := span.Tags[ti.baggageItemName]; ti.baggageItemValue != tagValue { + span.Finish() + + if tagValue := span.Tag(ti.baggageItemName); ti.baggageItemValue != tagValue { t.Error("couldn't set span tag from baggage item") } }) diff --git a/filters/tracing/statebagtotag_test.go b/filters/tracing/statebagtotag_test.go index 66b1a4c3b7..723e156fad 100644 --- a/filters/tracing/statebagtotag_test.go +++ b/filters/tracing/statebagtotag_test.go @@ -16,7 +16,9 @@ import ( func TestStateBagToTag(t *testing.T) { req := &http.Request{Header: http.Header{}} - span := tracingtest.NewSpan("start_span") + tracer := tracingtest.NewTracer() + span := tracer.StartSpan("start_span").(*tracingtest.MockSpan) + req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) ctx := &filtertest.Context{FRequest: req, FStateBag: map[string]interface{}{"item": "val"}} @@ -25,13 +27,18 @@ func TestStateBagToTag(t *testing.T) { f.Request(ctx) - assert.Equal(t, "val", span.Tags["tag"]) + span.Finish() + + assert.Equal(t, "val", span.Tag("tag")) } func TestStateBagToTagAllocs(t *testing.T) { req := &http.Request{Header: http.Header{}} - span := tracingtest.NewSpan("start_span") + tracer := tracingtest.NewTracer() + span := tracer.StartSpan("start_span") + defer span.Finish() + req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) ctx := &filtertest.Context{FRequest: req, FStateBag: map[string]interface{}{"item": "val"}} @@ -100,7 +107,9 @@ func BenchmarkStateBagToTag_StringValue(b *testing.B) { f, err := NewStateBagToTag().CreateFilter([]interface{}{"item", "tag"}) require.NoError(b, err) - span := tracingtest.NewSpan("start_span") + tracer := tracingtest.NewTracer() + span := tracer.StartSpan("start_span").(*tracingtest.MockSpan) + defer span.Finish() req := &http.Request{Header: http.Header{}} req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)) @@ -108,7 +117,7 @@ func BenchmarkStateBagToTag_StringValue(b *testing.B) { ctx := &filtertest.Context{FRequest: req, FStateBag: map[string]interface{}{"item": "val"}} f.Request(ctx) - require.Equal(b, "val", span.Tags["tag"]) + require.Equal(b, "val", span.Tag("tag")) b.ReportAllocs() b.ResetTimer() diff --git a/filters/tracing/tag_test.go b/filters/tracing/tag_test.go index 529b7000ef..6135618a87 100644 --- a/filters/tracing/tag_test.go +++ b/filters/tracing/tag_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/filtertest" @@ -306,7 +305,7 @@ func TestTracingTag(t *testing.T) { }, } { t.Run(ti.name, func(t *testing.T) { - span := tracer.StartSpan("proxy").(*mocktracer.MockSpan) + span := tracer.StartSpan("proxy").(*tracingtest.MockSpan) defer span.Finish() requestContext := &filtertest.Context{ diff --git a/proxy/tracing_test.go b/proxy/tracing_test.go index 6e1ba1ae85..92d3ad3ff0 100644 --- a/proxy/tracing_test.go +++ b/proxy/tracing_test.go @@ -1,7 +1,6 @@ package proxy import ( - "crypto/md5" "fmt" "io" "math/rand" @@ -20,60 +19,6 @@ import ( "github.com/stretchr/testify/require" ) -const traceHeader = "X-Trace-Header" - -func TestTracingFromWire(t *testing.T) { - traceContent := fmt.Sprintf("%x", md5.New().Sum([]byte(time.Now().String()))) - s := startTestServer(nil, 0, func(r *http.Request) { - th, ok := r.Header[traceHeader] - if !ok { - t.Errorf("missing %s request header", traceHeader) - } else { - if th[0] != traceContent { - t.Errorf("wrong X-Trace-Header content: %s", th[0]) - } - } - }) - defer s.Close() - - u, _ := url.ParseRequestURI("https://www.example.org/hello") - r := &http.Request{ - URL: u, - Method: "GET", - Header: make(http.Header), - } - r.Header.Set(traceHeader, traceContent) - w := httptest.NewRecorder() - - doc := fmt.Sprintf(`hello: Path("/hello") -> "%s"`, s.URL) - tracer := &tracingtest.Tracer{} - params := Params{ - OpenTracing: &OpenTracingParams{ - Tracer: tracer, - }, - Flags: FlagsNone, - } - - tp, err := newTestProxyWithParams(doc, params) - if err != nil { - t.Error(err) - return - } - defer tp.close() - - tp.proxy.ServeHTTP(w, r) - - if len(tracer.RecordedSpans) == 0 { - t.Fatal("no span recorded...") - } - if tracer.RecordedSpans[0].Trace != traceContent { - t.Errorf("trace not found, got `%s` instead", tracer.RecordedSpans[0].Trace) - } - if len(tracer.RecordedSpans[0].Refs) == 0 { - t.Errorf("no references found, this is a root span") - } -} - func TestTracingIngressSpan(t *testing.T) { s := startTestServer(nil, 0, func(r *http.Request) { p := &mocktracer.TextMapPropagator{} @@ -117,8 +62,8 @@ func TestTracingIngressSpan(t *testing.T) { t.Fatal(err) } - span, ok := findSpan(tracer, "ingress") - if !ok { + span := tracer.FindSpan("ingress") + if span == nil { t.Fatal("ingress span not found") } @@ -172,8 +117,8 @@ func TestTracingIngressSpanShunt(t *testing.T) { defer rsp.Body.Close() io.Copy(io.Discard, rsp.Body) - span, ok := findSpan(tracer, "ingress") - if !ok { + span := tracer.FindSpan("ingress") + if span == nil { t.Fatal("ingress span not found") } @@ -264,17 +209,7 @@ func TestTracingIngressSpanLoopback(t *testing.T) { } func TestTracingSpanName(t *testing.T) { - traceContent := fmt.Sprintf("%x", md5.New().Sum([]byte(time.Now().String()))) - s := startTestServer(nil, 0, func(r *http.Request) { - th, ok := r.Header[traceHeader] - if !ok { - t.Errorf("missing %s request header", traceHeader) - } else { - if th[0] != traceContent { - t.Errorf("wrong X-Trace-Header content: %s", th[0]) - } - } - }) + s := startTestServer(nil, 0, func(r *http.Request) {}) defer s.Close() u, _ := url.ParseRequestURI("https://www.example.org/hello") @@ -286,7 +221,7 @@ func TestTracingSpanName(t *testing.T) { w := httptest.NewRecorder() doc := fmt.Sprintf(`hello: Path("/hello") -> tracingSpanName("test-span") -> "%s"`, s.URL) - tracer := &tracingtest.Tracer{TraceContent: traceContent} + tracer := tracingtest.NewTracer() params := Params{ OpenTracing: &OpenTracingParams{ Tracer: tracer, @@ -303,23 +238,13 @@ func TestTracingSpanName(t *testing.T) { tp.proxy.ServeHTTP(w, r) - if _, ok := tracer.FindSpan("test-span"); !ok { + if span := tracer.FindSpan("test-span"); span == nil { t.Error("setting the span name failed") } } func TestTracingInitialSpanName(t *testing.T) { - traceContent := fmt.Sprintf("%x", md5.New().Sum([]byte(time.Now().String()))) - s := startTestServer(nil, 0, func(r *http.Request) { - th, ok := r.Header[traceHeader] - if !ok { - t.Errorf("missing %s request header", traceHeader) - } else { - if th[0] != traceContent { - t.Errorf("wrong X-Trace-Header content: %s", th[0]) - } - } - }) + s := startTestServer(nil, 0, func(r *http.Request) {}) defer s.Close() u, _ := url.ParseRequestURI("https://www.example.org/hello") @@ -331,7 +256,7 @@ func TestTracingInitialSpanName(t *testing.T) { w := httptest.NewRecorder() doc := fmt.Sprintf(`hello: Path("/hello") -> "%s"`, s.URL) - tracer := &tracingtest.Tracer{TraceContent: traceContent} + tracer := tracingtest.NewTracer() params := Params{ OpenTracing: &OpenTracingParams{ Tracer: tracer, @@ -349,7 +274,7 @@ func TestTracingInitialSpanName(t *testing.T) { tp.proxy.ServeHTTP(w, r) - if _, ok := tracer.FindSpan("test-initial-span"); !ok { + if span := tracer.FindSpan("test-initial-span"); span == nil { t.Error("setting the span name failed") } } @@ -390,8 +315,8 @@ func TestTracingProxySpan(t *testing.T) { t.Fatal(err) } - span, ok := findSpan(tracer, "proxy") - if !ok { + span := tracer.FindSpan("proxy") + if span == nil { t.Fatal("proxy span not found") } @@ -437,7 +362,7 @@ func TestTracingProxySpanWithRetry(t *testing.T) { const docFmt = `r: * -> ;` doc := fmt.Sprintf(docFmt, s0.URL, s1.URL) - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() tp, err := newTestProxyWithParams(doc, Params{OpenTracing: &OpenTracingParams{Tracer: tracer}}) if err != nil { t.Fatal(err) @@ -445,7 +370,7 @@ func TestTracingProxySpanWithRetry(t *testing.T) { defer tp.close() testFallback := func() bool { - tracer.Reset("") + tracer.Reset() req, err := http.NewRequest("GET", "https://www.example.org", nil) if err != nil { t.Fatal(err) @@ -453,7 +378,13 @@ func TestTracingProxySpanWithRetry(t *testing.T) { tp.proxy.ServeHTTP(httptest.NewRecorder(), req) - proxySpans := tracer.FindAllSpans("proxy") + var proxySpans []*tracingtest.MockSpan + for _, span := range tracer.FinishedSpans() { + if span.OperationName == "proxy" { + proxySpans = append(proxySpans, span) + } + } + if len(proxySpans) != 2 { t.Log("invalid count of proxy spans", len(proxySpans)) return false @@ -551,7 +482,7 @@ func TestFilterTracing(t *testing.T) { } } -func spanLogs(span *mocktracer.MockSpan) string { +func spanLogs(span *tracingtest.MockSpan) string { var logs []string for _, e := range span.Logs() { for _, f := range e.Fields { @@ -573,7 +504,7 @@ func TestEnabledLogStreamEvents(t *testing.T) { tracing.logStreamEvent(span, "test-filter", StartEvent) tracing.logStreamEvent(span, "test-filter", EndEvent) - mockSpan := span.(*mocktracer.MockSpan) + mockSpan := span.(*tracingtest.MockSpan) if len(mockSpan.Logs()) != 2 { t.Errorf("filter lifecycle events were not logged although it was enabled") @@ -592,7 +523,7 @@ func TestDisabledLogStreamEvents(t *testing.T) { tracing.logStreamEvent(span, "test-filter", StartEvent) tracing.logStreamEvent(span, "test-filter", EndEvent) - mockSpan := span.(*mocktracer.MockSpan) + mockSpan := span.(*tracingtest.MockSpan) if len(mockSpan.Logs()) != 0 { t.Errorf("filter lifecycle events were logged although it was disabled") @@ -611,7 +542,7 @@ func TestSetEnabledTags(t *testing.T) { tracing.setTag(span, HTTPStatusCodeTag, 200) tracing.setTag(span, ComponentTag, "skipper") - mockSpan := span.(*mocktracer.MockSpan) + mockSpan := span.(*tracingtest.MockSpan) tags := mockSpan.Tags() @@ -638,7 +569,7 @@ func TestSetDisabledTags(t *testing.T) { tracing.setTag(span, ComponentTag, "skipper") tracing.setTag(span, SkipperRouteIDTag, "long_route_id") - mockSpan := span.(*mocktracer.MockSpan) + mockSpan := span.(*tracingtest.MockSpan) tags := mockSpan.Tags() @@ -676,16 +607,7 @@ func TestSetTagWithEmptySpan(t *testing.T) { tracing.setTag(nil, "test", "val") } -func findSpan(tracer *tracingtest.MockTracer, name string) (*mocktracer.MockSpan, bool) { - for _, s := range tracer.FinishedSpans() { - if s.OperationName == name { - return s, true - } - } - return nil, false -} - -func findSpanByRouteID(tracer *tracingtest.MockTracer, routeID string) (*mocktracer.MockSpan, bool) { +func findSpanByRouteID(tracer *tracingtest.MockTracer, routeID string) (*tracingtest.MockSpan, bool) { for _, s := range tracer.FinishedSpans() { if s.Tag(SkipperRouteIDTag) == routeID { return s, true @@ -694,21 +616,21 @@ func findSpanByRouteID(tracer *tracingtest.MockTracer, routeID string) (*mocktra return nil, false } -func verifyTag(t *testing.T, span *mocktracer.MockSpan, name string, expected interface{}) { +func verifyTag(t *testing.T, span *tracingtest.MockSpan, name string, expected interface{}) { t.Helper() if got := span.Tag(name); got != expected { t.Errorf("unexpected '%s' tag value: '%v' != '%v'", name, got, expected) } } -func verifyNoTag(t *testing.T, span *mocktracer.MockSpan, name string) { +func verifyNoTag(t *testing.T, span *tracingtest.MockSpan, name string) { t.Helper() if got, ok := span.Tags()[name]; ok { t.Errorf("unexpected '%s' tag: '%v'", name, got) } } -func verifyHasTag(t *testing.T, span *mocktracer.MockSpan, name string) { +func verifyHasTag(t *testing.T, span *tracingtest.MockSpan, name string) { t.Helper() if got, ok := span.Tags()[name]; !ok || got == "" { t.Errorf("expected '%s' tag", name) diff --git a/redis_test.go b/redis_test.go index e4429763a2..d495a07189 100644 --- a/redis_test.go +++ b/redis_test.go @@ -126,7 +126,7 @@ spec: rt := routing.New(ro) defer rt.Close() <-rt.FirstLoad() - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() pr := proxy.WithParams(proxy.Params{ Routing: rt, OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, @@ -301,7 +301,7 @@ spec: rt := routing.New(ro) defer rt.Close() <-rt.FirstLoad() - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() pr := proxy.WithParams(proxy.Params{ Routing: rt, OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, diff --git a/skipper_test.go b/skipper_test.go index c47bc979b5..051434ab29 100644 --- a/skipper_test.go +++ b/skipper_test.go @@ -132,7 +132,7 @@ func TestOptionsFilterRegistry(t *testing.T) { } func TestOptionsOpenTracingTracerInstanceOverridesOpenTracing(t *testing.T) { - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() o := Options{ OpenTracingTracer: tracer, OpenTracing: []string{"noop"}, @@ -574,7 +574,7 @@ func TestDataClients(t *testing.T) { rt := routing.New(ro) defer rt.Close() <-rt.FirstLoad() - tracer := &tracingtest.Tracer{} + tracer := tracingtest.NewTracer() pr := proxy.WithParams(proxy.Params{ Routing: rt, OpenTracing: &proxy.OpenTracingParams{Tracer: tracer}, diff --git a/tracing/tracingtest/mocktracer.go b/tracing/tracingtest/mocktracer.go index 9082c81a9f..f848d61f95 100644 --- a/tracing/tracingtest/mocktracer.go +++ b/tracing/tracingtest/mocktracer.go @@ -1,6 +1,7 @@ package tracingtest import ( + "fmt" "sync/atomic" "time" @@ -13,6 +14,11 @@ type MockTracer struct { spans atomic.Int32 } +type MockSpan struct { + *mocktracer.MockSpan + t *MockTracer +} + func NewTracer() *MockTracer { return &MockTracer{mockTracer: mocktracer.New()} } @@ -24,24 +30,37 @@ func (t *MockTracer) Reset() { func (t *MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span { t.spans.Add(1) - return t.mockTracer.StartSpan(operationName, opts...) + return &MockSpan{MockSpan: t.mockTracer.StartSpan(operationName, opts...).(*mocktracer.MockSpan), t: t} } -func (t *MockTracer) FinishedSpans() []*mocktracer.MockSpan { +func (t *MockTracer) FinishedSpans() []*MockSpan { timeout := time.After(1 * time.Second) retry := time.NewTicker(100 * time.Millisecond) defer retry.Stop() for { finished := t.mockTracer.FinishedSpans() if len(finished) == int(t.spans.Load()) { - return finished + result := make([]*MockSpan, len(finished)) + for i, s := range finished { + result[i] = &MockSpan{MockSpan: s, t: t} + } + return result } select { case <-retry.C: case <-timeout: - return nil + panic(fmt.Sprintf("Timeout waiting for %d finished spans, got: %d", t.spans.Load(), len(finished))) + } + } +} + +func (t *MockTracer) FindSpan(operationName string) *MockSpan { + for _, s := range t.FinishedSpans() { + if s.OperationName == operationName { + return s } } + return nil } func (t *MockTracer) Inject(sm opentracing.SpanContext, format any, carrier any) error { @@ -51,3 +70,7 @@ func (t *MockTracer) Inject(sm opentracing.SpanContext, format any, carrier any) func (t *MockTracer) Extract(format any, carrier any) (opentracing.SpanContext, error) { return t.mockTracer.Extract(format, carrier) } + +func (s *MockSpan) Tracer() opentracing.Tracer { + return s.t +} diff --git a/tracing/tracingtest/mocktracer_test.go b/tracing/tracingtest/mocktracer_test.go new file mode 100644 index 0000000000..902f4ae49d --- /dev/null +++ b/tracing/tracingtest/mocktracer_test.go @@ -0,0 +1,17 @@ +package tracingtest_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/tracing/tracingtest" +) + +func TestMockTracerSpanTracer(t *testing.T) { + tracer := tracingtest.NewTracer() + + span := tracer.StartSpan("test") + span.Finish() + + assert.Same(t, tracer, span.Tracer()) +} diff --git a/tracing/tracingtest/testtracer.go b/tracing/tracingtest/testtracer.go index 4da0a6d4ba..0227f74387 100644 --- a/tracing/tracingtest/testtracer.go +++ b/tracing/tracingtest/testtracer.go @@ -13,6 +13,8 @@ import ( // Tracer is an implementation of opentracing.Tracer for testing. It records // the defined spans during a series of operations. +// +// Deprecated: use [NewTracer] instead. type Tracer struct { // TraceContent represents the tracing content passed along the wire. @@ -44,6 +46,7 @@ type Span struct { tracer *Tracer } +// Deprecated: use [NewTracer] and [MockTracer.StartSpan] instead. func NewSpan(operation string) *Span { return &Span{ operationName: operation, From df9f6df76d405e7184b9a3036d82a005d6e7515b Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 20 Jan 2025 17:36:30 +0100 Subject: [PATCH 0999/1047] eskip: improve invalid predicate arguments error message (#3380) Add route id and predicate name to the error message. Signed-off-by: Alexander Yastrebov --- cmd/webhook/admission/admission_test.go | 2 +- ...tes-annotation-missing-header-argument.log | 2 +- eskip/eskip.go | 42 +++++++++++-------- eskip/eskip_test.go | 7 +++- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/cmd/webhook/admission/admission_test.go b/cmd/webhook/admission/admission_test.go index 118c5ba380..ff7abe9f3f 100644 --- a/cmd/webhook/admission/admission_test.go +++ b/cmd/webhook/admission/admission_test.go @@ -200,7 +200,7 @@ func TestIngressAdmitter(t *testing.T) { { name: "invalid eskip routes", inputFile: "invalid-routes.json", - message: `invalid \"zalando.org/skipper-routes\" annotation: invalid predicate count arg`, + message: `invalid \"zalando.org/skipper-routes\" annotation: invalid route \"r1\": Header predicate expects 2 string arguments`, }, { name: "invalid eskip filters and predicates", diff --git a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-invalid-routes-annotation-missing-header-argument.log b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-invalid-routes-annotation-missing-header-argument.log index 5f5ab84cfe..473a4cf4c8 100644 --- a/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-invalid-routes-annotation-missing-header-argument.log +++ b/dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-invalid-routes-annotation-missing-header-argument.log @@ -1 +1 @@ -\[ingress\] invalid \\\"zalando\.org\/skipper-routes\\\" annotation: invalid predicate count arg +\[ingress\] invalid \\\"zalando\.org\/skipper-routes\\\" annotation: invalid route \\\"r1\\\": Header predicate expects 2 string arguments diff --git a/eskip/eskip.go b/eskip/eskip.go index 229789b873..5bb5d39855 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -17,8 +17,6 @@ import ( const duplicateHeaderPredicateErrorFmt = "duplicate header predicate: %s" var ( - invalidPredicateArgError = errors.New("invalid predicate arg") - invalidPredicateArgCountError = errors.New("invalid predicate count arg") duplicatePathTreePredicateError = errors.New("duplicate path tree predicate") duplicateMethodPredicateError = errors.New("duplicate method predicate") ) @@ -446,17 +444,25 @@ func (t BackendType) String() string { } // Expects exactly n arguments of type string, or fails. -func getStringArgs(n int, args []interface{}) ([]string, error) { - if len(args) != n { - return nil, invalidPredicateArgCountError +func getStringArgs(p *Predicate, n int) ([]string, error) { + failure := func() ([]string, error) { + if n == 1 { + return nil, fmt.Errorf("%s predicate expects 1 string argument", p.Name) + } else { + return nil, fmt.Errorf("%s predicate expects %d string arguments", p.Name, n) + } + } + + if len(p.Args) != n { + return failure() } sargs := make([]string, n) - for i, a := range args { + for i, a := range p.Args { if sa, ok := a.(string); ok { sargs[i] = sa } else { - return nil, invalidPredicateArgError + return failure() } } @@ -475,26 +481,22 @@ func applyPredicates(route *Route, proute *parsedRoute) error { ) for _, p := range proute.predicates { - if err != nil { - return err - } - switch p.Name { case "Path": if pathSet { return duplicatePathTreePredicateError } - if args, err = getStringArgs(1, p.Args); err == nil { + if args, err = getStringArgs(p, 1); err == nil { route.Path = args[0] pathSet = true } case "Host": - if args, err = getStringArgs(1, p.Args); err == nil { + if args, err = getStringArgs(p, 1); err == nil { route.HostRegexps = append(route.HostRegexps, args[0]) } case "PathRegexp": - if args, err = getStringArgs(1, p.Args); err == nil { + if args, err = getStringArgs(p, 1); err == nil { route.PathRegexps = append(route.PathRegexps, args[0]) } case "Method": @@ -502,12 +504,12 @@ func applyPredicates(route *Route, proute *parsedRoute) error { return duplicateMethodPredicateError } - if args, err = getStringArgs(1, p.Args); err == nil { + if args, err = getStringArgs(p, 1); err == nil { route.Method = args[0] methodSet = true } case "HeaderRegexp": - if args, err = getStringArgs(2, p.Args); err == nil { + if args, err = getStringArgs(p, 2); err == nil { if route.HeaderRegexps == nil { route.HeaderRegexps = make(map[string][]string) } @@ -515,7 +517,7 @@ func applyPredicates(route *Route, proute *parsedRoute) error { route.HeaderRegexps[args[0]] = append(route.HeaderRegexps[args[0]], args[1]) } case "Header": - if args, err = getStringArgs(2, p.Args); err == nil { + if args, err = getStringArgs(p, 2); err == nil { if route.Headers == nil { route.Headers = make(map[string]string) } @@ -531,9 +533,13 @@ func applyPredicates(route *Route, proute *parsedRoute) error { default: route.Predicates = append(route.Predicates, p) } + + if err != nil { + return fmt.Errorf("invalid route %q: %w", proute.id, err) + } } - return err + return nil } // Converts a parsing route objects to the exported route definition with diff --git a/eskip/eskip_test.go b/eskip/eskip_test.go index 33dbee39f7..8cac584a47 100644 --- a/eskip/eskip_test.go +++ b/eskip/eskip_test.go @@ -72,7 +72,12 @@ func TestParse(t *testing.T) { "invalid method predicate", `Path("/endpoint") && Method("GET", "POST") -> "https://www.example.org"`, nil, - "invalid predicate count arg", + `invalid route "": Method predicate expects 1 string argument`, + }, { + "invalid header predicate", + `foo: Path("/endpoint") && Header("Foo") -> "https://www.example.org";`, + nil, + `invalid route "foo": Header predicate expects 2 string arguments`, }, { "host regexps", `Host(/^www[.]/) && Host(/[.]org$/) -> "https://www.example.org"`, From 017f25db054d27b09f6cd0f6a16b1c774acd89f3 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 20 Jan 2025 18:27:49 +0100 Subject: [PATCH 1000/1047] dataclients/kubernetes: append filters to routes of Ingress/RouteGroup having specific annotation (#3376) Similar to #3328 this change adds new flags to configure filters appended to routes created for Kubernetes resources having specific annotation. Signed-off-by: Alexander Yastrebov --- config/config.go | 210 ++++++++------ config/config_test.go | 271 +++++++++++------- dataclients/kubernetes/annotations.go | 16 +- dataclients/kubernetes/ingress.go | 66 +++-- dataclients/kubernetes/ingress_test.go | 6 + dataclients/kubernetes/ingressv1.go | 6 +- dataclients/kubernetes/kube.go | 9 +- .../kubernetes/kubernetestest/fixtures.go | 52 ++-- dataclients/kubernetes/routegroup.go | 6 +- dataclients/kubernetes/routegroups_test.go | 6 +- .../annotation-filters-append.eskip | 43 +++ .../annotation-filters-append.kube | 48 ++++ .../annotation-filters-append.yaml | 90 ++++++ .../annotation-filters-append.eskip | 57 ++++ .../annotation-filters-append.kube | 46 +++ .../annotation-filters-append.yaml | 77 +++++ skipper.go | 77 ++--- 17 files changed, 810 insertions(+), 276 deletions(-) create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.eskip create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.kube create mode 100644 dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.yaml create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.eskip create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.kube create mode 100644 dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.yaml diff --git a/config/config.go b/config/config.go index e945af39f5..8a4e2150c4 100644 --- a/config/config.go +++ b/config/config.go @@ -149,38 +149,42 @@ type Config struct { RefusePayload multiFlag `yaml:"refuse-payload"` // Kubernetes: - KubernetesIngress bool `yaml:"kubernetes"` - KubernetesInCluster bool `yaml:"kubernetes-in-cluster"` - KubernetesURL string `yaml:"kubernetes-url"` - KubernetesTokenFile string `yaml:"kubernetes-token-file"` - KubernetesHealthcheck bool `yaml:"kubernetes-healthcheck"` - KubernetesHTTPSRedirect bool `yaml:"kubernetes-https-redirect"` - KubernetesHTTPSRedirectCode int `yaml:"kubernetes-https-redirect-code"` - KubernetesDisableCatchAllRoutes bool `yaml:"kubernetes-disable-catchall-routes"` - KubernetesIngressClass string `yaml:"kubernetes-ingress-class"` - KubernetesRouteGroupClass string `yaml:"kubernetes-routegroup-class"` - WhitelistedHealthCheckCIDR string `yaml:"whitelisted-healthcheck-cidr"` - KubernetesPathModeString string `yaml:"kubernetes-path-mode"` - KubernetesPathMode kubernetes.PathMode `yaml:"-"` - KubernetesNamespace string `yaml:"kubernetes-namespace"` - KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` - KubernetesEnableEastWest bool `yaml:"enable-kubernetes-east-west"` - KubernetesEastWestDomain string `yaml:"kubernetes-east-west-domain"` - KubernetesEastWestRangeDomains *listFlag `yaml:"kubernetes-east-west-range-domains"` - KubernetesEastWestRangePredicatesString string `yaml:"kubernetes-east-west-range-predicates"` - KubernetesEastWestRangeAnnotationPredicatesString multiFlag `yaml:"kubernetes-east-west-range-annotation-predicates"` - KubernetesAnnotationPredicatesString multiFlag `yaml:"kubernetes-annotation-predicates"` - KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"-"` - KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"-"` - KubernetesEastWestRangePredicates []*eskip.Predicate `yaml:"-"` - KubernetesOnlyAllowedExternalNames bool `yaml:"kubernetes-only-allowed-external-names"` - KubernetesAllowedExternalNames regexpListFlag `yaml:"kubernetes-allowed-external-names"` - KubernetesRedisServiceNamespace string `yaml:"kubernetes-redis-service-namespace"` - KubernetesRedisServiceName string `yaml:"kubernetes-redis-service-name"` - KubernetesRedisServicePort int `yaml:"kubernetes-redis-service-port"` - KubernetesBackendTrafficAlgorithmString string `yaml:"kubernetes-backend-traffic-algorithm"` - KubernetesBackendTrafficAlgorithm kubernetes.BackendTrafficAlgorithm `yaml:"-"` - KubernetesDefaultLoadBalancerAlgorithm string `yaml:"kubernetes-default-lb-algorithm"` + KubernetesIngress bool `yaml:"kubernetes"` + KubernetesInCluster bool `yaml:"kubernetes-in-cluster"` + KubernetesURL string `yaml:"kubernetes-url"` + KubernetesTokenFile string `yaml:"kubernetes-token-file"` + KubernetesHealthcheck bool `yaml:"kubernetes-healthcheck"` + KubernetesHTTPSRedirect bool `yaml:"kubernetes-https-redirect"` + KubernetesHTTPSRedirectCode int `yaml:"kubernetes-https-redirect-code"` + KubernetesDisableCatchAllRoutes bool `yaml:"kubernetes-disable-catchall-routes"` + KubernetesIngressClass string `yaml:"kubernetes-ingress-class"` + KubernetesRouteGroupClass string `yaml:"kubernetes-routegroup-class"` + WhitelistedHealthCheckCIDR string `yaml:"whitelisted-healthcheck-cidr"` + KubernetesPathModeString string `yaml:"kubernetes-path-mode"` + KubernetesPathMode kubernetes.PathMode `yaml:"-"` + KubernetesNamespace string `yaml:"kubernetes-namespace"` + KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` + KubernetesEnableEastWest bool `yaml:"enable-kubernetes-east-west"` + KubernetesEastWestDomain string `yaml:"kubernetes-east-west-domain"` + KubernetesEastWestRangeDomains *listFlag `yaml:"kubernetes-east-west-range-domains"` + KubernetesEastWestRangePredicatesString string `yaml:"kubernetes-east-west-range-predicates"` + KubernetesEastWestRangeAnnotationPredicatesString multiFlag `yaml:"kubernetes-east-west-range-annotation-predicates"` + KubernetesEastWestRangeAnnotationFiltersAppendString multiFlag `yaml:"kubernetes-east-west-range-annotation-filters-append"` + KubernetesAnnotationPredicatesString multiFlag `yaml:"kubernetes-annotation-predicates"` + KubernetesAnnotationFiltersAppendString multiFlag `yaml:"kubernetes-annotation-filters-append"` + KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"-"` + KubernetesEastWestRangeAnnotationFiltersAppend []kubernetes.AnnotationFilters `yaml:"-"` + KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"-"` + KubernetesAnnotationFiltersAppend []kubernetes.AnnotationFilters `yaml:"-"` + KubernetesEastWestRangePredicates []*eskip.Predicate `yaml:"-"` + KubernetesOnlyAllowedExternalNames bool `yaml:"kubernetes-only-allowed-external-names"` + KubernetesAllowedExternalNames regexpListFlag `yaml:"kubernetes-allowed-external-names"` + KubernetesRedisServiceNamespace string `yaml:"kubernetes-redis-service-namespace"` + KubernetesRedisServiceName string `yaml:"kubernetes-redis-service-name"` + KubernetesRedisServicePort int `yaml:"kubernetes-redis-service-port"` + KubernetesBackendTrafficAlgorithmString string `yaml:"kubernetes-backend-traffic-algorithm"` + KubernetesBackendTrafficAlgorithm kubernetes.BackendTrafficAlgorithm `yaml:"-"` + KubernetesDefaultLoadBalancerAlgorithm string `yaml:"kubernetes-default-lb-algorithm"` // Default filters DefaultFiltersDir string `yaml:"default-filters-dir"` @@ -475,7 +479,9 @@ func NewConfig() *Config { flag.Var(cfg.KubernetesEastWestRangeDomains, "kubernetes-east-west-range-domains", "set the the cluster internal domains for east west traffic. Identified routes to such domains will include the -kubernetes-east-west-range-predicates") flag.StringVar(&cfg.KubernetesEastWestRangePredicatesString, "kubernetes-east-west-range-predicates", "", "set the predicates that will be appended to routes identified as to -kubernetes-east-west-range-domains") flag.Var(&cfg.KubernetesAnnotationPredicatesString, "kubernetes-annotation-predicates", "configures predicates appended to non east-west routes of annotated resources. E.g. -kubernetes-annotation-predicates='zone-a=true=Foo() && Bar()' will add 'Foo() && Bar()' predicates to all non east-west routes of ingress or routegroup annotated with 'zone-a: true'. For east-west routes use -kubernetes-east-west-range-annotation-predicates.") + flag.Var(&cfg.KubernetesAnnotationFiltersAppendString, "kubernetes-annotation-filters-append", "configures filters appended to non east-west routes of annotated resources. E.g. -kubernetes-annotation-filters-append='zone-a=true=foo() -> bar()' will add 'foo() -> bar()' filters to all non east-west routes of ingress or routegroup annotated with 'zone-a: true'. For east-west routes use -kubernetes-east-west-range-annotation-filters-append.") flag.Var(&cfg.KubernetesEastWestRangeAnnotationPredicatesString, "kubernetes-east-west-range-annotation-predicates", "similar to -kubernetes-annotation-predicates configures predicates appended to east-west routes of annotated resources. See also -kubernetes-east-west-range-domains.") + flag.Var(&cfg.KubernetesEastWestRangeAnnotationFiltersAppendString, "kubernetes-east-west-range-annotation-filters-append", "similar to -kubernetes-annotation-filters-append configures filters appended to east-west routes of annotated resources. See also -kubernetes-east-west-range-domains.") flag.BoolVar(&cfg.KubernetesOnlyAllowedExternalNames, "kubernetes-only-allowed-external-names", false, "only accept external name services, route group network backends and route group explicit LB endpoints from an allow list defined by zero or more -kubernetes-allowed-external-name flags") flag.Var(&cfg.KubernetesAllowedExternalNames, "kubernetes-allowed-external-name", "set zero or more regular expressions from which at least one should be matched by the external name services, route group network addresses and explicit endpoints domain names") flag.StringVar(&cfg.KubernetesRedisServiceNamespace, "kubernetes-redis-service-namespace", "", "Sets namespace for redis to be used to lookup endpoints") @@ -623,11 +629,21 @@ func validate(c *Config) error { return fmt.Errorf("invalid annotation predicates: %q, %w", c.KubernetesAnnotationPredicatesString, err) } + _, err = parseAnnotationFilters(c.KubernetesAnnotationFiltersAppendString) + if err != nil { + return fmt.Errorf("invalid annotation filters: %q, %w", c.KubernetesAnnotationFiltersAppendString, err) + } + _, err = parseAnnotationPredicates(c.KubernetesEastWestRangeAnnotationPredicatesString) if err != nil { return fmt.Errorf("invalid east-west annotation predicates: %q, %w", c.KubernetesEastWestRangeAnnotationPredicatesString, err) } + _, err = parseAnnotationFilters(c.KubernetesEastWestRangeAnnotationFiltersAppendString) + if err != nil { + return fmt.Errorf("invalid east-west annotation filters: %q, %w", c.KubernetesEastWestRangeAnnotationFiltersAppendString, err) + } + _, err = kubernetes.ParseBackendTrafficAlgorithm(c.KubernetesBackendTrafficAlgorithmString) if err != nil { return err @@ -691,7 +707,9 @@ func (c *Config) ParseArgs(progname string, args []string) error { c.KubernetesPathMode, _ = kubernetes.ParsePathMode(c.KubernetesPathModeString) c.KubernetesEastWestRangePredicates, _ = eskip.ParsePredicates(c.KubernetesEastWestRangePredicatesString) c.KubernetesAnnotationPredicates, _ = parseAnnotationPredicates(c.KubernetesAnnotationPredicatesString) + c.KubernetesAnnotationFiltersAppend, _ = parseAnnotationFilters(c.KubernetesAnnotationFiltersAppendString) c.KubernetesEastWestRangeAnnotationPredicates, _ = parseAnnotationPredicates(c.KubernetesEastWestRangeAnnotationPredicatesString) + c.KubernetesEastWestRangeAnnotationFiltersAppend, _ = parseAnnotationFilters(c.KubernetesEastWestRangeAnnotationFiltersAppendString) c.KubernetesBackendTrafficAlgorithm, _ = kubernetes.ParseBackendTrafficAlgorithm(c.KubernetesBackendTrafficAlgorithmString) c.HistogramMetricBuckets, _ = c.parseHistogramBuckets() @@ -834,33 +852,35 @@ func (c *Config) ToOptions() skipper.Options { WaitFirstRouteLoad: c.WaitFirstRouteLoad, // Kubernetes: - Kubernetes: c.KubernetesIngress, - KubernetesInCluster: c.KubernetesInCluster, - KubernetesURL: c.KubernetesURL, - KubernetesTokenFile: c.KubernetesTokenFile, - KubernetesHealthcheck: c.KubernetesHealthcheck, - KubernetesHTTPSRedirect: c.KubernetesHTTPSRedirect, - KubernetesHTTPSRedirectCode: c.KubernetesHTTPSRedirectCode, - KubernetesDisableCatchAllRoutes: c.KubernetesDisableCatchAllRoutes, - KubernetesIngressClass: c.KubernetesIngressClass, - KubernetesRouteGroupClass: c.KubernetesRouteGroupClass, - WhitelistedHealthCheckCIDR: whitelistCIDRS, - KubernetesPathMode: c.KubernetesPathMode, - KubernetesNamespace: c.KubernetesNamespace, - KubernetesEnableEndpointslices: c.KubernetesEnableEndpointSlices, - KubernetesEnableEastWest: c.KubernetesEnableEastWest, - KubernetesEastWestDomain: c.KubernetesEastWestDomain, - KubernetesEastWestRangeDomains: c.KubernetesEastWestRangeDomains.values, - KubernetesEastWestRangePredicates: c.KubernetesEastWestRangePredicates, - KubernetesEastWestRangeAnnotationPredicates: c.KubernetesEastWestRangeAnnotationPredicates, - KubernetesAnnotationPredicates: c.KubernetesAnnotationPredicates, - KubernetesOnlyAllowedExternalNames: c.KubernetesOnlyAllowedExternalNames, - KubernetesAllowedExternalNames: c.KubernetesAllowedExternalNames, - KubernetesRedisServiceNamespace: c.KubernetesRedisServiceNamespace, - KubernetesRedisServiceName: c.KubernetesRedisServiceName, - KubernetesRedisServicePort: c.KubernetesRedisServicePort, - KubernetesBackendTrafficAlgorithm: c.KubernetesBackendTrafficAlgorithm, - KubernetesDefaultLoadBalancerAlgorithm: c.KubernetesDefaultLoadBalancerAlgorithm, + Kubernetes: c.KubernetesIngress, + KubernetesInCluster: c.KubernetesInCluster, + KubernetesURL: c.KubernetesURL, + KubernetesTokenFile: c.KubernetesTokenFile, + KubernetesHealthcheck: c.KubernetesHealthcheck, + KubernetesHTTPSRedirect: c.KubernetesHTTPSRedirect, + KubernetesHTTPSRedirectCode: c.KubernetesHTTPSRedirectCode, + KubernetesDisableCatchAllRoutes: c.KubernetesDisableCatchAllRoutes, + KubernetesIngressClass: c.KubernetesIngressClass, + KubernetesRouteGroupClass: c.KubernetesRouteGroupClass, + WhitelistedHealthCheckCIDR: whitelistCIDRS, + KubernetesPathMode: c.KubernetesPathMode, + KubernetesNamespace: c.KubernetesNamespace, + KubernetesEnableEndpointslices: c.KubernetesEnableEndpointSlices, + KubernetesEnableEastWest: c.KubernetesEnableEastWest, + KubernetesEastWestDomain: c.KubernetesEastWestDomain, + KubernetesEastWestRangeDomains: c.KubernetesEastWestRangeDomains.values, + KubernetesEastWestRangePredicates: c.KubernetesEastWestRangePredicates, + KubernetesEastWestRangeAnnotationPredicates: c.KubernetesEastWestRangeAnnotationPredicates, + KubernetesEastWestRangeAnnotationFiltersAppend: c.KubernetesEastWestRangeAnnotationFiltersAppend, + KubernetesAnnotationPredicates: c.KubernetesAnnotationPredicates, + KubernetesAnnotationFiltersAppend: c.KubernetesAnnotationFiltersAppend, + KubernetesOnlyAllowedExternalNames: c.KubernetesOnlyAllowedExternalNames, + KubernetesAllowedExternalNames: c.KubernetesAllowedExternalNames, + KubernetesRedisServiceNamespace: c.KubernetesRedisServiceNamespace, + KubernetesRedisServiceName: c.KubernetesRedisServiceName, + KubernetesRedisServicePort: c.KubernetesRedisServicePort, + KubernetesBackendTrafficAlgorithm: c.KubernetesBackendTrafficAlgorithm, + KubernetesDefaultLoadBalancerAlgorithm: c.KubernetesDefaultLoadBalancerAlgorithm, // API Monitoring: ApiUsageMonitoringEnable: c.ApiUsageMonitoringEnable, @@ -1173,42 +1193,70 @@ func (c *Config) checkDeprecated(configKeys map[string]interface{}, options ...s } func parseAnnotationPredicates(s []string) ([]kubernetes.AnnotationPredicates, error) { - var annotationPredicates []kubernetes.AnnotationPredicates + return parseAnnotationConfig(s, func(annotationKey, annotationValue, value string) (kubernetes.AnnotationPredicates, error) { + predicates, err := eskip.ParsePredicates(value) + if err != nil { + var zero kubernetes.AnnotationPredicates + return zero, err + } + return kubernetes.AnnotationPredicates{ + Key: annotationKey, + Value: annotationValue, + Predicates: predicates, + }, nil + }) +} - for _, annotationPredicate := range s { - if annotationPredicate == "" { +func parseAnnotationFilters(s []string) ([]kubernetes.AnnotationFilters, error) { + return parseAnnotationConfig(s, func(annotationKey, annotationValue, value string) (kubernetes.AnnotationFilters, error) { + filters, err := eskip.ParseFilters(value) + if err != nil { + var zero kubernetes.AnnotationFilters + return zero, err + } + return kubernetes.AnnotationFilters{ + Key: annotationKey, + Value: annotationValue, + Filters: filters, + }, nil + }) +} + +// parseAnnotationConfig parses a slice of strings in the "annotationKey=annotationValue=value" format +// by calling parseValue function to convert (annotationKey, annotationValue, value) tuple into T. +// Empty input strings are skipped and duplicate annotationKey-annotationValue pairs are rejected with error. +func parseAnnotationConfig[T any](kvvs []string, parseValue func(annotationKey, annotationValue, value string) (T, error)) ([]T, error) { + var result []T + seenKVs := make(map[string]struct{}) + for _, kvv := range kvvs { + if kvv == "" { continue } - annotationKey, rest, found := strings.Cut(annotationPredicate, "=") + annotationKey, rest, found := strings.Cut(kvv, "=") if !found { - return nil, fmt.Errorf("invalid annotation predicate flag: %q, failed to get annotation key", annotationPredicate) + return nil, fmt.Errorf("invalid annotation flag: %q, failed to get annotation key", kvv) } - annotationValue, predicates, found := strings.Cut(rest, "=") + annotationValue, value, found := strings.Cut(rest, "=") if !found { - return nil, fmt.Errorf("invalid annotation predicate flag: %q, faild to get annotation value", annotationPredicate) + return nil, fmt.Errorf("invalid annotation flag: %q, failed to get annotation value", kvv) } - predicateList, err := eskip.ParsePredicates(predicates) + v, err := parseValue(annotationKey, annotationValue, value) if err != nil { - return nil, fmt.Errorf("invalid annotation predicate flag: %q, %w", annotationPredicate, err) + return nil, fmt.Errorf("invalid annotation flag value: %q, %w", kvv, err) } - // We throw an err because having duplicate annotation keys will override each others - for _, ap := range annotationPredicates { - if ap.Key == annotationKey && ap.Value == annotationValue { - return nil, fmt.Errorf("invalid annotation predicate flag: %q, duplicate annotation key and value", annotationPredicate) - } + // Reject duplicate annotation key-value pairs + kv := annotationKey + "=" + annotationValue + if _, ok := seenKVs[kv]; ok { + return nil, fmt.Errorf("invalid annotation flag: %q, duplicate annotation key-value %q", kvv, kv) + } else { + seenKVs[kv] = struct{}{} } - annotationPredicates = append(annotationPredicates, kubernetes.AnnotationPredicates{ - Key: annotationKey, - Value: annotationValue, - Predicates: predicateList, - }) + result = append(result, v) } - - return annotationPredicates, nil - + return result, nil } diff --git a/config/config_test.go b/config/config_test.go index cf49e2c42d..cb4cd03c1a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -495,117 +495,190 @@ func TestMultiFlagYamlErr(t *testing.T) { } } -func TestAnnotationPredicatesParsingInvalid(t *testing.T) { - for _, tc := range []struct { - name string - input []string - }{ - { - name: "wrong predicate", - input: []string{`to-add-predicate=true="Fo_o("123")"`}, - }, - { - name: "wrong predicate and empty value", - input: []string{"", `to-add-predicate=true="Fo_o()"`}, - }, - { - name: "duplicate", - input: []string{`to-add-predicate=true=Foo("123")`, `to-add-predicate=true=Bar("456")`}, - }, - } { - t.Run(tc.name, func(t *testing.T) { - _, err := parseAnnotationPredicates(tc.input) - require.Error(t, err) - }) - } +func TestParseAnnotationConfigInvalid(t *testing.T) { + t.Run("parseAnnotationPredicates", func(t *testing.T) { + for _, tc := range []struct { + name string + input []string + }{ + { + name: "wrong predicate", + input: []string{`to-add-predicate=true="Fo_o("123")"`}, + }, + { + name: "wrong predicate and empty value", + input: []string{"", `to-add-predicate=true="Fo_o()"`}, + }, + { + name: "duplicate", + input: []string{`to-add-predicate=true=Foo("123")`, `to-add-predicate=true=Bar("456")`}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := parseAnnotationPredicates(tc.input) + assert.Error(t, err) + }) + } + }) + + t.Run("parseAnnotationFilters", func(t *testing.T) { + for _, tc := range []struct { + name string + input []string + }{ + { + name: "invalid filter", + input: []string{`foo=bar=invalid-filter()`}, + }, + { + name: "invalid filter and empty value", + input: []string{"", `foo=bar=invalid-filter()`}, + }, + { + name: "duplicate", + input: []string{`foo=bar=baz("123")`, `foo=bar=qux("456")`}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := parseAnnotationFilters(tc.input) + assert.Error(t, err) + }) + } + }) } -func TestAnnotationPredicatesParsingValid(t *testing.T) { - for _, tc := range []struct { - name string - input []string - expected []kubernetes.AnnotationPredicates - }{ - { - name: "empty", - input: []string{}, - expected: nil, - }, - { - name: "empty string", - input: []string{""}, - expected: nil, - }, - { - name: "empty string and a valid value", - input: []string{ - "", - `to-add-predicate=true=Foo("123")`, +func TestParseAnnotationConfig(t *testing.T) { + t.Run("parseAnnotationPredicates", func(t *testing.T) { + + for _, tc := range []struct { + name string + input []string + expected []kubernetes.AnnotationPredicates + }{ + { + name: "empty", + input: []string{}, + expected: nil, }, - expected: []kubernetes.AnnotationPredicates{ - { - Key: "to-add-predicate", - Value: "true", - Predicates: []*eskip.Predicate{ - { - Name: "Foo", - Args: []any{"123"}, - }, + { + name: "empty string", + input: []string{""}, + expected: nil, + }, + { + name: "empty string and a valid value", + input: []string{ + "", + `to-add-predicate=true=Foo("123")`, + }, + expected: []kubernetes.AnnotationPredicates{ + { + Key: "to-add-predicate", + Value: "true", + Predicates: eskip.MustParsePredicates(`Foo("123")`), }, }, }, - }, - { - name: "single", - input: []string{`to-add-predicate=true=Foo("123")`}, - expected: []kubernetes.AnnotationPredicates{ - { - Key: "to-add-predicate", - Value: "true", - Predicates: []*eskip.Predicate{ - { - Name: "Foo", - Args: []any{"123"}, - }, + { + name: "single", + input: []string{`to-add-predicate=true=Foo("123")`}, + expected: []kubernetes.AnnotationPredicates{ + { + Key: "to-add-predicate", + Value: "true", + Predicates: eskip.MustParsePredicates(`Foo("123")`), }, }, }, - }, - { - name: "multiple", - input: []string{`to-add-predicate=true=Foo("123")`, `to-add-predicate=false=Bar("456") && Foo("789")`}, - expected: []kubernetes.AnnotationPredicates{ - { - Key: "to-add-predicate", - Value: "true", - Predicates: []*eskip.Predicate{ - { - Name: "Foo", - Args: []any{"123"}, - }, + { + name: "multiple", + input: []string{`to-add-predicate=true=Foo("123")`, `to-add-predicate=false=Bar("456") && Foo("789")`}, + expected: []kubernetes.AnnotationPredicates{ + { + Key: "to-add-predicate", + Value: "true", + Predicates: eskip.MustParsePredicates(`Foo("123")`), + }, + { + Key: "to-add-predicate", + Value: "false", + Predicates: eskip.MustParsePredicates(`Bar("456") && Foo("789")`), + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + val, err := parseAnnotationPredicates(tc.input) + require.NoError(t, err) + assert.Equal(t, tc.expected, val) + }) + } + }) + + t.Run("parseAnnotationFilters", func(t *testing.T) { + + for _, tc := range []struct { + name string + input []string + expected []kubernetes.AnnotationFilters + }{ + { + name: "empty", + input: []string{}, + expected: nil, + }, + { + name: "empty string", + input: []string{""}, + expected: nil, + }, + { + name: "empty string and a valid value", + input: []string{ + "", + `foo=true=baz("123=456")`, + }, + expected: []kubernetes.AnnotationFilters{ + { + Key: "foo", + Value: "true", + Filters: eskip.MustParseFilters(`baz("123=456")`), }, }, - { - Key: "to-add-predicate", - Value: "false", - Predicates: []*eskip.Predicate{ - { - Name: "Bar", - Args: []any{"456"}, - }, - { - Name: "Foo", - Args: []any{"789"}, - }, + }, + { + name: "single", + input: []string{`foo=true=baz("123=456")`}, + expected: []kubernetes.AnnotationFilters{ + { + Key: "foo", + Value: "true", + Filters: eskip.MustParseFilters(`baz("123=456")`), }, }, }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - val, err := parseAnnotationPredicates(tc.input) - require.NoError(t, err) - assert.Equal(t, tc.expected, val) - }) - } + { + name: "multiple", + input: []string{`foo=true=baz("123=456")`, `foo=false=bar("456") -> foo("789")`}, + expected: []kubernetes.AnnotationFilters{ + { + Key: "foo", + Value: "true", + Filters: eskip.MustParseFilters(`baz("123=456")`), + }, + { + Key: "foo", + Value: "false", + Filters: eskip.MustParseFilters(`bar("456") -> foo("789")`), + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + val, err := parseAnnotationFilters(tc.input) + require.NoError(t, err) + assert.Equal(t, tc.expected, val) + }) + } + }) } diff --git a/dataclients/kubernetes/annotations.go b/dataclients/kubernetes/annotations.go index 3e0dd95ea1..716a6e65aa 100644 --- a/dataclients/kubernetes/annotations.go +++ b/dataclients/kubernetes/annotations.go @@ -10,7 +10,13 @@ type AnnotationPredicates struct { Predicates []*eskip.Predicate } -func addAnnotationPredicates(annotationPredicates []AnnotationPredicates, annotations map[string]string, r *eskip.Route) { +type AnnotationFilters struct { + Key string + Value string + Filters []*eskip.Filter +} + +func appendAnnotationPredicates(annotationPredicates []AnnotationPredicates, annotations map[string]string, r *eskip.Route) { for _, ap := range annotationPredicates { if objAnnotationVal, ok := annotations[ap.Key]; ok && ap.Value == objAnnotationVal { // since this annotation is managed by skipper operator, we can safely assume that the predicate is valid @@ -19,3 +25,11 @@ func addAnnotationPredicates(annotationPredicates []AnnotationPredicates, annota } } } + +func appendAnnotationFilters(annotationFilters []AnnotationFilters, annotations map[string]string, r *eskip.Route) { + for _, af := range annotationFilters { + if objAnnotationVal, ok := annotations[af.Key]; ok && af.Value == objAnnotationVal { + r.Filters = append(r.Filters, af.Filters...) + } + } +} diff --git a/dataclients/kubernetes/ingress.go b/dataclients/kubernetes/ingress.go index 27896408ec..2315503a24 100644 --- a/dataclients/kubernetes/ingress.go +++ b/dataclients/kubernetes/ingress.go @@ -46,20 +46,22 @@ type ingressContext struct { } type ingress struct { - eastWestRangeDomains []string - eastWestRangePredicates []*eskip.Predicate - allowedExternalNames []*regexp.Regexp - kubernetesEastWestDomain string - pathMode PathMode - httpsRedirectCode int - kubernetesEnableEastWest bool - provideHTTPSRedirect bool - disableCatchAllRoutes bool - forceKubernetesService bool - backendTrafficAlgorithm BackendTrafficAlgorithm - defaultLoadBalancerAlgorithm string - kubernetesAnnotationPredicates []AnnotationPredicates - kubernetesEastWestRangeAnnotationPredicates []AnnotationPredicates + eastWestRangeDomains []string + eastWestRangePredicates []*eskip.Predicate + allowedExternalNames []*regexp.Regexp + kubernetesEastWestDomain string + pathMode PathMode + httpsRedirectCode int + kubernetesEnableEastWest bool + provideHTTPSRedirect bool + disableCatchAllRoutes bool + forceKubernetesService bool + backendTrafficAlgorithm BackendTrafficAlgorithm + defaultLoadBalancerAlgorithm string + kubernetesAnnotationPredicates []AnnotationPredicates + kubernetesAnnotationFiltersAppend []AnnotationFilters + kubernetesEastWestRangeAnnotationPredicates []AnnotationPredicates + kubernetesEastWestRangeAnnotationFiltersAppend []AnnotationFilters } var nonWord = regexp.MustCompile(`\W`) @@ -72,20 +74,22 @@ func (ic *ingressContext) addHostRoute(host string, route *eskip.Route) { func newIngress(o Options) *ingress { return &ingress{ - provideHTTPSRedirect: o.ProvideHTTPSRedirect, - httpsRedirectCode: o.HTTPSRedirectCode, - disableCatchAllRoutes: o.DisableCatchAllRoutes, - pathMode: o.PathMode, - kubernetesEnableEastWest: o.KubernetesEnableEastWest, - kubernetesEastWestDomain: o.KubernetesEastWestDomain, - eastWestRangeDomains: o.KubernetesEastWestRangeDomains, - eastWestRangePredicates: o.KubernetesEastWestRangePredicates, - allowedExternalNames: o.AllowedExternalNames, - forceKubernetesService: o.ForceKubernetesService, - backendTrafficAlgorithm: o.BackendTrafficAlgorithm, - defaultLoadBalancerAlgorithm: o.DefaultLoadBalancerAlgorithm, - kubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates, - kubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates, + provideHTTPSRedirect: o.ProvideHTTPSRedirect, + httpsRedirectCode: o.HTTPSRedirectCode, + disableCatchAllRoutes: o.DisableCatchAllRoutes, + pathMode: o.PathMode, + kubernetesEnableEastWest: o.KubernetesEnableEastWest, + kubernetesEastWestDomain: o.KubernetesEastWestDomain, + eastWestRangeDomains: o.KubernetesEastWestRangeDomains, + eastWestRangePredicates: o.KubernetesEastWestRangePredicates, + allowedExternalNames: o.AllowedExternalNames, + forceKubernetesService: o.ForceKubernetesService, + backendTrafficAlgorithm: o.BackendTrafficAlgorithm, + defaultLoadBalancerAlgorithm: o.DefaultLoadBalancerAlgorithm, + kubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates, + kubernetesAnnotationFiltersAppend: o.KubernetesAnnotationFiltersAppend, + kubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates, + kubernetesEastWestRangeAnnotationFiltersAppend: o.KubernetesEastWestRangeAnnotationFiltersAppend, } } @@ -203,9 +207,11 @@ func (ing *ingress) addExtraRoutes(ic *ingressContext, ruleHost, path, pathType setPathV1(ic.pathMode, &route, pathType, path) if n := countPathPredicates(&route); n <= 1 { if ewHost { - addAnnotationPredicates(ing.kubernetesEastWestRangeAnnotationPredicates, ic.ingressV1.Metadata.Annotations, &route) + appendAnnotationPredicates(ing.kubernetesEastWestRangeAnnotationPredicates, ic.ingressV1.Metadata.Annotations, &route) + appendAnnotationFilters(ing.kubernetesEastWestRangeAnnotationFiltersAppend, ic.ingressV1.Metadata.Annotations, &route) } else { - addAnnotationPredicates(ing.kubernetesAnnotationPredicates, ic.ingressV1.Metadata.Annotations, &route) + appendAnnotationPredicates(ing.kubernetesAnnotationPredicates, ic.ingressV1.Metadata.Annotations, &route) + appendAnnotationFilters(ing.kubernetesAnnotationFiltersAppend, ic.ingressV1.Metadata.Annotations, &route) } ic.addHostRoute(ruleHost, &route) ic.redirect.updateHost(ruleHost) diff --git a/dataclients/kubernetes/ingress_test.go b/dataclients/kubernetes/ingress_test.go index 9c7f5a1c68..3c05bd5e7c 100644 --- a/dataclients/kubernetes/ingress_test.go +++ b/dataclients/kubernetes/ingress_test.go @@ -20,6 +20,12 @@ func TestIngressV1Fixtures(t *testing.T) { "testdata/ingressV1/traffic", "testdata/ingressV1/traffic-segment", "testdata/ingressV1/loadbalancer-algorithm", + ) +} + +func TestIngressV1AnnotationConfig(t *testing.T) { + kubernetestest.FixturesToTest(t, "testdata/ingressV1/annotation-predicates", + "testdata/ingressV1/annotation-filters", ) } diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index fed5caeed6..8b525fe2b4 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -230,9 +230,11 @@ func (ing *ingress) addEndpointsRuleV1(ic *ingressContext, host string, prule *d redirect.setHost(host) } - addAnnotationPredicates(ing.kubernetesAnnotationPredicates, meta.Annotations, endpointsRoute) + appendAnnotationPredicates(ing.kubernetesAnnotationPredicates, meta.Annotations, endpointsRoute) + appendAnnotationFilters(ing.kubernetesAnnotationFiltersAppend, meta.Annotations, endpointsRoute) } else { - addAnnotationPredicates(ing.kubernetesEastWestRangeAnnotationPredicates, meta.Annotations, endpointsRoute) + appendAnnotationPredicates(ing.kubernetesEastWestRangeAnnotationPredicates, meta.Annotations, endpointsRoute) + appendAnnotationFilters(ing.kubernetesEastWestRangeAnnotationFiltersAppend, meta.Annotations, endpointsRoute) } if ing.kubernetesEnableEastWest { diff --git a/dataclients/kubernetes/kube.go b/dataclients/kubernetes/kube.go index 671c4e2788..a66d0fe237 100644 --- a/dataclients/kubernetes/kube.go +++ b/dataclients/kubernetes/kube.go @@ -212,9 +212,16 @@ type Options struct { // routes that has KubernetesEastWestRangeDomains suffix. KubernetesEastWestRangeAnnotationPredicates []AnnotationPredicates - // KubernetesAnnotationPredicates set a list predicates for each annotation key and value + // KubernetesEastWestRangeAnnotationFiltersAppend same as KubernetesAnnotationFiltersAppend but will append to + // routes that has KubernetesEastWestRangeDomains suffix. + KubernetesEastWestRangeAnnotationFiltersAppend []AnnotationFilters + + // KubernetesAnnotationPredicates sets predicates to append for each annotation key and value KubernetesAnnotationPredicates []AnnotationPredicates + // KubernetesAnnotationFiltersAppend sets filters to append for each annotation key and value + KubernetesAnnotationFiltersAppend []AnnotationFilters + // DefaultFiltersDir enables default filters mechanism and sets the location of the default filters. // The provided filters are then applied to all routes. DefaultFiltersDir string diff --git a/dataclients/kubernetes/kubernetestest/fixtures.go b/dataclients/kubernetes/kubernetestest/fixtures.go index a17aa7ea40..d45f7dc3be 100644 --- a/dataclients/kubernetes/kubernetestest/fixtures.go +++ b/dataclients/kubernetes/kubernetestest/fixtures.go @@ -32,29 +32,31 @@ type fixtureSet struct { } type kubeOptionsParser struct { - IngressV1 bool `yaml:"ingressv1"` - EastWest bool `yaml:"eastWest"` - EastWestDomain string `yaml:"eastWestDomain"` - EastWestRangeDomains []string `yaml:"eastWestRangeDomains"` - EastWestRangePredicates []*eskip.Predicate `yaml:"eastWestRangePredicatesAppend"` - HTTPSRedirect bool `yaml:"httpsRedirect"` - HTTPSRedirectCode int `yaml:"httpsRedirectCode"` - DisableCatchAllRoutes bool `yaml:"disableCatchAllRoutes"` - BackendNameTracingTag bool `yaml:"backendNameTracingTag"` - OnlyAllowedExternalNames bool `yaml:"onlyAllowedExternalNames"` - AllowedExternalNames []string `yaml:"allowedExternalNames"` - IngressClass string `yaml:"kubernetes-ingress-class"` - KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` - KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` - IngressesLabels map[string]string `yaml:"kubernetes-ingresses-label-selector"` - ServicesLabels map[string]string `yaml:"kubernetes-services-label-selector"` - EndpointsLabels map[string]string `yaml:"kubernetes-endpoints-label-selector"` - EndpointsliceLabels map[string]string `yaml:"kubernetes-endpointslice-label-selector"` - ForceKubernetesService bool `yaml:"force-kubernetes-service"` - BackendTrafficAlgorithm string `yaml:"backend-traffic-algorithm"` - DefaultLoadBalancerAlgorithm string `yaml:"default-lb-algorithm"` - KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"kubernetesAnnotationPredicates"` - KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"kubernetesEastWestRangeAnnotationPredicates"` + IngressV1 bool `yaml:"ingressv1"` + EastWest bool `yaml:"eastWest"` + EastWestDomain string `yaml:"eastWestDomain"` + EastWestRangeDomains []string `yaml:"eastWestRangeDomains"` + EastWestRangePredicates []*eskip.Predicate `yaml:"eastWestRangePredicatesAppend"` + HTTPSRedirect bool `yaml:"httpsRedirect"` + HTTPSRedirectCode int `yaml:"httpsRedirectCode"` + DisableCatchAllRoutes bool `yaml:"disableCatchAllRoutes"` + BackendNameTracingTag bool `yaml:"backendNameTracingTag"` + OnlyAllowedExternalNames bool `yaml:"onlyAllowedExternalNames"` + AllowedExternalNames []string `yaml:"allowedExternalNames"` + IngressClass string `yaml:"kubernetes-ingress-class"` + KubernetesEnableEndpointSlices bool `yaml:"enable-kubernetes-endpointslices"` + KubernetesEnableTLS bool `yaml:"kubernetes-enable-tls"` + IngressesLabels map[string]string `yaml:"kubernetes-ingresses-label-selector"` + ServicesLabels map[string]string `yaml:"kubernetes-services-label-selector"` + EndpointsLabels map[string]string `yaml:"kubernetes-endpoints-label-selector"` + EndpointsliceLabels map[string]string `yaml:"kubernetes-endpointslice-label-selector"` + ForceKubernetesService bool `yaml:"force-kubernetes-service"` + BackendTrafficAlgorithm string `yaml:"backend-traffic-algorithm"` + DefaultLoadBalancerAlgorithm string `yaml:"default-lb-algorithm"` + KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"kubernetesAnnotationPredicates"` + KubernetesAnnotationFiltersAppend []kubernetes.AnnotationFilters `yaml:"kubernetesAnnotationFiltersAppend"` + KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates `yaml:"kubernetesEastWestRangeAnnotationPredicates"` + KubernetesEastWestRangeAnnotationFiltersAppend []kubernetes.AnnotationFilters `yaml:"kubernetesEastWestRangeAnnotationFiltersAppend"` } func baseNoExt(n string) string { @@ -232,7 +234,9 @@ func testFixture(t *testing.T, f fixtureSet) { o.KubernetesEastWestRangeDomains = kop.EastWestRangeDomains o.KubernetesEastWestRangePredicates = kop.EastWestRangePredicates o.KubernetesAnnotationPredicates = kop.KubernetesAnnotationPredicates + o.KubernetesAnnotationFiltersAppend = kop.KubernetesAnnotationFiltersAppend o.KubernetesEastWestRangeAnnotationPredicates = kop.KubernetesEastWestRangeAnnotationPredicates + o.KubernetesEastWestRangeAnnotationFiltersAppend = kop.KubernetesEastWestRangeAnnotationFiltersAppend o.ProvideHTTPSRedirect = kop.HTTPSRedirect o.HTTPSRedirectCode = kop.HTTPSRedirectCode o.DisableCatchAllRoutes = kop.DisableCatchAllRoutes @@ -302,7 +306,7 @@ func testFixture(t *testing.T, f fixtureSet) { t.Logf("routes: %d, expected: %d", len(routes), len(expectedRoutes)) t.Logf("got:\n%s", eskip.String(eskip.CanonicalList(routes)...)) t.Logf("expected:\n%s", eskip.String(eskip.CanonicalList(expectedRoutes)...)) - t.Logf("diff\n%s:", cmp.Diff( + t.Logf("diff:\n%s", cmp.Diff( eskip.Print(eskip.PrettyPrintInfo{Pretty: true}, eskip.CanonicalList(expectedRoutes)...), eskip.Print(eskip.PrettyPrintInfo{Pretty: true}, eskip.CanonicalList(routes)...), )) diff --git a/dataclients/kubernetes/routegroup.go b/dataclients/kubernetes/routegroup.go index 6f4a3b3fa3..dfcbf18de9 100644 --- a/dataclients/kubernetes/routegroup.go +++ b/dataclients/kubernetes/routegroup.go @@ -592,7 +592,8 @@ func (r *routeGroups) convert(s *clusterState, df defaultFilters, loggingEnabled } for _, route := range ri { - addAnnotationPredicates(r.options.KubernetesAnnotationPredicates, rg.Metadata.Annotations, route) + appendAnnotationPredicates(r.options.KubernetesAnnotationPredicates, rg.Metadata.Annotations, route) + appendAnnotationFilters(r.options.KubernetesAnnotationFiltersAppend, rg.Metadata.Annotations, route) } rs = append(rs, ri...) @@ -634,7 +635,8 @@ func (r *routeGroups) convert(s *clusterState, df defaultFilters, loggingEnabled applyEastWestRangePredicates(internalRi, r.options.KubernetesEastWestRangePredicates) for _, route := range internalRi { - addAnnotationPredicates(r.options.KubernetesEastWestRangeAnnotationPredicates, rg.Metadata.Annotations, route) + appendAnnotationPredicates(r.options.KubernetesEastWestRangeAnnotationPredicates, rg.Metadata.Annotations, route) + appendAnnotationFilters(r.options.KubernetesEastWestRangeAnnotationFiltersAppend, rg.Metadata.Annotations, route) } if internalCtx.certificateRegistry != nil { diff --git a/dataclients/kubernetes/routegroups_test.go b/dataclients/kubernetes/routegroups_test.go index ac9f2c89bd..365ed82f4b 100644 --- a/dataclients/kubernetes/routegroups_test.go +++ b/dataclients/kubernetes/routegroups_test.go @@ -62,6 +62,8 @@ func TestRouteGroupTLS(t *testing.T) { kubernetestest.FixturesToTest(t, "testdata/routegroups/tls") } -func TestAnnotationPredicates(t *testing.T) { - kubernetestest.FixturesToTest(t, "testdata/routegroups/annotation-predicates") +func TestRouteGroupAnnotationConfig(t *testing.T) { + kubernetestest.FixturesToTest(t, + "testdata/routegroups/annotation-predicates", + "testdata/routegroups/annotation-filters") } diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.eskip b/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.eskip new file mode 100644 index 0000000000..29b208fed1 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.eskip @@ -0,0 +1,43 @@ +kube_default__myapp1__zone1_test____myapp: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && True() + -> comment("zalando.org/skipper-filter [1]") + -> comment("zalando.org/skipper-filter [2]") + -> comment("zalando.org/zone=zone1 [1]") + -> comment("foo [1]") + -> ; + +kube_default__myapp1_r1_0__zone1_test____: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && Path("/r1") + && True() + -> comment("zalando.org/skipper-routes [1]") + -> comment("zalando.org/zone=zone1 [1]") + -> comment("foo [1]") + -> "https://zone1.test"; + + +kube_default__myapp1__zone1_ingress_cluster_local____myapp: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Weight(10) + -> comment("zalando.org/skipper-filter [1]") + -> comment("zalando.org/skipper-filter [2]") + -> comment("zalando.org/zone=zone1 [1] east-west") + -> comment("foo=bar [1] east-west") + -> ; + +kube_default__myapp1_r1_0__zone1_ingress_cluster_local____: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && Path("/r1") + && ClientIP("10.2.0.0/16") + && Weight(10) + -> comment("zalando.org/skipper-routes [1]") + -> comment("zalando.org/zone=zone1 [1] east-west") + -> comment("foo=bar [1] east-west") + -> "https://zone1.test"; + + +kube_default__myapp2__zone2_test____myapp: + Host("^(zone2[.]test[.]?(:[0-9]+)?)$") + -> ; diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.kube b/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.kube new file mode 100644 index 0000000000..f7cc9eae04 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.kube @@ -0,0 +1,48 @@ +# +# Test that multiple annotations: +# * append filters in the configured order +# * append different filters for east-west domain +# * append both filters and predicates +# * work with zalando.org/skipper-filter annotation +# * work with zalando.org/skipper-routes annotation +# +kubernetesAnnotationFiltersAppend: + - key: zalando.org/zone + value: zone1 + filters: + - name: comment + args: ["zalando.org/zone=zone1 [1]"] + - key: foo + value: bar + filters: + - name: comment + args: ["foo [1]"] +kubernetesEastWestRangeAnnotationFiltersAppend: + - key: zalando.org/zone + value: zone1 + filters: + - name: comment + args: ["zalando.org/zone=zone1 [1] east-west"] + - key: foo + value: bar + filters: + - name: comment + args: ["foo=bar [1] east-west"] + +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: True +kubernetesEastWestRangeAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: Weight + args: [10.0] + +eastWestRangeDomains: + - ingress.cluster.local +eastWestRangePredicatesAppend: + - name: ClientIP + args: ["10.2.0.0/16"] diff --git a/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.yaml b/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.yaml new file mode 100644 index 0000000000..3923845517 --- /dev/null +++ b/dataclients/kubernetes/testdata/ingressV1/annotation-filters/annotation-filters-append.yaml @@ -0,0 +1,90 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp1 + namespace: default + annotations: + foo: bar + zalando.org/zone: zone1 + zalando.org/skipper-filter: | + comment("zalando.org/skipper-filter [1]") -> + comment("zalando.org/skipper-filter [2]") + zalando.org/skipper-routes: | + r1: Path("/r1") + -> comment("zalando.org/skipper-routes [1]") + -> "https://zone1.test"; +spec: + rules: + - host: zone1.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific + - host: zone1.ingress.cluster.local + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp2 + namespace: default + annotations: + zalando.org/zone: unknown +spec: + rules: + - host: zone2.test + http: + paths: + - backend: + service: + name: myapp + port: + number: 8080 + pathType: ImplementationSpecific +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: foo + port: 8080 + protocol: TCP + targetPort: web + - name: web + port: 80 + protocol: TCP + targetPort: foo + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: foo + port: 8080 + protocol: TCP diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.eskip b/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.eskip new file mode 100644 index 0000000000..1c9d7aca49 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.eskip @@ -0,0 +1,57 @@ +kube_rg__default__myapp1__all__0_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && True() + -> comment("zalando.org/zone=zone1 [1]") + -> comment("foo [1]") + -> ; + +kube_rg__default__myapp1__all__1_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && True() + -> comment("zalando.org/zone=zone1 [1]") + -> comment("foo [1]") + -> ; + +kube_rg____zone1_test__catchall__0_0: + Host("^(zone1[.]test[.]?(:[0-9]+)?)$") + && True() + -> comment("zalando.org/zone=zone1 [1]") + -> comment("foo [1]") + -> ; + +kube_rg__internal_default__myapp1__all__0_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/") + && ClientIP("10.2.0.0/16") + && Weight(10) + -> comment("zalando.org/zone=zone1 [1] east-west") + -> comment("foo=bar [1] east-west") + -> ; + +kube_rg__internal_default__myapp1__all__1_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && PathSubtree("/shunt") + && ClientIP("10.2.0.0/16") + && Weight(10) + -> comment("zalando.org/zone=zone1 [1] east-west") + -> comment("foo=bar [1] east-west") + -> ; + +kube_rg__internal___zone1_ingress_cluster_local__catchall__0_0: + Host("^(zone1[.]ingress[.]cluster[.]local[.]?(:[0-9]+)?)$") + && ClientIP("10.2.0.0/16") + && Weight(10) + -> comment("zalando.org/zone=zone1 [1] east-west") + -> comment("foo=bar [1] east-west") + -> ; + +kube_rg__default__myapp2__all__0_0: + Host("^(zone2[.]test[.]?(:[0-9]+)?)$") + && PathSubtree("/") + -> ; + +kube_rg____zone2_test__catchall__0_0: + Host("^(zone2[.]test[.]?(:[0-9]+)?)$") + -> ; diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.kube b/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.kube new file mode 100644 index 0000000000..f9ceedaea3 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.kube @@ -0,0 +1,46 @@ +# +# Test that multiple annotations: +# * append filters in the configured order +# * append different filters for east-west domain +# * append both filters and predicates +# +kubernetesAnnotationFiltersAppend: + - key: zalando.org/zone + value: zone1 + filters: + - name: comment + args: ["zalando.org/zone=zone1 [1]"] + - key: foo + value: bar + filters: + - name: comment + args: ["foo [1]"] +kubernetesEastWestRangeAnnotationFiltersAppend: + - key: zalando.org/zone + value: zone1 + filters: + - name: comment + args: ["zalando.org/zone=zone1 [1] east-west"] + - key: foo + value: bar + filters: + - name: comment + args: ["foo=bar [1] east-west"] + +kubernetesAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: True +kubernetesEastWestRangeAnnotationPredicates: + - key: zalando.org/zone + value: zone1 + predicates: + - name: Weight + args: [10.0] + +eastWestRangeDomains: + - ingress.cluster.local +eastWestRangePredicatesAppend: + - name: ClientIP + args: ["10.2.0.0/16"] diff --git a/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.yaml b/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.yaml new file mode 100644 index 0000000000..a1f053c6b8 --- /dev/null +++ b/dataclients/kubernetes/testdata/routegroups/annotation-filters/annotation-filters-append.yaml @@ -0,0 +1,77 @@ +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp1 + annotations: + foo: bar + zalando.org/zone: zone1 +spec: + hosts: + - zone1.test + - zone1.ingress.cluster.local + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + - name: shunt + type: shunt + defaultBackends: + - backendName: shunt + routes: + - pathSubtree: / + backends: + - backendName: myapp + - pathSubtree: /shunt +--- +apiVersion: zalando.org/v1 +kind: RouteGroup +metadata: + name: myapp2 + annotations: + zalando.org/zone: unknown +spec: + hosts: + - zone2.test + backends: + - name: myapp + type: service + serviceName: myapp + servicePort: 80 + routes: + - pathSubtree: / + backends: + - backendName: myapp +--- +apiVersion: v1 +kind: Service +metadata: + labels: + application: myapp + name: myapp +spec: + clusterIP: 10.3.190.97 + ports: + - name: main + port: 80 + protocol: TCP + targetPort: 7272 + selector: + application: myapp + type: ClusterIP +--- +apiVersion: v1 +kind: Endpoints +metadata: + labels: + application: myapp + name: myapp + namespace: default +subsets: + - addresses: + - ip: 10.2.9.103 + - ip: 10.2.9.104 + ports: + - name: main + port: 7272 + protocol: TCP diff --git a/skipper.go b/skipper.go index 0bdfd9e541..0cfd71bd90 100644 --- a/skipper.go +++ b/skipper.go @@ -272,9 +272,16 @@ type Options struct { // routes that has KubernetesEastWestRangeDomains suffix. KubernetesEastWestRangeAnnotationPredicates []kubernetes.AnnotationPredicates - // KubernetesAnnotationPredicates set a list predicates for each annotation key and value + // KubernetesEastWestRangeAnnotationFiltersAppend same as KubernetesAnnotationFiltersAppend but will append to + // routes that has KubernetesEastWestRangeDomains suffix. + KubernetesEastWestRangeAnnotationFiltersAppend []kubernetes.AnnotationFilters + + // KubernetesAnnotationPredicates sets predicates to append for each annotation key and value KubernetesAnnotationPredicates []kubernetes.AnnotationPredicates + // KubernetesAnnotationFiltersAppend sets filters to append for each annotation key and value + KubernetesAnnotationFiltersAppend []kubernetes.AnnotationFilters + // KubernetesOnlyAllowedExternalNames will enable validation of ingress external names and route groups network // backend addresses, explicit LB endpoints validation against the list of patterns in // AllowedExternalNames. @@ -958,39 +965,41 @@ type Options struct { func (o *Options) KubernetesDataClientOptions() kubernetes.Options { return kubernetes.Options{ - AllowedExternalNames: o.KubernetesAllowedExternalNames, - BackendNameTracingTag: o.OpenTracingBackendNameTag, - DefaultFiltersDir: o.DefaultFiltersDir, - KubernetesInCluster: o.KubernetesInCluster, - KubernetesURL: o.KubernetesURL, - TokenFile: o.KubernetesTokenFile, - KubernetesNamespace: o.KubernetesNamespace, - KubernetesEnableEastWest: o.KubernetesEnableEastWest, - KubernetesEnableEndpointslices: o.KubernetesEnableEndpointslices, - KubernetesEastWestDomain: o.KubernetesEastWestDomain, - KubernetesEastWestRangeDomains: o.KubernetesEastWestRangeDomains, - KubernetesEastWestRangePredicates: o.KubernetesEastWestRangePredicates, - KubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates, - KubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates, - HTTPSRedirectCode: o.KubernetesHTTPSRedirectCode, - DisableCatchAllRoutes: o.KubernetesDisableCatchAllRoutes, - IngressClass: o.KubernetesIngressClass, - IngressLabelSelectors: o.KubernetesIngressLabelSelectors, - ServicesLabelSelectors: o.KubernetesServicesLabelSelectors, - EndpointsLabelSelectors: o.KubernetesEndpointsLabelSelectors, - SecretsLabelSelectors: o.KubernetesSecretsLabelSelectors, - RouteGroupsLabelSelectors: o.KubernetesRouteGroupsLabelSelectors, - OnlyAllowedExternalNames: o.KubernetesOnlyAllowedExternalNames, - OriginMarker: o.EnableRouteCreationMetrics, - PathMode: o.KubernetesPathMode, - ProvideHealthcheck: o.KubernetesHealthcheck, - ProvideHTTPSRedirect: o.KubernetesHTTPSRedirect, - ReverseSourcePredicate: o.ReverseSourcePredicate, - RouteGroupClass: o.KubernetesRouteGroupClass, - WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR, - ForceKubernetesService: o.KubernetesForceService, - BackendTrafficAlgorithm: o.KubernetesBackendTrafficAlgorithm, - DefaultLoadBalancerAlgorithm: o.KubernetesDefaultLoadBalancerAlgorithm, + AllowedExternalNames: o.KubernetesAllowedExternalNames, + BackendNameTracingTag: o.OpenTracingBackendNameTag, + DefaultFiltersDir: o.DefaultFiltersDir, + KubernetesInCluster: o.KubernetesInCluster, + KubernetesURL: o.KubernetesURL, + TokenFile: o.KubernetesTokenFile, + KubernetesNamespace: o.KubernetesNamespace, + KubernetesEnableEastWest: o.KubernetesEnableEastWest, + KubernetesEnableEndpointslices: o.KubernetesEnableEndpointslices, + KubernetesEastWestDomain: o.KubernetesEastWestDomain, + KubernetesEastWestRangeDomains: o.KubernetesEastWestRangeDomains, + KubernetesEastWestRangePredicates: o.KubernetesEastWestRangePredicates, + KubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates, + KubernetesEastWestRangeAnnotationFiltersAppend: o.KubernetesEastWestRangeAnnotationFiltersAppend, + KubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates, + KubernetesAnnotationFiltersAppend: o.KubernetesAnnotationFiltersAppend, + HTTPSRedirectCode: o.KubernetesHTTPSRedirectCode, + DisableCatchAllRoutes: o.KubernetesDisableCatchAllRoutes, + IngressClass: o.KubernetesIngressClass, + IngressLabelSelectors: o.KubernetesIngressLabelSelectors, + ServicesLabelSelectors: o.KubernetesServicesLabelSelectors, + EndpointsLabelSelectors: o.KubernetesEndpointsLabelSelectors, + SecretsLabelSelectors: o.KubernetesSecretsLabelSelectors, + RouteGroupsLabelSelectors: o.KubernetesRouteGroupsLabelSelectors, + OnlyAllowedExternalNames: o.KubernetesOnlyAllowedExternalNames, + OriginMarker: o.EnableRouteCreationMetrics, + PathMode: o.KubernetesPathMode, + ProvideHealthcheck: o.KubernetesHealthcheck, + ProvideHTTPSRedirect: o.KubernetesHTTPSRedirect, + ReverseSourcePredicate: o.ReverseSourcePredicate, + RouteGroupClass: o.KubernetesRouteGroupClass, + WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR, + ForceKubernetesService: o.KubernetesForceService, + BackendTrafficAlgorithm: o.KubernetesBackendTrafficAlgorithm, + DefaultLoadBalancerAlgorithm: o.KubernetesDefaultLoadBalancerAlgorithm, } } From 9bbfe3a1e9f118b579a4fb123b8e196dfde9d953 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 20 Jan 2025 22:17:39 +0100 Subject: [PATCH 1001/1047] all: re-enable staticcheck rule ST1012 (#3381) Rename error variables and re-enable staticheck [ST1012 - Poorly chosen name for error variable](https://staticcheck.dev/docs/checks/#ST1012) rule. All renamed variables are non-exported except errInvalidArgs in predicates/source which was made unexported. Follow up on #897 and #1642 Signed-off-by: Alexander Yastrebov --- Makefile | 4 +--- cmd/eskip/args.go | 12 ++++++------ cmd/eskip/args_test.go | 4 ++-- cmd/eskip/eskip.go | 10 +++++----- cmd/eskip/eskip_test.go | 4 ++-- cmd/eskip/load.go | 6 +++--- cmd/eskip/load_test.go | 11 ++--------- cmd/eskip/media.go | 30 +++++++++++++++--------------- cmd/eskip/media_test.go | 14 +++++++------- cmd/eskip/readclient.go | 2 +- cmd/eskip/writeclient.go | 4 ++-- eskip/eskip.go | 8 ++++---- eskip/lexer.go | 30 +++++++++++++++--------------- eskip/parser.go | 3 --- eskip/parser.y | 3 --- etcd/etcd.go | 30 +++++++++++++++--------------- etcd/etcd_test.go | 6 +++--- filters/builtin/compress.go | 8 ++++---- filters/builtin/compress_test.go | 2 +- packaging/version/version.go | 6 +++--- predicates/source/source.go | 6 +++--- 21 files changed, 94 insertions(+), 109 deletions(-) diff --git a/Makefile b/Makefile index 1b0f9b64eb..f59368bdc4 100644 --- a/Makefile +++ b/Makefile @@ -130,12 +130,10 @@ vet: $(SOURCES) ## run Go vet # TODO(sszuecs) review disabling these checks, f.e.: # -ST1000 missing package doc in many packages # -ST1003 wrong naming convention Api vs API, Id vs ID -# -ST1012 too many error variables are not having prefix "err" # -ST1020 too many wrong comments on exported functions to fix right away # -ST1021 too many wrong comments on exported functions to fix right away -# -ST1022 too many wrong comments on exported functions to fix right away staticcheck: $(SOURCES) ## run staticcheck - staticcheck -checks "all,-ST1000,-ST1003,-ST1012,-ST1020,-ST1021" ./... + staticcheck -checks "all,-ST1000,-ST1003,-ST1020,-ST1021" ./... .PHONY: gosec # TODO(sszuecs) review disabling these checks, f.e.: diff --git a/cmd/eskip/args.go b/cmd/eskip/args.go index 8588b005f1..a56d08d75e 100644 --- a/cmd/eskip/args.go +++ b/cmd/eskip/args.go @@ -55,9 +55,9 @@ func (w *noopWriter) Write(b []byte) (int, error) { } var ( - invalidNumberOfArgs = errors.New("invalid number of args") - missingOAuthToken = errors.New("missing OAuth token") - invalidIndentStr = errors.New("invalid indent. Must match regexp \\s") + errInvalidNumberOfArgs = errors.New("invalid number of args") + errMissingOAuthToken = errors.New("missing OAuth token") + errInvalidIndentStr = errors.New("invalid indent. Must match regexp \\s") ) // parsing vars for flags: @@ -175,7 +175,7 @@ func processInnkeeperArgs(innkeeperUrl, oauthToken string) (*medium, error) { } if oauthToken == "" { - return nil, missingOAuthToken + return nil, errMissingOAuthToken } if innkeeperUrl == "" { @@ -197,7 +197,7 @@ func processInnkeeperArgs(innkeeperUrl, oauthToken string) (*medium, error) { func processFileArg() (*medium, error) { nonFlagArgs := flags.Args() if len(nonFlagArgs) > 1 { - return nil, invalidNumberOfArgs + return nil, errInvalidNumberOfArgs } if len(nonFlagArgs) == 0 { @@ -212,7 +212,7 @@ func processFileArg() (*medium, error) { // if pretty print then check that indent matches pattern func processIndentStr() error { if pretty && !(regexp.MustCompile(`^[\s]*$`).MatchString(indentStr)) { - return invalidIndentStr + return errInvalidIndentStr } return nil diff --git a/cmd/eskip/args_test.go b/cmd/eskip/args_test.go index 57f1397a17..78fbe57bae 100644 --- a/cmd/eskip/args_test.go +++ b/cmd/eskip/args_test.go @@ -118,7 +118,7 @@ func TestProcessArgs(t *testing.T) { // innkeeper-url missing token []string{"-innkeeper-url", "https://innkeeper.example.org"}, true, - missingOAuthToken, + errMissingOAuthToken, nil, }, { @@ -166,7 +166,7 @@ func TestProcessArgs(t *testing.T) { // too many files []string{"file1", "file2"}, true, - invalidNumberOfArgs, + errInvalidNumberOfArgs, nil, }, { diff --git a/cmd/eskip/eskip.go b/cmd/eskip/eskip.go index 0626b71202..f948c78463 100644 --- a/cmd/eskip/eskip.go +++ b/cmd/eskip/eskip.go @@ -52,8 +52,8 @@ var commands = map[command]commandFunc{ ver: versionCmd} var ( - missingCommand = errors.New("missing command") - invalidCommand = errors.New("invalid command") + errMissingCommand = errors.New("missing command") + errInvalidCommand = errors.New("invalid command") ) var stdout io.Writer = os.Stdout @@ -90,18 +90,18 @@ func exit(err error) { exitErrHint(err, false) } // second argument must be the ('sub') command. func getCommand(args []string) (command, error) { if len(args) < 2 { - return "", missingCommand + return "", errMissingCommand } cmd := command(args[1]) if cmd[0] == '-' { - return "", missingCommand + return "", errMissingCommand } if _, ok := commands[cmd]; ok { return cmd, nil } else { - return "", invalidCommand + return "", errInvalidCommand } } diff --git a/cmd/eskip/eskip_test.go b/cmd/eskip/eskip_test.go index 7457b79c21..428f98266c 100644 --- a/cmd/eskip/eskip_test.go +++ b/cmd/eskip/eskip_test.go @@ -13,14 +13,14 @@ func TestGetCommandSuccess(t *testing.T) { func TestGetCommandFail(t *testing.T) { _, err := getCommand([]string{"some", "hello"}) - if err != invalidCommand { + if err != errInvalidCommand { t.Error("hello is an invalid command") } } func TestGetCommandEmpty(t *testing.T) { _, err := getCommand([]string{"some"}) - if err != missingCommand { + if err != errMissingCommand { t.Error("empty should fail ") } } diff --git a/cmd/eskip/load.go b/cmd/eskip/load.go index 050ae97c30..fa58fef345 100644 --- a/cmd/eskip/load.go +++ b/cmd/eskip/load.go @@ -28,7 +28,7 @@ type loadResult struct { parseErrors map[string]error } -var invalidRouteExpression = errors.New("one or more invalid route expressions") +var errInvalidRouteExpression = errors.New("one or more invalid route expressions") // store all loaded routes, even if invalid, and store the // parse errors if any. @@ -67,7 +67,7 @@ func checkParseErrors(lr loadResult) error { printStderr(id, perr) } - return invalidRouteExpression + return errInvalidRouteExpression } // load, parse routes and print parse errors if any. @@ -146,7 +146,7 @@ func printCmd(a cmdArgs) error { } if len(lr.parseErrors) > 0 { - return invalidRouteExpression + return errInvalidRouteExpression } return nil diff --git a/cmd/eskip/load_test.go b/cmd/eskip/load_test.go index e7aa5e45f4..ada1afe55a 100644 --- a/cmd/eskip/load_test.go +++ b/cmd/eskip/load_test.go @@ -16,7 +16,6 @@ package main import ( "bytes" - "errors" "os" "strings" "testing" @@ -26,8 +25,6 @@ import ( const testStdinName = "testStdin" -var ioError = errors.New("io error") - func preserveStdin(f *os.File, action func()) { f, os.Stdin = os.Stdin, f defer func() { os.Stdin = f }() @@ -61,11 +58,7 @@ func withFile(name string, content string, action func(f *os.File)) error { withError(func() { err = os.Remove(name) }) - if err == nil { - return nil - } - - return ioError + return err } func withStdin(content string, action func()) error { @@ -173,7 +166,7 @@ func TestCheckEtcdInvalid(t *testing.T) { } err = checkCmd(cmdArgs{in: &medium{typ: etcd, urls: urls, path: "/skippertest"}}) - if err != invalidRouteExpression { + if err != errInvalidRouteExpression { t.Error("failed to fail properly") } } diff --git a/cmd/eskip/media.go b/cmd/eskip/media.go index 5c67f42f15..e1f352ac92 100644 --- a/cmd/eskip/media.go +++ b/cmd/eskip/media.go @@ -57,9 +57,9 @@ type medium struct { } var ( - tooManyInputs = errors.New("too many inputs") - invalidInputType = errors.New("invalid input type") - missingInput = errors.New("missing input") + errTooManyInputs = errors.New("too many inputs") + errInvalidInputType = errors.New("invalid input type") + errMissingInput = errors.New("missing input") ) // validate medium from args, and check if it can be used @@ -67,7 +67,7 @@ var ( // (check and print) func validateSelectRead(media []*medium) (a cmdArgs, err error) { if len(media) > 1 { - err = tooManyInputs + err = errTooManyInputs return } @@ -78,7 +78,7 @@ func validateSelectRead(media []*medium) (a cmdArgs, err error) { switch media[0].typ { case inlineIds, patchPrepend, patchPrependFile, patchAppend, patchAppendFile: - err = invalidInputType + err = errInvalidInputType return } @@ -89,19 +89,19 @@ func validateSelectRead(media []*medium) (a cmdArgs, err error) { // validate media from args, and check if input was specified. func validateSelectWrite(media []*medium) (a cmdArgs, err error) { if len(media) == 0 { - err = missingInput + err = errMissingInput return } if len(media) > 2 { - err = tooManyInputs + err = errTooManyInputs return } for _, m := range media { switch media[0].typ { case inlineIds, patchPrepend, patchPrependFile, patchAppend, patchAppendFile: - err = invalidInputType + err = errInvalidInputType return } @@ -113,7 +113,7 @@ func validateSelectWrite(media []*medium) (a cmdArgs, err error) { } if a.in == nil { - err = missingInput + err = errMissingInput } return @@ -121,19 +121,19 @@ func validateSelectWrite(media []*medium) (a cmdArgs, err error) { func validateSelectDelete(media []*medium) (a cmdArgs, err error) { if len(media) == 0 { - err = missingInput + err = errMissingInput return } if len(media) > 2 { - err = tooManyInputs + err = errTooManyInputs return } for _, m := range media { switch media[0].typ { case patchPrepend, patchPrependFile, patchAppend, patchAppendFile: - err = invalidInputType + err = errInvalidInputType return } @@ -145,7 +145,7 @@ func validateSelectDelete(media []*medium) (a cmdArgs, err error) { } if a.in == nil { - err = missingInput + err = errMissingInput } return @@ -156,11 +156,11 @@ func validateSelectPatch(media []*medium) (a cmdArgs, err error) { switch m.typ { case patchPrepend, patchPrependFile, patchAppend, patchAppendFile: case inlineIds: - err = invalidInputType + err = errInvalidInputType return default: if a.in != nil { - err = tooManyInputs + err = errTooManyInputs return } diff --git a/cmd/eskip/media_test.go b/cmd/eskip/media_test.go index 5e15ccc44c..3a8a395ecd 100644 --- a/cmd/eskip/media_test.go +++ b/cmd/eskip/media_test.go @@ -69,7 +69,7 @@ func TestValidateSelectMedia(t *testing.T) { "check", []*medium{{}, {}}, true, - tooManyInputs, + errTooManyInputs, nil, nil, }, { @@ -78,7 +78,7 @@ func TestValidateSelectMedia(t *testing.T) { "check", []*medium{{typ: inlineIds}}, true, - invalidInputType, + errInvalidInputType, nil, nil, }, { @@ -119,7 +119,7 @@ func TestValidateSelectMedia(t *testing.T) { "upsert", nil, true, - missingInput, + errMissingInput, nil, nil, }, { @@ -128,7 +128,7 @@ func TestValidateSelectMedia(t *testing.T) { "upsert", []*medium{{typ: stdin}, {typ: file}, {typ: etcd}}, true, - tooManyInputs, + errTooManyInputs, nil, nil, }, { @@ -137,7 +137,7 @@ func TestValidateSelectMedia(t *testing.T) { "upsert", []*medium{{typ: inlineIds}}, true, - invalidInputType, + errInvalidInputType, nil, nil, }, { @@ -155,7 +155,7 @@ func TestValidateSelectMedia(t *testing.T) { "delete", []*medium{{typ: innkeeper}}, true, - missingInput, + errMissingInput, nil, nil, }, { @@ -173,7 +173,7 @@ func TestValidateSelectMedia(t *testing.T) { "upsert", []*medium{{typ: inlineIds}}, true, - invalidInputType, + errInvalidInputType, nil, nil, }, { diff --git a/cmd/eskip/readclient.go b/cmd/eskip/readclient.go index 6c0c20b73e..479e6da74e 100644 --- a/cmd/eskip/readclient.go +++ b/cmd/eskip/readclient.go @@ -52,7 +52,7 @@ func createReadClient(m *medium) (readClient, error) { return &idsReader{ids: m.ids}, nil default: - return nil, invalidInputType + return nil, errInvalidInputType } } diff --git a/cmd/eskip/writeclient.go b/cmd/eskip/writeclient.go index 6a48cfe68d..5787207919 100644 --- a/cmd/eskip/writeclient.go +++ b/cmd/eskip/writeclient.go @@ -13,7 +13,7 @@ type writeClient interface { DeleteAllIf(routes []*eskip.Route, cond eskip.RoutePredicate) error } -var invalidOutput = errors.New("invalid output") +var errInvalidOutput = errors.New("invalid output") func createWriteClient(out *medium) (writeClient, error) { // no output, no client @@ -29,5 +29,5 @@ func createWriteClient(out *medium) (writeClient, error) { Insecure: insecure, OAuthToken: out.oauthToken}) } - return nil, invalidOutput + return nil, errInvalidOutput } diff --git a/eskip/eskip.go b/eskip/eskip.go index 5bb5d39855..1aecde9118 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -17,8 +17,8 @@ import ( const duplicateHeaderPredicateErrorFmt = "duplicate header predicate: %s" var ( - duplicatePathTreePredicateError = errors.New("duplicate path tree predicate") - duplicateMethodPredicateError = errors.New("duplicate method predicate") + errDuplicatePathTreePredicate = errors.New("duplicate path tree predicate") + errDuplicateMethodPredicate = errors.New("duplicate method predicate") ) // NewEditor creates an Editor PreProcessor, that matches routes and @@ -484,7 +484,7 @@ func applyPredicates(route *Route, proute *parsedRoute) error { switch p.Name { case "Path": if pathSet { - return duplicatePathTreePredicateError + return errDuplicatePathTreePredicate } if args, err = getStringArgs(p, 1); err == nil { @@ -501,7 +501,7 @@ func applyPredicates(route *Route, proute *parsedRoute) error { } case "Method": if methodSet { - return duplicateMethodPredicateError + return errDuplicateMethodPredicate } if args, err = getStringArgs(p, 1); err == nil { diff --git a/eskip/lexer.go b/eskip/lexer.go index b960ebd291..a6ca91c30f 100644 --- a/eskip/lexer.go +++ b/eskip/lexer.go @@ -35,11 +35,11 @@ const ( ) var ( - invalidCharacter = errors.New("invalid character") - incompleteToken = errors.New("incomplete token") - unexpectedToken = errors.New("unexpected token") - void = errors.New("void") - eof = errors.New("eof") + errInvalidCharacter = errors.New("invalid character") + errIncompleteToken = errors.New("incomplete token") + errUnexpectedToken = errors.New("unexpected token") + errVoid = errors.New("void") + errEOF = errors.New("eof") ) var ( @@ -192,7 +192,7 @@ func scanRegexpLiteral(code string) (t token, rest string, err error) { t.id = regexpliteral t.val, rest = scanRegexp(code[1:]) if len(rest) == 0 { - err = incompleteToken + err = errIncompleteToken return } @@ -204,13 +204,13 @@ func scanRegexpLiteral(code string) (t token, rest string, err error) { func scanRegexpOrComment(code string) (t token, rest string, err error) { if len(code) < 2 { rest = code - err = invalidCharacter + err = errInvalidCharacter return } if code[1] == '/' { rest = scanComment(code) - err = void + err = errVoid return } @@ -221,7 +221,7 @@ func scanStringLiteral(delimiter byte, code string) (t token, rest string, err e t.id = stringliteral t.val, rest = scanEscaped(delimiter, code[1:]) if len(rest) == 0 { - err = incompleteToken + err = errIncompleteToken return } @@ -264,7 +264,7 @@ func scanNumber(code string) (t token, rest string, err error) { }) if isDecimalChar(t.val[len(t.val)-1]) { - err = incompleteToken + err = errIncompleteToken return } @@ -331,22 +331,22 @@ func scan(code string) (token, string, error) { return scanSymbol(code) } - return token{}, "", unexpectedToken + return token{}, "", errUnexpectedToken } func (l *eskipLex) next() (token, error) { l.code = scanWhitespace(l.code) if len(l.code) == 0 { - return token{}, eof + return token{}, errEOF } t, rest, err := scan(l.code) - if err == unexpectedToken { + if err == errUnexpectedToken { return token{}, err } l.code = rest - if err == void { + if err == errVoid { return l.next() } @@ -366,7 +366,7 @@ func (l *eskipLex) Lex(lval *eskipSymType) int { } t, err := l.next() - if err == eof { + if err == errEOF { return -1 } diff --git a/eskip/parser.go b/eskip/parser.go index 52de78b4a5..88b0370d6a 100644 --- a/eskip/parser.go +++ b/eskip/parser.go @@ -1,7 +1,4 @@ // Code generated by goyacc -l -v -o parser.go -p eskip parser.y. DO NOT EDIT. -//lint:file-ignore ST1016 This is a generated file. -//lint:file-ignore SA4006 This is a generated file. - package eskip import __yyfmt__ "fmt" diff --git a/eskip/parser.y b/eskip/parser.y index 5378360834..d8c612e694 100644 --- a/eskip/parser.y +++ b/eskip/parser.y @@ -13,9 +13,6 @@ // limitations under the License. %{ -//lint:file-ignore ST1016 This is a generated file. -//lint:file-ignore SA4006 This is a generated file. - package eskip import "strconv" diff --git a/etcd/etcd.go b/etcd/etcd.go index 50843f103a..8db0a1507a 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -117,18 +117,18 @@ type Client struct { } var ( - missingEtcdEndpoint = errors.New("missing etcd endpoint") - missingRouteId = errors.New("missing route id") - invalidNode = errors.New("invalid node") - unexpectedHttpResponse = errors.New("unexpected http response") - notFound = errors.New("not found") - invalidResponseDocument = errors.New("invalid response document") + errMissingEtcdEndpoint = errors.New("missing etcd endpoint") + errMissingRouteId = errors.New("missing route id") + errInvalidNode = errors.New("invalid node") + errUnexpectedHttpResponse = errors.New("unexpected http response") + errNotFound = errors.New("not found") + errInvalidResponseDocument = errors.New("invalid response document") ) // Creates a new Client with the provided options. func New(o Options) (*Client, error) { if len(o.Endpoints) == 0 { - return nil, missingEtcdEndpoint + return nil, errMissingEtcdEndpoint } if o.Timeout == 0 { @@ -228,7 +228,7 @@ func parseResponse(rsp *http.Response) (*response, error) { } if r.Node == nil || r.Node.Key == "" { - return nil, invalidResponseDocument + return nil, errInvalidResponseDocument } r.etcdIndex, err = strconv.ParseUint(rsp.Header.Get(etcdIndexHeader), 10, 64) @@ -239,11 +239,11 @@ func parseResponse(rsp *http.Response) (*response, error) { // As the first argument, returns true in case of error. func httpError(code int) (bool, error) { if code == http.StatusNotFound { - return true, notFound + return true, errNotFound } if code < http.StatusOK || code >= http.StatusMultipleChoices { - return true, unexpectedHttpResponse + return true, errUnexpectedHttpResponse } return false, nil @@ -387,7 +387,7 @@ func infoToRoutesLogged(info []*eskip.RouteInfo) []*eskip.Route { // or the parsing error in case of failure. func (c *Client) LoadAndParseAll() ([]*eskip.RouteInfo, error) { response, err := c.etcdGet() - if err == notFound { + if err == errNotFound { return nil, nil } @@ -396,7 +396,7 @@ func (c *Client) LoadAndParseAll() ([]*eskip.RouteInfo, error) { } if !response.Node.Dir { - return nil, invalidNode + return nil, errInvalidNode } data, etcdIndex := c.iterateNodes(response.Node, 0) @@ -471,7 +471,7 @@ func (c *Client) LoadUpdate() ([]*eskip.Route, []string, error) { // Inserts or updates a route in etcd. func (c *Client) Upsert(r *eskip.Route) error { if r.Id == "" { - return missingRouteId + return errMissingRouteId } return c.etcdSet(r) @@ -480,11 +480,11 @@ func (c *Client) Upsert(r *eskip.Route) error { // Deletes a route from etcd. func (c *Client) Delete(id string) error { if id == "" { - return missingRouteId + return errMissingRouteId } err := c.etcdDelete(id) - if err == notFound { + if err == errNotFound { err = nil } diff --git a/etcd/etcd_test.go b/etcd/etcd_test.go index 7086589349..df01478b75 100644 --- a/etcd/etcd_test.go +++ b/etcd/etcd_test.go @@ -202,7 +202,7 @@ func TestValidatesDocument(t *testing.T) { } _, err = c.LoadAll() - if err != invalidResponseDocument { + if err != errInvalidResponseDocument { t.Error("failed to fail") } } @@ -399,7 +399,7 @@ func TestUpsertNoId(t *testing.T) { } err = c.Upsert(&eskip.Route{}) - if err != missingRouteId { + if err != errMissingRouteId { t.Error("failed to fail") } } @@ -480,7 +480,7 @@ func TestDeleteNoId(t *testing.T) { } err = c.Delete("") - if err != missingRouteId { + if err != errMissingRouteId { t.Error("failed to fail") } } diff --git a/filters/builtin/compress.go b/filters/builtin/compress.go index f28f281921..1aa8104202 100644 --- a/filters/builtin/compress.go +++ b/filters/builtin/compress.go @@ -46,8 +46,8 @@ type encoder interface { } var ( - supportedEncodings = []string{"gzip", "deflate", "br"} - unsupportedEncoding = errors.New("unsupported encoding") + supportedEncodings = []string{"gzip", "deflate", "br"} + errUnsupportedEncoding = errors.New("unsupported encoding") ) var defaultCompressMIME = []string{ @@ -159,7 +159,7 @@ func NewCompressWithOptions(options CompressOptions) (filters.Spec, error) { m := map[string]int{} for i, v := range options.Encodings { if !stringsContain(supportedEncodings, v) { - return nil, unsupportedEncoding + return nil, errUnsupportedEncoding } m[v] = i } @@ -303,7 +303,7 @@ func responseHeader(r *http.Response, enc string) { // these functions are only called from inside the package, and the // encoding should be selected from a predefined set. func unsupported() { - panic(unsupportedEncoding) + panic(errUnsupportedEncoding) } func newEncoder(enc string, level int) (encoder, error) { diff --git a/filters/builtin/compress_test.go b/filters/builtin/compress_test.go index 0026dbe789..29b3ba9b44 100644 --- a/filters/builtin/compress_test.go +++ b/filters/builtin/compress_test.go @@ -85,7 +85,7 @@ func decoder(enc string, r io.Reader) io.Reader { case "deflate": return flate.NewReader(r) default: - panic(unsupportedEncoding) + panic(errUnsupportedEncoding) } } diff --git a/packaging/version/version.go b/packaging/version/version.go index f75345ea9d..51911302e8 100644 --- a/packaging/version/version.go +++ b/packaging/version/version.go @@ -15,7 +15,7 @@ const ( cmdPatch = "patch" ) -var invalidCommand = errors.New("invalid command") +var errInvalidCommand = errors.New("invalid command") func usage() string { return `version ` @@ -23,14 +23,14 @@ func usage() string { func command() (string, string, error) { if len(os.Args) != 3 { - return "", "", invalidCommand + return "", "", errInvalidCommand } switch os.Args[1] { case cmdMajor, cmdMinor, cmdPatch: return os.Args[1], os.Args[2], nil default: - return "", "", invalidCommand + return "", "", errInvalidCommand } } diff --git a/predicates/source/source.go b/predicates/source/source.go index 580dc86dcd..0a47c36d64 100644 --- a/predicates/source/source.go +++ b/predicates/source/source.go @@ -60,7 +60,7 @@ const ( NameClientIP = predicates.ClientIPName ) -var InvalidArgsError = errors.New("invalid arguments") +var errInvalidArgs = errors.New("invalid arguments") type sourcePred int @@ -96,7 +96,7 @@ func (s *spec) Name() string { func (s *spec) Create(args []interface{}) (routing.Predicate, error) { if len(args) == 0 { - return nil, InvalidArgsError + return nil, errInvalidArgs } var cidrs []string @@ -104,7 +104,7 @@ func (s *spec) Create(args []interface{}) (routing.Predicate, error) { if s, ok := args[i].(string); ok { cidrs = append(cidrs, s) } else { - return nil, InvalidArgsError + return nil, errInvalidArgs } } From e16e0d327f8849bdbe02babde0e927e382b0ef7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 10:13:44 +0100 Subject: [PATCH 1002/1047] build(deps): bump the all-go-mod-patch-and-minor group with 2 updates (#3379) Bumps the all-go-mod-patch-and-minor group with 2 updates: [github.com/instana/go-sensor](https://github.com/instana/go-sensor) and google.golang.org/protobuf. Updates `github.com/instana/go-sensor` from 1.66.0 to 1.66.1 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/CHANGELOG.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.66.0...v1.66.1) Updates `google.golang.org/protobuf` from 1.36.2 to 1.36.3 --- updated-dependencies: - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 184ae22483..e95421b907 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.2 - github.com/instana/go-sensor v1.66.0 + github.com/instana/go-sensor v1.66.1 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.62 github.com/oklog/ulid v1.3.1 @@ -55,7 +55,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/term v0.28.0 golang.org/x/time v0.9.0 - google.golang.org/protobuf v1.36.2 + google.golang.org/protobuf v1.36.3 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf diff --git a/go.sum b/go.sum index 08a0e91ddd..a35091fe18 100644 --- a/go.sum +++ b/go.sum @@ -264,8 +264,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.66.0 h1:4ugORxo+iNmmSh6a3zmpAdXOJTOWvywSN0zRgLbhAts= -github.com/instana/go-sensor v1.66.0/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= +github.com/instana/go-sensor v1.66.1 h1:awoxgi4Qs+coJWv8mtMqWHmzm+c+gRtcDuTMBjXsN1k= +github.com/instana/go-sensor v1.66.1/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -714,8 +714,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= -google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From a75b4f8397caecb82027b70c1a6bbbc278298c75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 10:14:37 +0100 Subject: [PATCH 1003/1047] build(deps): bump docker/build-push-action from 6.11.0 to 6.12.0 (#3378) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.11.0 to 6.12.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/b32b51a8eda65d6793cd0494a773d4f6bcef32dc...67a2d409c0a876cbe6b11854e3e25193efe4e62d) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index bdaa4d48f2..58e511db12 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc + uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 5726d725e3ffabbfdae7eefbb5a9730c406e3547 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 10:15:09 +0100 Subject: [PATCH 1004/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3377) Bumps oss-fuzz-base/base-builder-go from `f4b5582` to `74c2133`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 60ced38433..02a007dd5a 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:f4b55824218c62ca44a0fb3985c2ca528c3fd64d52987b72ce45b5ec520123dd +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:74c2133a1fc6ec6c90270998c1156361cde70a2ef91531d4485a4ed59e5e1618 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From c2dbda66b929187234795d6e38ee007ac566808c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Thu, 23 Jan 2025 15:31:20 +0100 Subject: [PATCH 1005/1047] all: remove unused fields (#3382) Remove (some) unused fields detected via: ``` $ go install honnef.co/go/tools/internal/cmd/unused@latest $ unused -field-writes-are-uses=false ./... 2>/dev/null ``` Signed-off-by: Alexander Yastrebov --- net/httpclient.go | 6 ++---- net/redisclient.go | 6 ++---- ratelimit/registry.go | 4 ---- routesrv/routesrv.go | 5 +---- secrets/secrettest/secrettest.go | 2 -- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/net/httpclient.go b/net/httpclient.go index 0de67d962f..89f485b491 100644 --- a/net/httpclient.go +++ b/net/httpclient.go @@ -30,7 +30,6 @@ type Client struct { once sync.Once client http.Client tr *Transport - log logging.Logger sr secrets.SecretsReader } @@ -67,9 +66,8 @@ func NewClient(o Options) *Client { Transport: tr, CheckRedirect: o.CheckRedirect, }, - tr: tr, - log: o.Log, - sr: sr, + tr: tr, + sr: sr, } return c diff --git a/net/redisclient.go b/net/redisclient.go index 5a117ad139..5523683fa2 100644 --- a/net/redisclient.go +++ b/net/redisclient.go @@ -137,15 +137,13 @@ func (j *jumpHash) Get(k string) string { // Multi-probe consistent hashing - mpchash // https://arxiv.org/pdf/1505.00062.pdf type multiprobe struct { - hash *mpchash.Multi - shards []string + hash *mpchash.Multi } func NewMultiprobe(shards []string) redis.ConsistentHash { return &multiprobe{ // 2 seeds and k=21 got from library - hash: mpchash.New(shards, siphash64seed, [2]uint64{1, 2}, 21), - shards: shards, + hash: mpchash.New(shards, siphash64seed, [2]uint64{1, 2}, 21), } } diff --git a/ratelimit/registry.go b/ratelimit/registry.go index 714e2b2a96..5c9a79e818 100644 --- a/ratelimit/registry.go +++ b/ratelimit/registry.go @@ -22,8 +22,6 @@ const ( type Registry struct { sync.Mutex once sync.Once - closed bool - defaults Settings global Settings lookup map[Settings]*Ratelimit swarm Swarmer @@ -52,7 +50,6 @@ func NewSwarmRegistry(swarm Swarmer, ro *net.RedisOptions, settings ...Settings) r := &Registry{ once: sync.Once{}, - defaults: defaults, global: defaults, lookup: make(map[Settings]*Ratelimit), swarm: swarm, @@ -72,7 +69,6 @@ func NewSwarmRegistry(swarm Swarmer, ro *net.RedisOptions, settings ...Settings) // Close teardown Registry and dependent resources func (r *Registry) Close() { r.once.Do(func() { - r.closed = true r.redisRing.Close() for _, rl := range r.lookup { rl.Close() diff --git a/routesrv/routesrv.go b/routesrv/routesrv.go index 4c4d249a5c..bad0fd6604 100644 --- a/routesrv/routesrv.go +++ b/routesrv/routesrv.go @@ -23,7 +23,6 @@ import ( // RouteServer is used to serve eskip-formatted routes, // that originate from the polled data source. type RouteServer struct { - metrics metrics.Metrics server *http.Server supportServer *http.Server poller *poller @@ -53,9 +52,7 @@ func New(opts skipper.Options) (*RouteServer, error) { m := metrics.NewMetrics(mopt) metricsHandler := metrics.NewHandler(mopt, m) - rs := &RouteServer{ - metrics: m, - } + rs := &RouteServer{} opentracingOpts := opts.OpenTracing if len(opentracingOpts) == 0 { diff --git a/secrets/secrettest/secrettest.go b/secrets/secrettest/secrettest.go index cdc35df9e7..a5d1396b87 100644 --- a/secrets/secrettest/secrettest.go +++ b/secrets/secrettest/secrettest.go @@ -42,11 +42,9 @@ func (tr *TestRegistry) GetEncrypter(refreshInterval time.Duration, s string) (s } type TestingSecretSource struct { - getCount int secretKey string } func (s *TestingSecretSource) GetSecret() ([][]byte, error) { - s.getCount++ return [][]byte{[]byte(s.secretKey)}, nil } From 214258319c542225dc3ca931bd491d14daa12c71 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 24 Jan 2025 22:27:23 +0100 Subject: [PATCH 1006/1047] docs: clarify Ingress annotations (#3384) Clarify relation between `zalando.org/skipper-routes` and `zalando.org/skipper-filter` annotation. Follow up on #3323 Signed-off-by: Alexander Yastrebov --- docs/kubernetes/ingress-usage.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/kubernetes/ingress-usage.md b/docs/kubernetes/ingress-usage.md index 99d6f0dd8a..cb8bd58c7f 100644 --- a/docs/kubernetes/ingress-usage.md +++ b/docs/kubernetes/ingress-usage.md @@ -239,8 +239,8 @@ spec: Please consider using [RouteGroups](routegroups.md), instead of custom routes! -Custom routes is a way of extending the default routes configured for -an ingress resource. +Custom routes extend default routes configured for an ingress resource and +are specified via `zalando.org/skipper-routes` annotation. Sometimes you just want to return a header, redirect or even static html content. You can return from Skipper without doing a proxy call @@ -270,7 +270,10 @@ You will get an error in Skipper logs, similar to: [APP]time="2019-01-02T13:30:16Z" level=error msg="Failed to add route having 2 path routes: Path(\"/foo/bar\") -> inlineContent(\"custom route\") -> status(200) -> " ``` -> **Note:** Predicates coming from `zalando.org/skipper-predicate` won't be appended to routes generated from `zalando.org/skipper-routes` +> **Warning:** +> Predicates from `zalando.org/skipper-predicate` and filters from `zalando.org/skipper-filter` annotations +> won't be appended to routes from `zalando.org/skipper-routes` annotation. + ### Redirects From a64c14326487cd39da590ee5168f7009e67a28e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:51:07 +0100 Subject: [PATCH 1007/1047] build(deps): bump docker/build-push-action from 6.12.0 to 6.13.0 (#3385) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.12.0 to 6.13.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/67a2d409c0a876cbe6b11854e3e25193efe4e62d...ca877d9245402d1537745e0e356eab47c3520991) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 58e511db12..ed4b7601ce 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d + uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From eff092399fe5b1462e136df4637fe55601e83292 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:51:26 +0100 Subject: [PATCH 1008/1047] build(deps): bump actions/setup-go from 5.2.0 to 5.3.0 (#3386) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/3041bf56c941b39c61721a86cd11f3bb1338122a...f111f3307d8850f501ac008e886eec1fd1932a34) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5a7462e3e8..7e81a9eb85 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: fetch-depth: 2 - name: Setup Go - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a + uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: go-version: '^1.21' check-latest: true diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ed4b7601ce..be7f7623d1 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -25,7 +25,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: '^1.21' diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 5a7607171c..a2f10853c5 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,7 +13,7 @@ jobs: contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index df7ea8351c..4a63a1e14f 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 go-version: "^1.21" From 08e48da101b695bc56f4922b345918bf0b700323 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:51:44 +0100 Subject: [PATCH 1009/1047] build(deps): bump coverallsapp/github-action from 2.3.4 to 2.3.6 (#3387) Bumps [coverallsapp/github-action](https://github.com/coverallsapp/github-action) from 2.3.4 to 2.3.6. - [Release notes](https://github.com/coverallsapp/github-action/releases) - [Commits](https://github.com/coverallsapp/github-action/compare/cfd0633edbd2411b532b808ba7a8b5e04f76d2c8...648a8eb78e6d50909eff900e4ec85cab4524a45b) --- updated-dependencies: - dependency-name: coverallsapp/github-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/master.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index a2f10853c5..c488b1f56f 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -31,7 +31,7 @@ jobs: - name: Convert coverage to lcov uses: jandelgado/gcov2lcov-action@4e1989767862652e6ca8d3e2e61aabe6d43be28b - name: Coveralls - uses: coverallsapp/github-action@cfd0633edbd2411b532b808ba7a8b5e04f76d2c8 + uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: github-token: ${{ secrets.GITHUB_TOKEN }} path-to-lcov: coverage.lcov From cdf43e49bed56c40cd7a7c4af3a053400ffc8ff8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:52:01 +0100 Subject: [PATCH 1010/1047] build(deps): bump amazonlinux from `6e7b74e` to `9b8a888` in /fuzz (#3390) Bumps amazonlinux from `6e7b74e` to `9b8a888`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 96dbbc2d41..30d18433bc 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:6e7b74e958e8601f6e51cc59603544ea9946e69831525cb987c1a7ae3a7a1447 +FROM amazonlinux:2023@sha256:9b8a88839ab2c2abd6a8ded18e49255ab6cb89fcbb81eace8b330ee2afd598fd WORKDIR /workspace From 8ecef39cea5ce487277ee6cbca797f15002981f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:37:57 +0100 Subject: [PATCH 1011/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3395) Bumps oss-fuzz-base/base-builder-go from `74c2133` to `8cacfff`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 02a007dd5a..cd7e34ee91 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:74c2133a1fc6ec6c90270998c1156361cde70a2ef91531d4485a4ed59e5e1618 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:8cacfff023ee3485afb059b2a14d1615e6059f27b7a4f465ec8a5b85d26156b7 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From d61658cd86052cac3e6cc6029da1431934a891b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:38:36 +0100 Subject: [PATCH 1012/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 4 updates (#3394) Bumps the all-go-mod-patch-and-minor group with 4 updates in the / directory: [github.com/hashicorp/memberlist](https://github.com/hashicorp/memberlist), [github.com/instana/go-sensor](https://github.com/instana/go-sensor), [github.com/miekg/dns](https://github.com/miekg/dns) and google.golang.org/protobuf. Updates `github.com/hashicorp/memberlist` from 0.5.2 to 0.5.3 - [Release notes](https://github.com/hashicorp/memberlist/releases) - [Commits](https://github.com/hashicorp/memberlist/compare/v0.5.2...v0.5.3) Updates `github.com/instana/go-sensor` from 1.66.1 to 1.66.2 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/CHANGELOG.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.66.1...v1.66.2) Updates `github.com/miekg/dns` from 1.1.62 to 1.1.63 - [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release) - [Commits](https://github.com/miekg/dns/compare/v1.1.62...v1.1.63) Updates `google.golang.org/protobuf` from 1.36.3 to 1.36.4 --- updated-dependencies: - dependency-name: github.com/hashicorp/memberlist dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/miekg/dns dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index e95421b907..c4b5be75b7 100644 --- a/go.mod +++ b/go.mod @@ -22,10 +22,10 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 - github.com/hashicorp/memberlist v0.5.2 - github.com/instana/go-sensor v1.66.1 + github.com/hashicorp/memberlist v0.5.3 + github.com/instana/go-sensor v1.66.2 github.com/lightstep/lightstep-tracer-go v0.26.0 - github.com/miekg/dns v1.1.62 + github.com/miekg/dns v1.1.63 github.com/oklog/ulid v1.3.1 github.com/open-policy-agent/opa v0.70.0 github.com/open-policy-agent/opa-envoy-plugin v0.70.0-envoy-1 @@ -55,7 +55,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/term v0.28.0 golang.org/x/time v0.9.0 - google.golang.org/protobuf v1.36.3 + google.golang.org/protobuf v1.36.4 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf diff --git a/go.sum b/go.sum index a35091fe18..90eb80fe4f 100644 --- a/go.sum +++ b/go.sum @@ -258,14 +258,14 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/memberlist v0.5.2 h1:rJoNPWZ0juJBgqn48gjy59K5H4rNgvUoM1kUD7bXiuI= -github.com/hashicorp/memberlist v0.5.2/go.mod h1:Ri9p/tRShbjYnpNf4FFPXG7wxEGY4Nrcn6E7jrVa//4= +github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu0hndk= +github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.66.1 h1:awoxgi4Qs+coJWv8mtMqWHmzm+c+gRtcDuTMBjXsN1k= -github.com/instana/go-sensor v1.66.1/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= +github.com/instana/go-sensor v1.66.2 h1:ykUZWHfgaVkHn0S+K1RmHv9WF5e0wmHC8sxqYAlgRik= +github.com/instana/go-sensor v1.66.2/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -313,8 +313,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -714,8 +714,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= -google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From e05854a5d05bf333ba295fcb97759c3440adc85f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:39:00 +0100 Subject: [PATCH 1013/1047] build(deps): bump actions/setup-python from 5.3.0 to 5.4.0 (#3393) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/0b93645e9fea7318ecaed2b359559ac225c90a2b...42375524e23c412d93fb67b49958b491fce71c38) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 9323ed8771..162c30e5e1 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 with: python-version: 3.x - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 From 9c433e6939375ac4df6b37339a289033692ce0a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:39:25 +0100 Subject: [PATCH 1014/1047] build(deps): bump amazonlinux from `9b8a888` to `8217eec` in /fuzz (#3392) Bumps amazonlinux from `9b8a888` to `8217eec`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 30d18433bc..79ac9f7458 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:9b8a88839ab2c2abd6a8ded18e49255ab6cb89fcbb81eace8b330ee2afd598fd +FROM amazonlinux:2023@sha256:8217eec04f21ec88f5fd0ce57641a9fa9a586af5c4d2a68c7fcd50f2146ddb03 WORKDIR /workspace From 788da21b0391987e906ad9012d3dbc0897ecc22f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:55:15 +0100 Subject: [PATCH 1015/1047] build(deps): bump github.com/golang/glog in the go_modules group (#3396) Bumps the go_modules group with 1 update: [github.com/golang/glog](https://github.com/golang/glog). Updates `github.com/golang/glog` from 1.2.2 to 1.2.4 - [Release notes](https://github.com/golang/glog/releases) - [Commits](https://github.com/golang/glog/compare/v1.2.2...v1.2.4) --- updated-dependencies: - dependency-name: github.com/golang/glog dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c4b5be75b7..8c1062ecb1 100644 --- a/go.mod +++ b/go.mod @@ -101,7 +101,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.2 // indirect + github.com/golang/glog v1.2.4 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index 90eb80fe4f..00c0f9eef8 100644 --- a/go.sum +++ b/go.sum @@ -187,8 +187,8 @@ github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQg github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= -github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= +github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= From fbf7a64cf8141704cf03578268fb7b30a1855124 Mon Sep 17 00:00:00 2001 From: Pushpalanka Jayawardhana Date: Fri, 7 Feb 2025 12:45:47 +0100 Subject: [PATCH 1016/1047] Add a test case on input contract validation (#3315) * Add a simple test case to cover input contract Signed-off-by: Pushpalanka Jayawardhana --- .../opaauthorizerequest_test.go | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 4498cde081..b872328552 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -2,6 +2,7 @@ package opaauthorizerequest import ( "fmt" + "github.com/stretchr/testify/require" "github.com/zalando/skipper/filters/builtin" "io" "log" @@ -617,9 +618,157 @@ func TestCreateFilterArguments(t *testing.T) { assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) } +func TestAuthorizeRequestInputContract(t *testing.T) { + for _, ti := range []struct { + msg string + filterName string + bundleName string + regoQuery string + requestPath string + requestMethod string + requestHeaders http.Header + body string + contextExtensions string + expectedBody string + expectedHeaders http.Header + expectedStatus int + backendHeaders http.Header + removeHeaders http.Header + }{ + { + msg: "Input contract validation", + filterName: "opaAuthorizeRequestWithBody", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/users/profile/amal?param=1", + requestMethod: "GET", + contextExtensions: "", + body: `{ "key" : "value" }`, + requestHeaders: http.Header{ + "accept": []string{"*/*"}, + "user-agent": []string{"curl/7.68.0-DEV"}, + "x-request-id": []string{"1455bbb0-0623-4810-a2c6-df73ffd8863a"}, + "content-type": {"application/json"}, + }, + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: map[string][]string{"user-agent": {"curl/7.68.0-DEV"}}, + }, + } { + t.Run(ti.msg, func(t *testing.T) { + t.Logf("Running test for %v", ti) + clientServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Welcome!")) + + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + assert.Equal(t, ti.body, string(body)) + })) + defer clientServer.Close() + + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle("/bundles/"+ti.bundleName, map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + + allow { + input.attributes.request.http.path == "/users/profile/amal?param=1" + input.parsed_path == ["users", "profile", "amal"] + input.parsed_query == {"param": ["1"]} + input.attributes.request.http.headers["accept"] == "*/*" + input.attributes.request.http.headers["user-agent"] == "curl/7.68.0-DEV" + input.attributes.request.http.headers["x-request-id"] == "1455bbb0-0623-4810-a2c6-df73ffd8863a" + input.attributes.request.http.headers["content-type"] == "application/json" + input.attributes.metadataContext.filterMetadata["envoy.filters.http.header_to_metadata"].policy_type == "ingress" + opa.runtime().config.labels.environment == "test" + input.parsed_body.key == "value" + } + `, + }), + ) + + fr := make(filters.Registry) + + config := []byte(fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/{{ .bundlename }}" + } + }, + "labels": { + "environment": "test" + }, + "plugins": { + "envoy_ext_authz_grpc": { + "path": %q, + "dry-run": false + } + } + }`, opaControlPlane.URL(), ti.regoQuery)) + + envoyMetaDataConfig := []byte(`{ + "filter_metadata": { + "envoy.filters.http.header_to_metadata": { + "policy_type": "ingress" + } + } + }`) + + opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0) + opts = append(opts, + openpolicyagent.WithConfigTemplate(config), + openpolicyagent.WithEnvoyMetadataBytes(envoyMetaDataConfig)) + + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithTracer(tracingtest.NewTracer())) + ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, opts...) + fr.Register(ftSpec) + ftSpec = NewOpaAuthorizeRequestWithBodySpec(opaFactory, opts...) + fr.Register(ftSpec) + fr.Register(builtin.NewSetPath()) + + r := eskip.MustParse(fmt.Sprintf(`* -> %s("%s", "%s") -> "%s"`, ti.filterName, ti.bundleName, ti.contextExtensions, clientServer.URL)) + + proxy := proxytest.New(fr, r...) + + var bodyReader io.Reader + if ti.body != "" { + bodyReader = strings.NewReader(ti.body) + } + + req, err := http.NewRequest(ti.requestMethod, proxy.URL+ti.requestPath, bodyReader) + + require.NoError(t, err) + + for name, values := range ti.requestHeaders { + for _, value := range values { + req.Header.Add(name, value) + } + } + + rsp, err := proxy.Client().Do(req) + require.NoError(t, err) + + assert.Equal(t, ti.expectedStatus, rsp.StatusCode, "HTTP status does not match") + + defer rsp.Body.Close() + body, err := io.ReadAll(rsp.Body) + require.NoError(t, err) + assert.Equal(t, ti.expectedBody, string(body), "HTTP Body does not match") + }) + } +} + func isHeadersPresent(t *testing.T, expectedHeaders http.Header, headers http.Header) bool { for headerName, expectedValues := range expectedHeaders { actualValues, headerFound := headers[headerName] + if !headerFound { return false } From d4e17a690992050e9e1802108196c2263b2a7642 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:40:31 +0100 Subject: [PATCH 1017/1047] build(deps): bump docker/setup-qemu-action from 3.3.0 to 3.4.0 (#3400) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/53851d14592bedcffcf25ea515637cff71ef929a...4574d27a4764455b42196d70a065bc6853246a25) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index be7f7623d1..ddb98e9756 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -47,7 +47,7 @@ jobs: make build.linux - name: Set up QEMU - uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a + uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 - name: Set up Docker Buildx uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 From cd1343cc5881038955505f8c65853dcd31d642de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:41:01 +0100 Subject: [PATCH 1018/1047] build(deps): bump docker/setup-buildx-action from 3.8.0 to 3.9.0 (#3399) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.8.0 to 3.9.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/6524bf65af31da8d45b59e8c27de4bd072b392f5...f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index ddb98e9756..9bf9b22822 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 + uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From c843ca66727653ef1b013152b1417cd52eba547e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:41:31 +0100 Subject: [PATCH 1019/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3401) Bumps oss-fuzz-base/base-builder-go from `8cacfff` to `7031d0f`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index cd7e34ee91..c02495de17 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:8cacfff023ee3485afb059b2a14d1615e6059f27b7a4f465ec8a5b85d26156b7 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:7031d0f13d7cbd86a1c9a5807bb83196f3614c9c1631a0d34174bcbe6b9c12c3 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 128b43767fe91d691551873aa6fc07e6fba6cb1f Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Mon, 10 Feb 2025 15:02:37 +0100 Subject: [PATCH 1020/1047] filters/auth: add jwtMetrics benchmark (#3402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```console $ go test ./filters/auth/ -run=NONE '-bench=BenchmarkJwtMetrics$' -count=10 | benchstat - goos: linux goarch: amd64 pkg: github.com/zalando/skipper/filters/auth cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz │ - │ │ sec/op │ JwtMetrics-8 2.279µ ± 4% │ - │ │ B/op │ JwtMetrics-8 747.0 ± 0% │ - │ │ allocs/op │ JwtMetrics-8 19.00 ± 0% ``` Signed-off-by: Alexander Yastrebov --- filters/auth/jwt_metrics_test.go | 40 +++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/filters/auth/jwt_metrics_test.go b/filters/auth/jwt_metrics_test.go index 3c6b324084..672a352ff7 100644 --- a/filters/auth/jwt_metrics_test.go +++ b/filters/auth/jwt_metrics_test.go @@ -14,6 +14,7 @@ import ( "github.com/zalando/skipper/eskip" "github.com/zalando/skipper/filters/auth" "github.com/zalando/skipper/filters/builtin" + "github.com/zalando/skipper/filters/filtertest" "github.com/zalando/skipper/metrics/metricstest" "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/proxy/proxytest" @@ -359,7 +360,7 @@ func TestJwtMetricsArgs(t *testing.T) { }) } -func marshalBase64JSON(t *testing.T, v any) string { +func marshalBase64JSON(t testing.TB, v any) string { d, err := json.Marshal(v) if err != nil { t.Fatalf("failed to marshal json: %v, %v", v, err) @@ -380,3 +381,40 @@ func BenchmarkJwtMetrics_CreateFilter(b *testing.B) { _, _ = spec.CreateFilter(args) } } + +func BenchmarkJwtMetrics(b *testing.B) { + spec := auth.NewJwtMetrics() + args := []any{`{issuers: [foo, bar], optOutAnnotations: [oauth.disabled], optOutHosts: [ '^.+[.]domain[.]test$' ]}`} + + f, err := spec.CreateFilter(args) + require.NoError(b, err) + + m := &metricstest.MockMetrics{} + defer m.Close() + + ctx := &filtertest.Context{ + FRequest: &http.Request{ + Method: "GET", Host: "foo.test", + Header: http.Header{ + "Authorization": []string{ + "Bearer header." + marshalBase64JSON(b, map[string]any{"iss": "baz"}) + ".signature", + }, + }, + }, + FResponse: &http.Response{StatusCode: http.StatusOK}, + FMetrics: m, + } + + f.Request(ctx) + f.Response(ctx) + m.WithCounters(func(counters map[string]int64) { + require.Equal(b, map[string]int64{"GET.foo_test.200.invalid-issuer": 1}, counters) + }) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + f.Request(ctx) + f.Response(ctx) + } +} From 30f0005eee6ad4fe6023ffae96533959ab69e36b Mon Sep 17 00:00:00 2001 From: jub0bs <52325150+jub0bs@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:02:27 +0100 Subject: [PATCH 1021/1047] jwt: reduce heap allocations in jwt.Parse (#3403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * jwt: benchmark Parse against malicious requests Signed-off-by: jub0bs * jwt: defend Parse against malicious requests Some benchmark results: ``` goos: darwin goarch: amd64 pkg: github.com/zalando/skipper/jwt cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz │ old │ new │ │ sec/op │ sec/op vs base │ Parse_malicious/all_periods-8 13681180.50n ± 2% 97.85n ± 47% -100.00% (p=0.000 n=10) Parse_malicious/two_trailing_periods-8 60.78µ ± 1% 35.41µ ± 11% -41.74% (p=0.000 n=10) geomean 911.9µ 1.861µ -99.80% │ old │ new │ │ B/op │ B/op vs base │ Parse_malicious/all_periods-8 16785409.00 ± 0% 64.00 ± 0% -100.00% (p=0.000 n=10) Parse_malicious/two_trailing_periods-8 224.0 ± 0% 240.0 ± 0% +7.14% (p=0.000 n=10) geomean 59.88Ki 123.9 -99.80% │ old │ new │ │ allocs/op │ allocs/op vs base │ Parse_malicious/all_periods-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Parse_malicious/two_trailing_periods-8 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ geomean 2.000 2.000 +0.00% ¹ all samples are equal ``` Signed-off-by: jub0bs --------- Signed-off-by: jub0bs --- jwt/token.go | 2 +- jwt/token_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/jwt/token.go b/jwt/token.go index 1c56b13c4b..ca49e91d0a 100644 --- a/jwt/token.go +++ b/jwt/token.go @@ -16,7 +16,7 @@ type Token struct { } func Parse(value string) (*Token, error) { - parts := strings.Split(value, ".") + parts := strings.SplitN(value, ".", 4) if len(parts) != 3 { return nil, errInvalidToken } diff --git a/jwt/token_test.go b/jwt/token_test.go index 2fbc1f16e8..d2b4f3fe63 100644 --- a/jwt/token_test.go +++ b/jwt/token_test.go @@ -3,6 +3,7 @@ package jwt import ( "encoding/base64" "encoding/json" + "net/http" "reflect" "strings" "testing" @@ -93,3 +94,26 @@ func BenchmarkParse(b *testing.B) { parseSink, _ = Parse(value) } } + +func BenchmarkParse_malicious(b *testing.B) { + cases := []struct { + name string + value string + }{ + { + name: "all periods", + value: strings.Repeat(".", http.DefaultMaxHeaderBytes), + }, { + name: "two trailing periods", + value: strings.Repeat("a", http.DefaultMaxHeaderBytes-2) + "..", + }, + } + for _, bc := range cases { + b.Run(bc.name, func(b *testing.B) { + b.ReportAllocs() + for range b.N { + parseSink, _ = Parse(bc.value) + } + }) + } +} From 7aef486e1c129a7e77c732e71ffe16c1e9c38e86 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 12 Feb 2025 13:55:02 +0100 Subject: [PATCH 1022/1047] net: use strings.LastIndex to parse header (#3404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * net: update BenchmarkRemoteHostFromLast * update benchmark to use X-Forwarded-For value and report allocations. * add more test cases Signed-off-by: Alexander Yastrebov * net: use strings.LastIndex to parse header Use strings.LastIndex instead of strings.Split to reduce memory allocations. See https://github.com/zalando/skipper/pull/3403 for details. ``` goos: linux goarch: amd64 pkg: github.com/zalando/skipper/net cpu: Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz │ HEAD~1 │ HEAD │ │ sec/op │ sec/op vs base │ RemoteHostFromLast/no_header-8 144.10n ± 7% 99.73n ± 6% -30.79% (p=0.000 n=10) RemoteHostFromLast/single_value-8 210.8n ± 5% 163.6n ± 5% -22.39% (p=0.000 n=10) RemoteHostFromLast/many_values-8 374.3n ± 4% 166.0n ± 3% -55.66% (p=0.000 n=10) geomean 224.9n 139.4n -38.01% │ HEAD~1 │ HEAD │ │ B/op │ B/op vs base │ RemoteHostFromLast/no_header-8 64.00 ± 0% 48.00 ± 0% -25.00% (p=0.000 n=10) RemoteHostFromLast/single_value-8 64.00 ± 0% 48.00 ± 0% -25.00% (p=0.000 n=10) RemoteHostFromLast/many_values-8 224.00 ± 0% 48.00 ± 0% -78.57% (p=0.000 n=10) geomean 97.17 48.00 -50.60% │ HEAD~1 │ HEAD │ │ allocs/op │ allocs/op vs base │ RemoteHostFromLast/no_header-8 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10) RemoteHostFromLast/single_value-8 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10) RemoteHostFromLast/many_values-8 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10) geomean 3.000 2.000 -33.33% ``` Signed-off-by: Alexander Yastrebov --------- Signed-off-by: Alexander Yastrebov --- net/net.go | 6 ++++-- net/net_test.go | 44 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/net/net.go b/net/net.go index 755541cd87..b7d77c2d42 100644 --- a/net/net.go +++ b/net/net.go @@ -90,8 +90,10 @@ func RemoteAddrFromLast(r *http.Request) netip.Addr { // RemoteHostFromLast is *deprecated* use RemoteAddrFromLast instead func RemoteHostFromLast(r *http.Request) net.IP { ffs := r.Header.Get("X-Forwarded-For") - ffa := strings.Split(ffs, ",") - ff := ffa[len(ffa)-1] + ff := ffs + if i := strings.LastIndex(ffs, ","); i != -1 { + ff = ffs[i+1:] + } if ff != "" { if ip := parse(strings.TrimSpace(ff)); ip != nil { return ip diff --git a/net/net_test.go b/net/net_test.go index 6f05d11420..d7432b7edc 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -147,8 +147,11 @@ func TestRemoteHostFromLast(t *testing.T) { {"no header2", "1.2.3.4", net.IPv4(1, 2, 3, 4), []string{}}, {"no header3", "100.200.300.400", nil, []string{}}, {"no header4", "127.0.0.1:8080", net.IPv4(127, 0, 0, 1), []string{}}, + {"empty header", "127.0.0.1", net.IPv4(127, 0, 0, 1), []string{""}}, {"single header", "127.0.0.1", net.IPv4(172, 16, 0, 1), []string{"172.16.0.1"}}, - {"invalid header", "127.0.0.1", net.IPv4(127, 0, 0, 1), []string{"invalid header"}}, + {"invalid header", "127.0.0.1", net.IPv4(127, 0, 0, 1), []string{"invalid header"}}, + {"invalid header entry", "127.0.0.1", net.IPv4(127, 0, 0, 1), []string{"172.16.0.1", "invalid header"}}, + {"empty header entry", "127.0.0.1", net.IPv4(127, 0, 0, 1), []string{"172.16.0.1", ""}}, {"multiple entries", "127.0.0.1", net.IPv4(8, 7, 6, 5), []string{"172.16.0.1", "1.2.3.4", "8.7.6.5"}}, {"2 entries", "127.0.0.1", net.IPv4(8, 7, 6, 5), []string{"1.2.3.4", "8.7.6.5"}}, {"single header2", "127.0.0.1", net.IPv4(8, 7, 6, 5), []string{"8.7.6.5"}}, @@ -167,11 +170,40 @@ func TestRemoteHostFromLast(t *testing.T) { } func BenchmarkRemoteHostFromLast(b *testing.B) { - r := &http.Request{RemoteAddr: "1.2.3.4"} - b.ResetTimer() - for i := 0; i < b.N; i++ { - RemoteHostFromLast(r) - } + b.Run("no header", func(b *testing.B) { + b.ReportAllocs() + r := &http.Request{ + RemoteAddr: "1.2.3.4", + } + + for i := 0; i < b.N; i++ { + _ = RemoteHostFromLast(r) + } + }) + + b.Run("single value", func(b *testing.B) { + b.ReportAllocs() + r := &http.Request{ + RemoteAddr: "1.2.3.4", + Header: http.Header{"X-Forwarded-For": []string{"4.3.2.1"}}, + } + + for i := 0; i < b.N; i++ { + _ = RemoteHostFromLast(r) + } + }) + + b.Run("many values", func(b *testing.B) { + b.ReportAllocs() + r := &http.Request{ + RemoteAddr: "1.2.3.4", + Header: http.Header{"X-Forwarded-For": []string{"4.3.2.1" + strings.Repeat(", 4.3.2.1", 10)}}, + } + + for i := 0; i < b.N; i++ { + _ = RemoteHostFromLast(r) + } + }) } func TestParseIPCIDRs(t *testing.T) { From 28e76bf970b38eeb1bb729c2a6ca9ea00ffac611 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 12 Feb 2025 14:03:58 +0100 Subject: [PATCH 1023/1047] build: update min go version to 1.23 (#3406) As [go1.24 is out](https://go.dev/doc/go1.24) this change updates minimal required go version. Signed-off-by: Alexander Yastrebov --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/gh-packages.yaml | 2 +- .github/workflows/master.yaml | 2 +- .github/workflows/pr.yaml | 4 ++-- go.mod | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7e81a9eb85..89f5243493 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -49,7 +49,7 @@ jobs: - name: Setup Go uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: - go-version: '^1.21' + go-version: '^1.23' check-latest: true # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 9bf9b22822..bf61a41e62 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -28,7 +28,7 @@ jobs: - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: '^1.21' + go-version: '^1.23' check-latest: true - name: Login to Github Container Registry diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index c488b1f56f..814ee0b971 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: "^1.21" + go-version: "^1.23" check-latest: true - run: go version - run: make deps diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4a63a1e14f..a9be823f3a 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: "^1.21" + go-version: "^1.23" check-latest: true - run: go version - run: make deps @@ -37,7 +37,7 @@ jobs: - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 with: # https://www.npmjs.com/package/semver#caret-ranges-123-025-004 - go-version: "^1.21" + go-version: "^1.23" check-latest: true - run: go version - run: make deps diff --git a/go.mod b/go.mod index 8c1062ecb1..6b3fe38480 100644 --- a/go.mod +++ b/go.mod @@ -184,4 +184,4 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -go 1.22.0 +go 1.23.0 From 377d5c3f9a9cc8cededf113643b128fecfcabd32 Mon Sep 17 00:00:00 2001 From: Farasath Ahamed Date: Wed, 12 Feb 2025 16:02:08 +0100 Subject: [PATCH 1024/1047] Refactor: Improve OPA authorization filter benchmarks (#3391) - Move benchmarks to a separate file - Add parallel execution to benchmarks - Includ benchmarks for minimal policy with/without decision logging - Add reference benchmark running with a policy bundle Signed-off-by: Farasath Ahamed --- .../opaauthorizerequest/benchmark_test.go | 450 ++++++++++++++++++ .../opaauthorizerequest_test.go | 248 +--------- .../testResources/cert.pem | 22 + .../opaauthorizerequest/testResources/key.pem | 28 ++ .../simple-opa-bundle/allow.rego | 11 + .../simple-opa-bundle/allow_test.rego | 21 + skptesting/simple-opa-bundle/allow.rego | 11 + skptesting/simple-opa-bundle/allow_test.rego | 21 + 8 files changed, 570 insertions(+), 242 deletions(-) create mode 100644 filters/openpolicyagent/opaauthorizerequest/benchmark_test.go create mode 100644 filters/openpolicyagent/opaauthorizerequest/testResources/cert.pem create mode 100644 filters/openpolicyagent/opaauthorizerequest/testResources/key.pem create mode 100644 filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow.rego create mode 100644 filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow_test.rego create mode 100644 skptesting/simple-opa-bundle/allow.rego create mode 100644 skptesting/simple-opa-bundle/allow_test.rego diff --git a/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go b/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go new file mode 100644 index 0000000000..c2d80b4861 --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go @@ -0,0 +1,450 @@ +package opaauthorizerequest + +import ( + _ "embed" + "fmt" + "github.com/golang-jwt/jwt/v4" + opasdktest "github.com/open-policy-agent/opa/sdk/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/filtertest" + "github.com/zalando/skipper/filters/openpolicyagent" + "github.com/zalando/skipper/metrics/metricstest" + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strings" + "testing" + "time" +) + +const ( + tokenExp = 2 * time.Hour + testDecisionPath = "envoy/authz/allow" + testBundleName = "somebundle.tar.gz" +) + +var ( + //go:embed testResources/cert.pem + publicKey []byte + //go:embed testResources/key.pem + privateKey []byte + + testBundleEndpoint = fmt.Sprintf("/bundles/%s", testBundleName) +) + +// BenchmarkMinimalPolicy measures the performance of the OPA authorization filter with a minimal policy. +// +// Run this benchmark with varying parallelism: +// go test -bench=^BenchmarkMinimalPolicy$ -cpu=1,2,4 -benchmem ./filters/openpolicyagent/opaauthorizerequest +// +// Run all benchmarks with varying parallelism: +// go test -bench=. -cpu=1,2,4 -benchmem ./filters/openpolicyagent/opaauthorizerequest +// +// `-cpu` controls parallelism by setting the maximum number of CPUs that can be used to run the benchmark. +// Values should be less than or equal to the number of logical CPUs available on your system. If the `-cpu` +// flag is omitted, the benchmark will default to using GOMAXPROCS, which is typically set to the number +// of logical CPUs. This means the benchmark will run with maximum available parallelism by default. +// +// `-benchmem` adds memory allocation information to the benchmark results, showing how much memory +// is being used during the benchmark. This is useful for identifying memory leaks or inefficiencies. +// +// `b.RunParallel` is used internally to execute the benchmark in parallel. The `-cpu` flag (or GOMAXPROCS) +// controls the number of goroutines created by `b.RunParallel`, thus determining the degree of parallelism. +func BenchmarkMinimalPolicy(b *testing.B) { + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle(testBundleEndpoint, map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + + allow { + input.parsed_path = [ "allow" ] + } + `, + }), + ) + defer opaControlPlane.Stop() + + filterOpts := NewFilterOptionsWithDefaults(opaControlPlane.URL()) + f, err := createOpaFilter(filterOpts) + require.NoError(b, err) + + reqUrl, err := url.Parse("http://opa-authorized.test/allow") + require.NoError(b, err) + + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + URL: reqUrl, + }, + FMetrics: &metricstest.MockMetrics{}, + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Request(ctx) + assert.False(b, ctx.FServed) + } + }) +} + +func BenchmarkMinimalPolicyWithDecisionLogs(b *testing.B) { + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle(testBundleEndpoint, map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + + allow { + input.parsed_path = [ "allow" ] + } + `, + }), + ) + defer opaControlPlane.Stop() + + decisionLogsConsumer := newDecisionConsumer() + defer decisionLogsConsumer.Close() + + filterOpts := FilterOptions{ + OpaControlPlaneUrl: opaControlPlane.URL(), + DecisionConsumerUrl: decisionLogsConsumer.URL, + DecisionPath: testDecisionPath, + BundleName: testBundleName, + DecisionLogging: true, + ContextExtensions: "", + } + f, err := createOpaFilter(filterOpts) + require.NoError(b, err) + + reqUrl, err := url.Parse("http://opa-authorized.test/allow") + require.NoError(b, err) + + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + URL: reqUrl, + }, + FMetrics: &metricstest.MockMetrics{}, + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Request(ctx) + } + }) +} + +func BenchmarkAllowWithReqBody(b *testing.B) { + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle(testBundleEndpoint, map[string]string{ + "main.rego": ` + package envoy.authz + + import rego.v1 + + default allow = false + + allow if { + endswith(input.parsed_body.email, "@zalando.de") + } + `, + }), + ) + defer opaControlPlane.Stop() + + filterOpts := NewFilterOptionsWithDefaults(opaControlPlane.URL()) + f, err := createBodyBasedOpaFilter(filterOpts) + require.NoError(b, err) + + reqUrl, err := url.Parse("http://opa-authorized.test/allow") + require.NoError(b, err) + + body := `{"email": "bench-test@zalando.de"}` + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + Method: "POST", + Header: map[string][]string{ + "Content-Type": {"application/json"}, + }, + URL: reqUrl, + Body: io.NopCloser(strings.NewReader(body)), + ContentLength: int64(len(body)), + }, + FMetrics: &metricstest.MockMetrics{}, + } + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Request(ctx) + } + }) + +} + +func BenchmarkJwtValidation(b *testing.B) { + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle(testBundleEndpoint, map[string]string{ + "main.rego": fmt.Sprintf(` + package envoy.authz + + import future.keywords.if + + default allow = false + + public_key_cert := %q + + bearer_token := t if { + v := input.attributes.request.http.headers.authorization + startswith(v, "Bearer ") + t := substring(v, count("Bearer "), -1) + } + + allow if { + [valid, _, payload] := io.jwt.decode_verify(bearer_token, { + "cert": public_key_cert, + "aud": "nqz3xhorr5" + }) + + valid + + payload.sub == "5974934733" + } + `, publicKey), + }), + ) + defer opaControlPlane.Stop() + + filterOpts := NewFilterOptionsWithDefaults(opaControlPlane.URL()) + f, err := createOpaFilter(filterOpts) + require.NoError(b, err) + + reqUrl, err := url.Parse("http://opa-authorized.test/somepath") + require.NoError(b, err) + + claims := jwt.MapClaims{ + "iss": "https://some.identity.acme.com", + "sub": "5974934733", + "aud": "nqz3xhorr5", + "iat": time.Now().Add(-time.Minute).UTC().Unix(), + "exp": time.Now().Add(tokenExp).UTC().Unix(), + "email": "someone@example.org", + } + + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + + key, err := jwt.ParseRSAPrivateKeyFromPEM(privateKey) + require.NoError(b, err, "Failed to parse RSA PEM") + + // Sign and get the complete encoded token as a string using the secret + signedToken, err := token.SignedString(key) + require.NoError(b, err, "Failed to sign token") + + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + Header: map[string][]string{ + "Authorization": {fmt.Sprintf("Bearer %s", signedToken)}, + }, + URL: reqUrl, + }, + FMetrics: &metricstest.MockMetrics{}, + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Request(ctx) + assert.False(b, ctx.FServed) + } + }) +} + +// BenchmarkPolicyBundle measures the performance of the OPA authorization filter +// when using a pre-compiled policy bundle loaded from a .tar.gz file. + +// This benchmark evaluates the filter's authorization decision overhead with a +// pre-compiled bundle, serving as a representative use case. + +// A sample bundle for this benchmark is located at testResources/simple-opa-bundle. +// To generate a .tar.gz bundle, use the following command: +// +// opa build -b -o +// +// For example: +// +// cd testResources +// opa build -b simple-opa-bundle -o simple-opa-bundle.tar.gz + +// You can also use your own bundle. If you do so, ensure that the bundleName, +// bundlePath, and filterOpts variables are correctly configured to match your bundle. +func BenchmarkMinimalPolicyBundle(b *testing.B) { + bundleName := "simple-opa-bundle.tar.gz" + bundlePath := fmt.Sprintf("testResources/%s", bundleName) + + opaControlPlane := newOpaControlPlaneServingBundle(bundlePath, bundleName, b) + defer opaControlPlane.Close() + + filterOpts := FilterOptions{ + OpaControlPlaneUrl: opaControlPlane.URL, + DecisionConsumerUrl: opaControlPlane.URL, + DecisionPath: "envoy/authz/allow", + BundleName: bundleName, + DecisionLogging: false, + } + f, err := createOpaFilter(filterOpts) + require.NoError(b, err) + + requestUrl, err := url.Parse("http://opa-authorized.test/allow") + assert.NoError(b, err) + + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + URL: requestUrl, + Method: "GET", + }, + FMetrics: &metricstest.MockMetrics{}, + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Request(ctx) + assert.False(b, ctx.FServed) + } + }) +} + +func newDecisionConsumer() *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) +} + +func newOpaControlPlaneServingBundle(bundlePath, bundleName string, b *testing.B) *httptest.Server { + if !strings.HasSuffix(bundlePath, ".tar.gz") { + b.Fatalf("bundle file %q does not have .tar.gz extension", bundlePath) + } + + fileData, err := os.ReadFile(bundlePath) + if err != nil { + b.Fatalf("failed to read bundle file from path %q: %v", bundlePath, err) + } + + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/bundles/"+bundleName { + w.Header().Set("Content-Type", "application/gzip") + w.Header().Set("Content-Disposition", "attachment; filename="+bundleName) + _, err := w.Write(fileData) + if err != nil { + fmt.Printf("failed to write bundle file: %v", err) + w.WriteHeader(http.StatusInternalServerError) + } + return + } + + // Decision logs consumer endpoint + if r.URL.Path == "/logs" { + w.WriteHeader(http.StatusOK) + return + } + + w.WriteHeader(http.StatusNotFound) + })) +} + +func createOpaFilter(opts FilterOptions) (filters.Filter, error) { + config := generateConfig(opts.OpaControlPlaneUrl, opts.DecisionConsumerUrl, opts.DecisionPath, opts.DecisionLogging) + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + spec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + return spec.CreateFilter([]interface{}{opts.BundleName, opts.ContextExtensions}) +} + +func createBodyBasedOpaFilter(opts FilterOptions) (filters.Filter, error) { + config := generateConfig(opts.OpaControlPlaneUrl, opts.DecisionConsumerUrl, opts.DecisionPath, opts.DecisionLogging) + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + spec := NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + return spec.CreateFilter([]interface{}{opts.BundleName, opts.ContextExtensions}) +} + +func generateConfig(opaControlPlane string, decisionLogConsumer string, decisionPath string, decisionLogging bool) []byte { + var decisionPlugin string + if decisionLogging { + decisionPlugin = ` + "decision_logs": { + "console": false, + "service": "decision_svc", + "reporting": { + "min_delay_seconds": 300, + "max_delay_seconds": 600 + } + }, + ` + } + + return []byte(fmt.Sprintf(`{ + "services": { + "bundle_svc": { + "url": %q + }, + "decision_svc": { + "url": %q + } + }, + "bundles": { + "test": { + "service": "bundle_svc", + "resource": "/bundles/{{ .bundlename }}", + "polling": { + "min_delay_seconds": 600, + "max_delay_seconds": 1200 + } + } + }, + "labels": { + "environment": "test" + }, + %s + "plugins": { + "envoy_ext_authz_grpc": { + "path": %q, + "dry-run": false + } + } + }`, opaControlPlane, decisionLogConsumer, decisionPlugin, decisionPath)) +} + +type FilterOptions struct { + OpaControlPlaneUrl string + DecisionConsumerUrl string + DecisionPath string + BundleName string + DecisionLogging bool + ContextExtensions string +} + +func NewFilterOptionsWithDefaults(opaControlPlaneURL string) FilterOptions { + return FilterOptions{ + OpaControlPlaneUrl: opaControlPlaneURL, + DecisionConsumerUrl: "", + DecisionPath: testDecisionPath, + BundleName: testBundleName, + DecisionLogging: false, + ContextExtensions: "", + } +} diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index b872328552..56b4435bf8 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -2,28 +2,20 @@ package opaauthorizerequest import ( "fmt" + opasdktest "github.com/open-policy-agent/opa/sdk/test" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" "github.com/zalando/skipper/filters/builtin" + "github.com/zalando/skipper/proxy/proxytest" + "github.com/zalando/skipper/tracing/tracingtest" "io" - "log" "net/http" "net/http/httptest" - "net/url" - "os" "strings" "testing" - "time" - - "github.com/golang-jwt/jwt/v4" - opasdktest "github.com/open-policy-agent/opa/sdk/test" - "github.com/stretchr/testify/assert" - "github.com/zalando/skipper/eskip" - "github.com/zalando/skipper/filters" - "github.com/zalando/skipper/metrics/metricstest" - "github.com/zalando/skipper/proxy/proxytest" - "github.com/zalando/skipper/tracing/tracingtest" - "github.com/zalando/skipper/filters/filtertest" "github.com/zalando/skipper/filters/openpolicyagent" ) @@ -791,231 +783,3 @@ func isHeadersAbsent(t *testing.T, unwantedHeaders http.Header, headers http.Hea } return true } - -const ( - tokenExp = 2 * time.Hour - - certPath = "../../../skptesting/cert.pem" - keyPath = "../../../skptesting/key.pem" -) - -func BenchmarkAuthorizeRequest(b *testing.B) { - b.Run("authorize-request-minimal", func(b *testing.B) { - opaControlPlane := opasdktest.MustNewServer( - opasdktest.MockBundle("/bundles/somebundle.tar.gz", map[string]string{ - "main.rego": ` - package envoy.authz - - default allow = false - - allow { - input.parsed_path = [ "allow" ] - } - `, - }), - ) - - f, err := createOpaFilter(opaControlPlane) - assert.NoError(b, err) - - url, err := url.Parse("http://opa-authorized.test/somepath") - assert.NoError(b, err) - - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - Header: map[string][]string{ - "Authorization": {"Bearer FOOBAR"}, - }, - URL: url, - }, - FMetrics: &metricstest.MockMetrics{}, - } - - b.ResetTimer() - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - f.Request(ctx) - } - }) - - b.Run("authorize-request-with-body", func(b *testing.B) { - opaControlPlane := opasdktest.MustNewServer( - opasdktest.MockBundle("/bundles/somebundle.tar.gz", map[string]string{ - "main.rego": ` - package envoy.authz - - import rego.v1 - - default allow = false - - allow if { - endswith(input.parsed_body.email, "@zalando.de") - } - `, - }), - ) - - f, err := createBodyBasedOpaFilter(opaControlPlane) - assert.NoError(b, err) - - url, err := url.Parse("http://opa-authorized.test/somepath") - assert.NoError(b, err) - - body := `{"email": "bench-test@zalando.de"}` - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - Method: "POST", - Header: map[string][]string{ - "Authorization": {"Bearer FOOBAR"}, - "Content-Type": {"application/json"}, - }, - URL: url, - Body: io.NopCloser(strings.NewReader(body)), - ContentLength: int64(len(body)), - }, - FMetrics: &metricstest.MockMetrics{}, - } - - b.ResetTimer() - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - f.Request(ctx) - } - }) - - b.Run("authorize-request-jwt-validation", func(b *testing.B) { - - publicKey, err := os.ReadFile(certPath) - if err != nil { - log.Fatalf("Failed to read public key: %v", err) - } - - opaControlPlane := opasdktest.MustNewServer( - opasdktest.MockBundle("/bundles/somebundle.tar.gz", map[string]string{ - "main.rego": fmt.Sprintf(` - package envoy.authz - - import future.keywords.if - - default allow = false - - public_key_cert := %q - - bearer_token := t if { - v := input.attributes.request.http.headers.authorization - startswith(v, "Bearer ") - t := substring(v, count("Bearer "), -1) - } - - allow if { - [valid, _, payload] := io.jwt.decode_verify(bearer_token, { - "cert": public_key_cert, - "aud": "nqz3xhorr5" - }) - - valid - - payload.sub == "5974934733" - } - `, publicKey), - }), - ) - - f, err := createOpaFilter(opaControlPlane) - assert.NoError(b, err) - - url, err := url.Parse("http://opa-authorized.test/somepath") - assert.NoError(b, err) - - claims := jwt.MapClaims{ - "iss": "https://some.identity.acme.com", - "sub": "5974934733", - "aud": "nqz3xhorr5", - "iat": time.Now().Add(-time.Minute).UTC().Unix(), - "exp": time.Now().Add(tokenExp).UTC().Unix(), - "email": "someone@example.org", - } - - token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) - - privKey, err := os.ReadFile(keyPath) - if err != nil { - log.Fatalf("Failed to read priv key: %v", err) - } - - key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privKey)) - if err != nil { - log.Fatalf("Failed to parse RSA PEM: %v", err) - } - - // Sign and get the complete encoded token as a string using the secret - signedToken, err := token.SignedString(key) - if err != nil { - log.Fatalf("Failed to sign token: %v", err) - } - - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - Header: map[string][]string{ - "Authorization": {fmt.Sprintf("Bearer %s", signedToken)}, - }, - URL: url, - }, - FMetrics: &metricstest.MockMetrics{}, - } - - b.ResetTimer() - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - f.Request(ctx) - assert.False(b, ctx.FServed) - } - }) -} - -func createOpaFilter(opaControlPlane *opasdktest.Server) (filters.Filter, error) { - config := generateConfig(opaControlPlane, "envoy/authz/allow") - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() - spec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) - return spec.CreateFilter([]interface{}{"somebundle.tar.gz"}) -} - -func createBodyBasedOpaFilter(opaControlPlane *opasdktest.Server) (filters.Filter, error) { - config := generateConfig(opaControlPlane, "envoy/authz/allow") - opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() - spec := NewOpaAuthorizeRequestWithBodySpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) - return spec.CreateFilter([]interface{}{"somebundle.tar.gz"}) -} - -func generateConfig(opaControlPlane *opasdktest.Server, path string) []byte { - return []byte(fmt.Sprintf(`{ - "services": { - "test": { - "url": %q - } - }, - "bundles": { - "test": { - "resource": "/bundles/{{ .bundlename }}" - } - }, - "labels": { - "environment": "test" - }, - "plugins": { - "envoy_ext_authz_grpc": { - "path": %q, - "dry-run": false - } - } - }`, opaControlPlane.URL(), path)) -} diff --git a/filters/openpolicyagent/opaauthorizerequest/testResources/cert.pem b/filters/openpolicyagent/opaauthorizerequest/testResources/cert.pem new file mode 100644 index 0000000000..c84c236b0b --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/testResources/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIJAKjOTehGhMMYMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxGTAXBgNVBAMMEGxvY2FsZXhhbXBsZS5vcmcwHhcNMTkw +MTE4MTYzNzI3WhcNMTkwMTI3MTYzNzI3WjBgMQswCQYDVQQGEwJBVTETMBEGA1UE +CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk +MRkwFwYDVQQDDBBsb2NhbGV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA42H1BFt2/ZZuqNCl+VVG99sfRO4r5FXcJccUmHajLDIYdudA +jWLXMG9jhc+HfOLIKYWd5NGAJKAHVzNVbGgEDGG8icy83ShTkmMISEnyYqqDH6wf +qkxMwZ6hYQnJUveRPOejcfGVUNh8XNu7WC228qyWd+0JaVVxVU7F3ks2q/eRxKQM +fOZYh4gY5d4gt/JbAU8VNgWo9jCEuK+026CkpTyGVYa2iCLjOiKjjnKpixgSXhUU +kQsHITp2yTULHC9Wqn92VLCi1al/CCtpZExwO+jq5sihCCfE/Erq+wesnC+vFalE +BmV7K9qe+k0SGyM1O7yFLClCfbuuIEwguhCNVwIDAQABo1AwTjAdBgNVHQ4EFgQU +gJB4kmWERbmiOj3FQULuMtowTpswHwYDVR0jBBgwFoAUgJB4kmWERbmiOj3FQULu +MtowTpswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk2FmuKemxfNF +b0Aeoph/KEXekzM2YSsMjAlXiIt7SidNEy5XWLE30RKY1TXpYg3kB5qihLT7S0VB +UVV3vQwvc8htnuAJB+tukBcQzIFPHWFxdv69j+iRTVjf/zPXTS4CeoHujAVRlFuc +wbEumkxyT/MX4XfhBHPjhe7fy5MMULESAmWPHrOm5HO+HUVYt8OzBpYG2o3izJ+I +NFy9rAjfgbakvv0D2XnNl01n9gycLag6m6UVnXyvisiiNmFhobYlryM/O8T4uEo1 +rU62hJKI6iN3S9wtRDvNk6PwXIwJ0kGJkjFXR/K84ViN6DaQULwZDWyaytfvJKtY +2IZhpkvX0Q== +-----END CERTIFICATE----- diff --git a/filters/openpolicyagent/opaauthorizerequest/testResources/key.pem b/filters/openpolicyagent/opaauthorizerequest/testResources/key.pem new file mode 100644 index 0000000000..86abece3c0 --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/testResources/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDjYfUEW3b9lm6o +0KX5VUb32x9E7ivkVdwlxxSYdqMsMhh250CNYtcwb2OFz4d84sgphZ3k0YAkoAdX +M1VsaAQMYbyJzLzdKFOSYwhISfJiqoMfrB+qTEzBnqFhCclS95E856Nx8ZVQ2Hxc +27tYLbbyrJZ37QlpVXFVTsXeSzar95HEpAx85liHiBjl3iC38lsBTxU2Baj2MIS4 +r7TboKSlPIZVhraIIuM6IqOOcqmLGBJeFRSRCwchOnbJNQscL1aqf3ZUsKLVqX8I +K2lkTHA76OrmyKEIJ8T8Sur7B6ycL68VqUQGZXsr2p76TRIbIzU7vIUsKUJ9u64g +TCC6EI1XAgMBAAECggEARNs3HVitUeGqJQj6GeUPFqOOdotBU2YEwpPk0r6qbwnE +is1AqRATZiF1G+JafyEVyC0kQVSH349uaaOr7KYbA7zdIUWUs8wwcpX2vh1WfzZv +erne5O0yGTf2WrJh+vPpUW47+pdTxiWok14e/3bofdhxwGNRhEpRhanTFZvlNHGz +0p9SY8AyBbpNFEl0TkwLP+hcyKqz91jgOnE98MnUYKh6ZG3CY6L3Ca8A4BE3RuOw +gXdua7T2hsyMzYdv9aIdW+BMk2Fa3VFpvnm6Sh2Uje1KBIOuWTgSumcSLbaPVHKs +ZQthyOtt7ueJ46E2hdgoCP0R8Rbyqdy6bhUmCpFrEQKBgQD3A6+O0BhjJovKka5L +u33fphLtwnABHnH+QqPeUAy8yN2yiy0ZxL19WN0doAbF56xErTHYq+UriHgHfYAv +lYtmtocwkl782yiUscIVfA9sUovzG5fsVP9fRUC5xWo+Ak59NX6lr5QXoBltanON +YMAbwhz5q1NTAt9c9FPGjERLswKBgQDrp3OCqobVpcK2haLPFNsj+huqKPSp9STS +hjqUjUiPAQTuZyb8H3Q5NtHB5j354BzkqaOXFCKaqiJIKHz794IuDOvou7cjF+J4 +4GOxnCJOFsvtvBoYWTFF0lz4++tvBtPLYZnnN2fCF/olk4ZQ2/FAapF/m6ehTLBa +N3S52AzVzQKBgH7WUatPh3ZCML3PmGQi3judF1Mm6ERq0bXxmhtpl2DI863ecUYu +E+7tVn0D0vaEQ5zwIgxUF9UGujt+YfgNHgub5kc2obfNAUV5EWPe1DyXFm262YPt +EURmVxoNGMTGgm8grOt/ANgwyV25r6QE7iBSyHYbVynk67TbcLkfBWKpAoGBAKGw +kIIcTU20YYho4w/hSIdD6c10MoOW73//l5wr4Jg2Y9LMNiSR1GYZZgz71JRpoImI +l/VmXGPwznriRPeBmPHN7eQPQJY0ojC8DctkzOj1nVDuWp1QPY2hzCcOezj/3zxS +KI5MJsY3O2yi72r1rm+7bz+2Zms3Ol18ZXvKcOwVAoGBALYNfh4TYHRMLB8HDNZm +a7I1cq0N6ZPgvTzS/rf1qM0EabK8gvxDHzxAdnc1NLGx7JLd4svbATW7p5WjLYeu +0hh/DPy2LbTYLnhOn4a5Ncm+xKYfsvw4IQg98chxemQrGkwXSDsekujoCRUt2hmJ +kMwU3lK8Y0ChrcMQUvQasSrM +-----END PRIVATE KEY----- diff --git a/filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow.rego b/filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow.rego new file mode 100644 index 0000000000..2141d1b98a --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow.rego @@ -0,0 +1,11 @@ +package envoy.authz + +import rego.v1 + +default allow := false + +# METADATA +# entrypoint: true +allow if { + input.parsed_path = [ "allow" ] +} diff --git a/filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow_test.rego b/filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow_test.rego new file mode 100644 index 0000000000..b424ddeb72 --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/testResources/simple-opa-bundle/allow_test.rego @@ -0,0 +1,21 @@ +package envoy.authz_test + +import rego.v1 + +import data.envoy.authz + +test_not_allowed if { + not authz.allow with input as { + "parsed_path": [ + "some-path" + ], + } +} + +test_allowed if { + authz.allow with input as { + "parsed_path": [ + "allow" + ], + } +} diff --git a/skptesting/simple-opa-bundle/allow.rego b/skptesting/simple-opa-bundle/allow.rego new file mode 100644 index 0000000000..2141d1b98a --- /dev/null +++ b/skptesting/simple-opa-bundle/allow.rego @@ -0,0 +1,11 @@ +package envoy.authz + +import rego.v1 + +default allow := false + +# METADATA +# entrypoint: true +allow if { + input.parsed_path = [ "allow" ] +} diff --git a/skptesting/simple-opa-bundle/allow_test.rego b/skptesting/simple-opa-bundle/allow_test.rego new file mode 100644 index 0000000000..b424ddeb72 --- /dev/null +++ b/skptesting/simple-opa-bundle/allow_test.rego @@ -0,0 +1,21 @@ +package envoy.authz_test + +import rego.v1 + +import data.envoy.authz + +test_not_allowed if { + not authz.allow with input as { + "parsed_path": [ + "some-path" + ], + } +} + +test_allowed if { + authz.allow with input as { + "parsed_path": [ + "allow" + ], + } +} From 6e5018e97b9a260cbf1b5753387c7d5529225a78 Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 14 Feb 2025 12:20:12 +0100 Subject: [PATCH 1025/1047] filters/auth: tokeninfo cache comment (#3408) Signed-off-by: Alexander Yastrebov --- filters/auth/tokeninfocache.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/filters/auth/tokeninfocache.go b/filters/auth/tokeninfocache.go index df31bc62f6..76e2328be3 100644 --- a/filters/auth/tokeninfocache.go +++ b/filters/auth/tokeninfocache.go @@ -69,11 +69,8 @@ func (c *tokeninfoCache) cached(token string) map[string]any { now := c.now() e := v.(*entry) if now.Before(e.expiresAt) { - // It might be ok to return cached value - // without adjusting "expires_in" to avoid copy - // if caller never modifies the result and - // when "expires_in" did not change (same second) - // or for small TTL values + // Clone cached value because callers may modify it, + // see e.g. [OAuthConfig.GrantTokeninfoKeys] and [grantFilter.setupToken]. info := maps.Clone(e.info) info[expiresInField] = e.infoExpiresAt.Sub(now).Truncate(time.Second).Seconds() From 87b289c6de6ff7cf46de9d4b97a72f3ed8a0ec2c Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Fri, 14 Feb 2025 15:37:04 +0100 Subject: [PATCH 1026/1047] predicates/forwarded: use strings.Index to parse header (#3405) Use strings.Index instead of strings.Split to reduce memory allocations. See #3403 for details. Signed-off-by: Alexander Yastrebov --- predicates/forwarded/forwarded.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/predicates/forwarded/forwarded.go b/predicates/forwarded/forwarded.go index 6c1489c340..fb5b1e55d2 100644 --- a/predicates/forwarded/forwarded.go +++ b/predicates/forwarded/forwarded.go @@ -130,11 +130,10 @@ type forwarded struct { } func parseForwarded(fh string) *forwarded { - f := &forwarded{} - for _, forwardedFull := range strings.Split(fh, ",") { - for _, forwardedPair := range strings.Split(strings.TrimSpace(forwardedFull), ";") { + for forwardedFull := range splitSeq(fh, ",") { + for forwardedPair := range splitSeq(strings.TrimSpace(forwardedFull), ";") { token, value, found := strings.Cut(forwardedPair, "=") value = strings.Trim(value, `"`) if found && value != "" { @@ -147,6 +146,23 @@ func parseForwarded(fh string) *forwarded { } } } - return f } + +// TODO: use [strings.SplitSeq] added in go1.24 once go1.25 is released. +func splitSeq(s string, sep string) func(yield func(string) bool) { + return func(yield func(string) bool) { + for { + i := strings.Index(s, sep) + if i < 0 { + break + } + frag := s[:i] + if !yield(frag) { + return + } + s = s[i+len(sep):] + } + yield(s) + } +} From b3105ba27914379f8d2a63a3038a767d26dbb0ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:04:18 +0100 Subject: [PATCH 1027/1047] build(deps): bump amazonlinux from `8217eec` to `0377b0a` in /fuzz (#3411) Bumps amazonlinux from `8217eec` to `0377b0a`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 79ac9f7458..9a5bfb7058 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:8217eec04f21ec88f5fd0ce57641a9fa9a586af5c4d2a68c7fcd50f2146ddb03 +FROM amazonlinux:2023@sha256:0377b0a0f83dc67a044d179e0c4f069d2fbd3aeba43c545b61a26d5b6851b6d3 WORKDIR /workspace From fb0d1f085b02d7fcd159082476698cd3a5fc9026 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:04:32 +0100 Subject: [PATCH 1028/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3410) Bumps oss-fuzz-base/base-builder-go from `7031d0f` to `98616f3`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index c02495de17..b49751247f 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:7031d0f13d7cbd86a1c9a5807bb83196f3614c9c1631a0d34174bcbe6b9c12c3 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:98616f371934cbdd901131b29a36cd636ddcd407aa622d9a29bd96f1d2631630 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From d31b5916e81bba07695d47c7bda05ba152b2dc00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:51:32 +0100 Subject: [PATCH 1029/1047] build(deps): bump alpine from `56fa17d` to `a8560b3` in /packaging (#3412) Bumps alpine from `56fa17d` to `a8560b3`. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packaging/Dockerfile.arm64 | 2 +- packaging/Dockerfile.armv7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/Dockerfile.arm64 b/packaging/Dockerfile.arm64 index d722f2f7e9..7019e901da 100644 --- a/packaging/Dockerfile.arm64 +++ b/packaging/Dockerfile.arm64 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm64 alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 +FROM --platform=linux/arm64 alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm64/skipper \ diff --git a/packaging/Dockerfile.armv7 b/packaging/Dockerfile.armv7 index d2ec131224..947457c4d4 100644 --- a/packaging/Dockerfile.armv7 +++ b/packaging/Dockerfile.armv7 @@ -1,4 +1,4 @@ -FROM --platform=linux/arm/v7 alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 +FROM --platform=linux/arm/v7 alpine@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c LABEL maintainer="Team Gateway&Proxy @ Zalando SE " RUN apk --no-cache add ca-certificates && update-ca-certificates ADD build/linux/arm/v7/skipper \ From c1bd123d2b246c45e35e2a1fd27556ee6b70f300 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Feb 2025 20:32:15 +0100 Subject: [PATCH 1030/1047] build(deps): bump the all-go-mod-patch-and-minor group across 1 directory with 8 updates (#3409) Bumps the all-go-mod-patch-and-minor group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/instana/go-sensor](https://github.com/instana/go-sensor) | `1.66.2` | `1.67.1` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.32.0` | `0.33.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.34.0` | `0.35.0` | | [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.25.0` | `0.26.0` | | [golang.org/x/time](https://github.com/golang/time) | `0.9.0` | `0.10.0` | | google.golang.org/protobuf | `1.36.4` | `1.36.5` | Updates `github.com/instana/go-sensor` from 1.66.2 to 1.67.1 - [Release notes](https://github.com/instana/go-sensor/releases) - [Changelog](https://github.com/instana/go-sensor/blob/main/CHANGELOG.md) - [Commits](https://github.com/instana/go-sensor/compare/v1.66.2...v1.67.1) Updates `golang.org/x/crypto` from 0.32.0 to 0.33.0 - [Commits](https://github.com/golang/crypto/compare/v0.32.0...v0.33.0) Updates `golang.org/x/net` from 0.34.0 to 0.35.0 - [Commits](https://github.com/golang/net/compare/v0.34.0...v0.35.0) Updates `golang.org/x/oauth2` from 0.25.0 to 0.26.0 - [Commits](https://github.com/golang/oauth2/compare/v0.25.0...v0.26.0) Updates `golang.org/x/sync` from 0.10.0 to 0.11.0 - [Commits](https://github.com/golang/sync/compare/v0.10.0...v0.11.0) Updates `golang.org/x/term` from 0.28.0 to 0.29.0 - [Commits](https://github.com/golang/term/compare/v0.28.0...v0.29.0) Updates `golang.org/x/time` from 0.9.0 to 0.10.0 - [Commits](https://github.com/golang/time/compare/v0.9.0...v0.10.0) Updates `google.golang.org/protobuf` from 1.36.4 to 1.36.5 --- updated-dependencies: - dependency-name: github.com/instana/go-sensor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/term dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/time dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 20 ++++++++++---------- go.sum | 40 ++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 6b3fe38480..2889ba1e12 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.3 - github.com/instana/go-sensor v1.66.2 + github.com/instana/go-sensor v1.67.1 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.63 github.com/oklog/ulid v1.3.1 @@ -48,14 +48,14 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.33.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.34.0 - golang.org/x/oauth2 v0.25.0 - golang.org/x/sync v0.10.0 - golang.org/x/term v0.28.0 - golang.org/x/time v0.9.0 - google.golang.org/protobuf v1.36.4 + golang.org/x/net v0.35.0 + golang.org/x/oauth2 v0.26.0 + golang.org/x/sync v0.11.0 + golang.org/x/term v0.29.0 + golang.org/x/time v0.10.0 + google.golang.org/protobuf v1.36.5 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf @@ -172,8 +172,8 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/tools v0.26.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/go.sum b/go.sum index 00c0f9eef8..2ea19d7448 100644 --- a/go.sum +++ b/go.sum @@ -264,8 +264,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/instana/go-sensor v1.66.2 h1:ykUZWHfgaVkHn0S+K1RmHv9WF5e0wmHC8sxqYAlgRik= -github.com/instana/go-sensor v1.66.2/go.mod h1:Ngi6H3q4iZ6yn4EH9zQYUflI/eDTULrM3B+RW9HN4zI= +github.com/instana/go-sensor v1.67.1 h1:NwmXMtMb7LnNeKI/ns5Gr31hYLFakjSMDiedVgtH7+c= +github.com/instana/go-sensor v1.67.1/go.mod h1:mpCPkpkqZcFWpY9hYt6s2Myz+hZ2W2bYhHu/GZj2jp8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -554,8 +554,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -595,12 +595,12 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= +golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -610,8 +610,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -644,17 +644,17 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -714,8 +714,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= From c1b5dfd494698f8f97412044400a00b3189aceef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:24:24 +0100 Subject: [PATCH 1031/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3413) Bumps oss-fuzz-base/base-builder-go from `98616f3` to `d354bdf`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index b49751247f..3a51ba20ae 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:98616f371934cbdd901131b29a36cd636ddcd407aa622d9a29bd96f1d2631630 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:d354bdf1a0843e869593a7030accd20a6e9a964fe699a010f8d671a8914ad448 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From 81b30264a1817fb589024a49a80633ac87b483c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:24:54 +0100 Subject: [PATCH 1032/1047] build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.1 (#3415) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.0 to 2.4.1. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/62b2cac7ed8198b15735ed49ab1e5cf35480ba46...f49aabe0b5af0936a0987cfb85d86b75731b0186) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index ef0e6732aa..8a206f0ad4 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -39,7 +39,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 with: results_file: results.sarif results_format: sarif From 3084d79d3da61f2db25ef1eace0e53d1bc6764f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:25:32 +0100 Subject: [PATCH 1033/1047] build(deps): bump actions/cache from 4.2.0 to 4.2.1 (#3416) Bumps [actions/cache](https://github.com/actions/cache) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/1bd1e32a3bdc45362d1e726936510720a7c30a57...0c907a75c2c80ebcb7f088228285e798b750cf8f) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 162c30e5e1..f2407c9110 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 with: python-version: 3.x - - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 + - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f with: key: ${{ github.ref }} path: .cache From 97d0373943874b3e2e3a9577633dfba6cdd39a33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:27:37 +0100 Subject: [PATCH 1034/1047] build(deps): bump the all-go-mod-patch-and-minor group with 4 updates (#3414) Bumps the all-go-mod-patch-and-minor group with 4 updates: [github.com/google/go-cmp](https://github.com/google/go-cmp), [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang), [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) and [golang.org/x/crypto](https://github.com/golang/crypto). Updates `github.com/google/go-cmp` from 0.6.0 to 0.7.0 - [Release notes](https://github.com/google/go-cmp/releases) - [Commits](https://github.com/google/go-cmp/compare/v0.6.0...v0.7.0) Updates `github.com/prometheus/client_golang` from 1.20.5 to 1.21.0 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.5...v1.21.0) Updates `github.com/redis/go-redis/v9` from 9.7.0 to 9.7.1 - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.7.0...v9.7.1) Updates `golang.org/x/crypto` from 0.33.0 to 0.34.0 - [Commits](https://github.com/golang/crypto/compare/v0.33.0...v0.34.0) --- updated-dependencies: - dependency-name: github.com/google/go-cmp dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 12 ++++++------ go.sum | 23 ++++++++++++----------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 2889ba1e12..c5f476b268 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/envoyproxy/go-control-plane v0.13.1 github.com/ghodss/yaml v1.0.0 github.com/golang-jwt/jwt/v4 v4.5.1 - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/hashicorp/memberlist v0.5.3 github.com/instana/go-sensor v1.67.1 @@ -31,10 +31,10 @@ require ( github.com/open-policy-agent/opa-envoy-plugin v0.70.0-envoy-1 github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.20.5 + github.com/prometheus/client_golang v1.21.0 github.com/prometheus/client_model v0.6.1 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 - github.com/redis/go-redis/v9 v9.7.0 + github.com/redis/go-redis/v9 v9.7.1 github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 github.com/sirupsen/logrus v1.9.3 github.com/sony/gobreaker v1.0.0 @@ -48,7 +48,7 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.33.0 + golang.org/x/crypto v0.34.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.35.0 golang.org/x/oauth2 v0.26.0 @@ -118,7 +118,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/influxdata/tdigest v0.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect github.com/looplab/fsm v1.0.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -144,7 +144,7 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect diff --git a/go.sum b/go.sum index 2ea19d7448..99c255a53d 100644 --- a/go.sum +++ b/go.sum @@ -224,8 +224,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -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-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -281,8 +282,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -398,8 +399,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -410,8 +411,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -421,8 +422,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= -github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc= +github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -554,8 +555,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= +golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= From f9f2f2af464fae8bc98801d493b32b8b17d16f1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:52:12 +0100 Subject: [PATCH 1035/1047] build(deps): bump actions/upload-artifact from 4.6.0 to 4.6.1 (#3417) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08...4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/openssf-scorecard.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 8a206f0ad4..ebb5563c7e 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -61,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: SARIF file path: results.sarif From 99209d73bb8d94928392fabbf7d21d639e5cf651 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:53:06 +0100 Subject: [PATCH 1036/1047] build(deps): bump docker/build-push-action from 6.13.0 to 6.14.0 (#3418) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.13.0 to 6.14.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/ca877d9245402d1537745e0e356eab47c3520991...0adf9959216b96bec444f325f1e493d4aa344497) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index bf61a41e62..8412a27808 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 + uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From 2ddf741050a1b904ecf10e3d5be574eccabf5d7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 10:57:20 +0100 Subject: [PATCH 1037/1047] build(deps): bump github.com/go-jose/go-jose/v4 in the go_modules group (#3419) Bumps the go_modules group with 1 update: [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose). Updates `github.com/go-jose/go-jose/v4` from 4.0.2 to 4.0.5 - [Release notes](https://github.com/go-jose/go-jose/releases) - [Changelog](https://github.com/go-jose/go-jose/blob/main/CHANGELOG.md) - [Commits](https://github.com/go-jose/go-jose/compare/v4.0.2...v4.0.5) --- updated-dependencies: - dependency-name: github.com/go-jose/go-jose/v4 dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c5f476b268..8daab8751f 100644 --- a/go.mod +++ b/go.mod @@ -95,7 +95,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect diff --git a/go.sum b/go.sum index 99c255a53d..3f49cf4da4 100644 --- a/go.sum +++ b/go.sum @@ -158,8 +158,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= -github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= From 2e4116ad77130ca3a975225a90fc5f79eef5972e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:54:11 +0100 Subject: [PATCH 1038/1047] build(deps): bump docker/metadata-action from 5.6.1 to 5.7.0 (#3422) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.6.1 to 5.7.0. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/369eb591f429131d6889c46b94e711f089e6ca96...902fa8ec7d6ecbf8d84d538b9b233a880e428804) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 8412a27808..0abfe61c5a 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -61,7 +61,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Docker meta - uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 id: meta with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} From 448be0acee84d63e91d2b5aae6f9df88a8d3ffb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:54:41 +0100 Subject: [PATCH 1039/1047] build(deps): bump docker/setup-qemu-action from 3.4.0 to 3.6.0 (#3423) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.4.0 to 3.6.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/4574d27a4764455b42196d70a065bc6853246a25...29109295f81e9208d7d86ff1c6c12d2833863392) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 0abfe61c5a..7d57defb4c 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -47,7 +47,7 @@ jobs: make build.linux - name: Set up QEMU - uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 - name: Set up Docker Buildx uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca From c536a04a74217de60b786391ac7bf6d0db1ec541 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:55:02 +0100 Subject: [PATCH 1040/1047] build(deps): bump actions/cache from 4.2.1 to 4.2.2 (#3424) Bumps [actions/cache](https://github.com/actions/cache) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/0c907a75c2c80ebcb7f088228285e798b750cf8f...d4323d4df104b026a6aa633fdb11d772146be0bf) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index f2407c9110..3c8876b36e 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 with: python-version: 3.x - - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f + - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf with: key: ${{ github.ref }} path: .cache From 3a902ac808224e3dd039db80fb92265b9b2a9329 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:56:22 +0100 Subject: [PATCH 1041/1047] build(deps): bump amazonlinux from `0377b0a` to `5af63ef` in /fuzz (#3429) Bumps amazonlinux from `0377b0a` to `5af63ef`. --- updated-dependencies: - dependency-name: amazonlinux dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- fuzz/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Dockerfile b/fuzz/Dockerfile index 9a5bfb7058..87517b62c1 100644 --- a/fuzz/Dockerfile +++ b/fuzz/Dockerfile @@ -1,4 +1,4 @@ -FROM amazonlinux:2023@sha256:0377b0a0f83dc67a044d179e0c4f069d2fbd3aeba43c545b61a26d5b6851b6d3 +FROM amazonlinux:2023@sha256:5af63ef484a9613b3080bb3ae722b6e46b53714360290c5edba71f1af3ac733b WORKDIR /workspace From a4988a21ee57b03d7ef14ad69c3e91c675d6d84e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:57:05 +0100 Subject: [PATCH 1042/1047] build(deps): bump oss-fuzz-base/base-builder-go in /.clusterfuzzlite (#3428) Bumps oss-fuzz-base/base-builder-go from `d354bdf` to `41601fc`. --- updated-dependencies: - dependency-name: oss-fuzz-base/base-builder-go dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .clusterfuzzlite/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 3a51ba20ae..fa67d28e8d 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:d354bdf1a0843e869593a7030accd20a6e9a964fe699a010f8d671a8914ad448 +FROM gcr.io/oss-fuzz-base/base-builder-go@sha256:41601fc0fe28208aa765cdfa45e9d194b6e1e6447be4bf8efd3ccaca67373180 COPY . $SRC/skipper COPY ./.clusterfuzzlite/build.sh $SRC/ From af45b9d401d8adda3ca32031a546268c7d0a0eb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 15:34:09 +0100 Subject: [PATCH 1043/1047] build(deps): bump docker/setup-buildx-action from 3.9.0 to 3.10.0 (#3425) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.9.0 to 3.10.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca...b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index 7d57defb4c..d77904cd66 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -50,7 +50,7 @@ jobs: uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca + uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' From 745200699cab3991b108b02b0d6842bd402867b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:51:16 +0100 Subject: [PATCH 1044/1047] build(deps): bump the all-go-mod-patch-and-minor group with 2 updates (#3427) Bumps the all-go-mod-patch-and-minor group with 2 updates: [golang.org/x/crypto](https://github.com/golang/crypto) and [golang.org/x/oauth2](https://github.com/golang/oauth2). Updates `golang.org/x/crypto` from 0.34.0 to 0.35.0 - [Commits](https://github.com/golang/crypto/compare/v0.34.0...v0.35.0) Updates `golang.org/x/oauth2` from 0.26.0 to 0.27.0 - [Commits](https://github.com/golang/oauth2/compare/v0.26.0...v0.27.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-go-mod-patch-and-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8daab8751f..85d7bf6e1e 100644 --- a/go.mod +++ b/go.mod @@ -48,10 +48,10 @@ require ( github.com/yookoala/gofast v0.8.0 github.com/yuin/gopher-lua v1.1.1 go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab - golang.org/x/crypto v0.34.0 + golang.org/x/crypto v0.35.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.35.0 - golang.org/x/oauth2 v0.26.0 + golang.org/x/oauth2 v0.27.0 golang.org/x/sync v0.11.0 golang.org/x/term v0.29.0 golang.org/x/time v0.10.0 diff --git a/go.sum b/go.sum index 3f49cf4da4..50b87328c5 100644 --- a/go.sum +++ b/go.sum @@ -555,8 +555,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= -golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -600,8 +600,8 @@ golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From e4951a77670f8b618adad2b838cd0ce96663756e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:57:01 +0100 Subject: [PATCH 1045/1047] build(deps): bump docker/build-push-action from 6.14.0 to 6.15.0 (#3426) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.14.0 to 6.15.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/0adf9959216b96bec444f325f1e493d4aa344497...471d1dc4e07e5cdedd4c2171150001c434f0b7a4) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gh-packages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-packages.yaml b/.github/workflows/gh-packages.yaml index d77904cd66..75c576db15 100644 --- a/.github/workflows/gh-packages.yaml +++ b/.github/workflows/gh-packages.yaml @@ -73,7 +73,7 @@ jobs: org.opencontainers.image.vendor=Zalando SE - name: Build and push - uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 + uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 with: context: ./packaging build-args: BASE_IMAGE=golang:alpine From b06d1f139a91bfb663884e22315e1c6060b00243 Mon Sep 17 00:00:00 2001 From: Farasath Ahamed Date: Tue, 4 Mar 2025 20:44:23 +0100 Subject: [PATCH 1046/1047] filters/openpolicyagent: Resolve race conditions in OPA filter benchmark tests (#3432) Changes, - Move context creation to b.RunParallel loop to prevent concurrent modification. - Add missing assertions in benchmark tests. - Remove duplicate test resource. Signed-off-by: Farasath Ahamed Co-authored-by: Farasath Ahamed --- .../opaauthorizerequest/benchmark_test.go | 87 +++++++++---------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go b/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go index c2d80b4861..67369cfd1a 100644 --- a/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/benchmark_test.go @@ -77,18 +77,17 @@ func BenchmarkMinimalPolicy(b *testing.B) { reqUrl, err := url.Parse("http://opa-authorized.test/allow") require.NoError(b, err) - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - URL: reqUrl, - }, - FMetrics: &metricstest.MockMetrics{}, - } - b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + URL: reqUrl, + }, + FMetrics: &metricstest.MockMetrics{}, + } f.Request(ctx) assert.False(b, ctx.FServed) } @@ -128,19 +127,19 @@ func BenchmarkMinimalPolicyWithDecisionLogs(b *testing.B) { reqUrl, err := url.Parse("http://opa-authorized.test/allow") require.NoError(b, err) - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - URL: reqUrl, - }, - FMetrics: &metricstest.MockMetrics{}, - } - b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + URL: reqUrl, + }, + FMetrics: &metricstest.MockMetrics{}, + } f.Request(ctx) + assert.False(b, ctx.FServed) } }) } @@ -171,24 +170,25 @@ func BenchmarkAllowWithReqBody(b *testing.B) { require.NoError(b, err) body := `{"email": "bench-test@zalando.de"}` - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - Method: "POST", - Header: map[string][]string{ - "Content-Type": {"application/json"}, - }, - URL: reqUrl, - Body: io.NopCloser(strings.NewReader(body)), - ContentLength: int64(len(body)), - }, - FMetrics: &metricstest.MockMetrics{}, - } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + Method: "POST", + Header: map[string][]string{ + "Content-Type": {"application/json"}, + }, + URL: reqUrl, + Body: io.NopCloser(strings.NewReader(body)), + ContentLength: int64(len(body)), + }, + FMetrics: &metricstest.MockMetrics{}, + } f.Request(ctx) + assert.False(b, ctx.FServed) } }) @@ -311,19 +311,18 @@ func BenchmarkMinimalPolicyBundle(b *testing.B) { requestUrl, err := url.Parse("http://opa-authorized.test/allow") assert.NoError(b, err) - ctx := &filtertest.Context{ - FStateBag: map[string]interface{}{}, - FResponse: &http.Response{}, - FRequest: &http.Request{ - URL: requestUrl, - Method: "GET", - }, - FMetrics: &metricstest.MockMetrics{}, - } - b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { + ctx := &filtertest.Context{ + FStateBag: map[string]interface{}{}, + FResponse: &http.Response{}, + FRequest: &http.Request{ + URL: requestUrl, + Method: "GET", + }, + FMetrics: &metricstest.MockMetrics{}, + } f.Request(ctx) assert.False(b, ctx.FServed) } @@ -337,8 +336,8 @@ func newDecisionConsumer() *httptest.Server { } func newOpaControlPlaneServingBundle(bundlePath, bundleName string, b *testing.B) *httptest.Server { - if !strings.HasSuffix(bundlePath, ".tar.gz") { - b.Fatalf("bundle file %q does not have .tar.gz extension", bundlePath) + if !(strings.HasSuffix(bundlePath, ".tar.gz") || strings.HasSuffix(bundlePath, ".tgz")) { + b.Fatalf("bundle file %q does not have the expected .tar.gz or .tgz extension", bundlePath) } fileData, err := os.ReadFile(bundlePath) From 4dab609f1683afd558b6f9f64025401c0557ae8d Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Wed, 5 Mar 2025 15:48:59 +0100 Subject: [PATCH 1047/1047] go.mod: use latest 1.23 version (#3434) Current go version is 1.24 and we support two latest go versions. This change updates go.mod to use latest go 1.23 version to include all available go runtime security fixes. It also bumps minor skipper version as per https://github.com/zalando/skipper/?tab=readme-ov-file#version-promise (overlooked within #3406). Signed-off-by: Alexander Yastrebov --- VERSION | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 6e75c01de5..9d40912649 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.21 +v0.22 diff --git a/go.mod b/go.mod index 85d7bf6e1e..5eee59c91a 100644 --- a/go.mod +++ b/go.mod @@ -184,4 +184,4 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -go 1.23.0 +go 1.23.6