Compare commits

...

825 Commits

Author SHA1 Message Date
Kubernetes Publisher 6016cdfb88 Merge pull request #132803 from aramase/aramase/f/token_request_uid_check
Make kubelet token cache UID-aware to prevent stale tokens after service account recreation

Kubernetes-commit: 2bdcad7419c7a584da4174513e36f867e88424df
2025-07-11 09:20:03 +00:00
Kubernetes Publisher 6dc2a56d3a Merge pull request #132848 from serathius/etcd3-compactor
Expose compaction revision from compactor

Kubernetes-commit: 054750fc262a62dd311a52f31e1577d6096bcaa9
2025-07-10 21:20:36 +00:00
Marek Siarkowicz dda2fcb797 Expose compaction revision from compactor
Kubernetes-commit: 03e32bd260fbd77351e1b528f22a69a8eaece9ee
2025-07-09 16:22:19 +02:00
Marek Siarkowicz 34f5472b35 Add test for compactor
Kubernetes-commit: 5e3ea8e279c51a0b4fcc996d244d2c5593935816
2025-07-09 16:29:55 +02:00
Kubernetes Publisher 532260d25f Merge pull request #132813 from serathius/compact-test
Add testing compaction in storage List tests

Kubernetes-commit: da7058e92caf87208a15331020051c0523d71cfd
2025-07-09 21:30:45 +00:00
Kubernetes Publisher 0a000a99f6 Merge pull request #132715 from googs1025/fix/flaky_patch
fix: TestPatchResourceTimeout flaky test

Kubernetes-commit: 4142fb530ceb25d9965a3e6925be79a03d918cba
2025-07-08 17:28:12 +00:00
Kubernetes Publisher e824ccb0fa Merge pull request #132788 from ylink-lfs/chore/strptr_removal
chore: remove strPtr usage with ptr.To

Kubernetes-commit: 3d13f39f24e5533286278f04702c69fbed448c35
2025-07-08 09:20:06 +00:00
Marek Siarkowicz 2f077fcbca Add testing compaction in storage List tests
Kubernetes-commit: c53b41e98c2e9248916704f36443de87cbee64e0
2025-07-08 11:19:59 +02:00
Kubernetes Publisher d7827fc7e8 Merge pull request #132794 from PatrickLaabs/132749-boolptr
chore: replacement of boolPtr helper functions to ptr packge

Kubernetes-commit: 5f34f9233b2e2f9714b71dc1e1a0dbf24548866c
2025-07-08 05:20:18 +00:00
Kubernetes Publisher 32cbb819fe Merge pull request #132799 from BenTheElder/etcd-apiserver-panic
storage/etcd3: add back missing errcheck

Kubernetes-commit: 70e225397bb8e397d5219291f2dbfbb4aaaa999e
2025-07-08 05:20:16 +00:00
Benjamin Elder bf8c1a6d90 storage/etcd3: add back missing errcheck
lost in recent refactoring.

Kubernetes-commit: 838f3afc5219640725922266b689c7d24fbdf804
2025-07-07 17:37:36 -07:00
Anish Ramasekar c42cceba12 Add TokenRequestServiceAccountUIDValidation feature gate with UID validation
This change introduces the TokenRequestServiceAccountUIDValidation feature
gate and implements feature-gated service account UID validation for the
TokenRequest API. When enabled, the API validates that the service account
UID in token requests matches the actual service account UID, preventing
token requests for recreated service accounts with the same name but
different UIDs.

Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 45dfb46448a130482e9050664f9d7f7288c0b4b3
2025-07-07 16:51:41 -07:00
PatrickLaabs d972fefbb0 chore: replacement of helper functions to ptr packge
Kubernetes-commit: cfd65c5f74dbb5fa02e7b20c3b1039193567ba53
2025-07-07 21:28:54 +02:00
ylink-lfs 8cd1907cc9 chore: remove strPtr usage with ptr.To instead
Kubernetes-commit: 830a088d1bc7075bbb176b44d73a79adfa659d75
2025-07-07 20:12:31 +08:00
Kubernetes Publisher 5e1c1db1da Merge pull request #132752 from PatrickLaabs/132086-apiserver-2
chore: second depr. pointer pkg replacement for apiserver (2/2)

Kubernetes-commit: 116cfc0c5f38f37be05542d468943790cedde7c4
2025-07-07 01:28:13 +00:00
Kubernetes Publisher 03b538a8e8 Merge pull request #132751 from PatrickLaabs/132086-apiserver-1
chore: first depr. pointer pkg replacement for apiserver (1/2)

Kubernetes-commit: d7a06387fe7305df5d7b3ebbab998686fae84a63
2025-07-06 21:20:28 +00:00
PatrickLaabs d0ee71ec17 chore: second depr. pointer pkg replacement for apiserver
Kubernetes-commit: fe61e825e046d793d64fc0686110212dacf8231e
2025-07-06 11:09:16 +02:00
PatrickLaabs 9b1ef94042 chore: first depr. pointer pkg replacement for apiserver
Kubernetes-commit: 4a61b8170138998a1f1d49f01ad38e8e3aa88f20
2025-07-06 10:58:23 +02:00
googs1025 f482a77223 fix: TestPatchResourceTimeout flaky test
Signed-off-by: googs1025 <googs1025@gmail.com>

Kubernetes-commit: 83584d13c495d39392f02ac0ef5d753f7ef67a04
2025-07-04 11:34:06 +08:00
Kubernetes Publisher 7364e005dc Merge pull request #132314 from thockin/jp_nicer_api_errors
Nicer value rendering in API errors

Kubernetes-commit: 2a4b5f64761f1578409c3f69ef7aaab614cdd907
2025-07-03 09:20:11 +00:00
Kubernetes Publisher d2a696650a Merge pull request #131902 from liuxu623/fix-storage-objects-metrics
remove apiserver_storage_objects metrics after crd deleted

Kubernetes-commit: 2c2530c3dd31a31ae2a54cf5d19da94eb21a1eff
2025-07-03 01:33:26 -07:00
liuxu ab47d6f39c remove apiserver_storage_objects metrics after crd deleted
Kubernetes-commit: 8c6c7df736dff27903bc03a1b9a42c1e2c84c7ee
2025-07-03 09:57:15 +08:00
Kubernetes Publisher c93fe8d40f Merge pull request #132675 from dims/bump-sigs-k8s-io-json-no-code-changes
Bump sigs.k8s.io/json to latest - no code changes

Kubernetes-commit: e47ac3eb6faa97874658dc281c72b5623f994801
2025-07-03 01:30:47 +00:00
Kubernetes Publisher 2898f40277 Merge pull request #132677 from dims/update-github.com/emicklei/go-restful/v3-to-v3.12.2
Update github.com/emicklei/go-restful/v3 to v3.12.2

Kubernetes-commit: 305c0e06c99ff9df013edd804294217a04e4dd31
2025-07-02 21:26:27 +00:00
Kubernetes Publisher 3637a3a5aa Merge pull request #132049 from googs1025/fix/data_race_patch
fix: data race for patchResource func

Kubernetes-commit: 0a51ae22542b6e78fec51a573d95a922592f8273
2025-07-02 21:26:25 +00:00
Kubernetes Publisher 179348d538 Merge pull request #132676 from dims/bump-go.yaml.in/yaml/v3-to-v3.0.4
Bump go.yaml.in/yaml/v3 to v3.0.4

Kubernetes-commit: 01c03ae9cf7b1371c8bc2bdf12d9244e63e83750
2025-07-02 17:29:59 +00:00
Davanum Srinivas 6bc5ac2a1a Update github.com/emicklei/go-restful/v3 to v3.12.2
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: b44b0fbf1299c6821033076352b91914d2efef67
2025-07-02 08:00:43 -04:00
Davanum Srinivas b6975dfbe9 Bump go.yaml.in/yaml/v3 to v3.0.4
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 58e620cc4403d30f9fb6aab245cfb47db17957de
2025-07-02 07:37:06 -04:00
Davanum Srinivas 61137484d0 Bump sigs.k8s.io/json to latest - no code changes
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 00f8cbae6b8fd3799a1a044abcefdbb572d35b27
2025-07-02 07:32:24 -04:00
Kubernetes Publisher e26c3d7e23 Merge pull request #132654 from Jefftree/b-openapi
Bump kube-openapi

Kubernetes-commit: db49c25956df36c777213251c4a47d6d9ee1c5ea
2025-07-01 21:31:05 +00:00
Kubernetes Publisher 7710d9ff3e Merge pull request #132648 from serathius/kep-5116-ga
Graduate streaming list encoding feature gates

Kubernetes-commit: 20141c8a92a76900b0b207ca7c0fd4832bacaa55
2025-07-01 17:20:21 +00:00
Jefftree 08b1bbe4b9 Update vendor
Kubernetes-commit: d04ee27c98ba91680ac6c6a8ade9e33d7ee44569
2025-07-01 15:23:58 +00:00
Jefftree ff84ca3520 pin kube-openapi to v0.0.0-20250628140032-d90c4fd18f59
Kubernetes-commit: b41d375b8881f25ff5fe7775b4dedaba1eaa3f02
2025-07-01 15:21:22 +00:00
Marek Siarkowicz 2c9f20fd49 Graduate streaming list encoding feature gates
Kubernetes-commit: 799aa8b2a8cd4d6768d1c9cd4aa7df3a8f9d6113
2025-07-01 13:04:40 +02:00
Kubernetes Publisher 3565462805 Merge pull request #132472 from xiaoweim/validation-cleanup
Cleanup: Remove redundant detail messages in field.Required

Kubernetes-commit: eb1b603cda3b956e52bddf3b51748191e80a59a6
2025-07-01 09:41:38 +00:00
Kubernetes Publisher 09be259d76 Merge pull request #132625 from ylink-lfs/chore/rest_delete_utilpointer_removal
chore: remove utilpointer usage in package staging/src/k8s.io/apiserver/pkg/registry/rest/delete

Kubernetes-commit: ffa8eb89dab40c15b8393cbe3c67df0b91d490e0
2025-07-01 09:41:36 +00:00
Kubernetes Publisher ee98ed2094 Merge pull request #128767 from knrc/nested-filterlatency-tracing
Trace across start handler invocations, nesting spans

Kubernetes-commit: a87674895178ced3f6fc1987e3fc1ee76b05629c
2025-07-01 05:42:06 +00:00
ylink-lfs 6874062f41 chore: remove utilpointer usage in package staging/src/k8s.io/apiserver/pkg/registry/rest/delete
Kubernetes-commit: a0ea2569a760f114f62e40b494c376e90f2d9ede
2025-06-30 20:50:51 +08:00
Kubernetes Publisher 06b7bf2af7 Merge pull request #132438 from dims/golangci-plugin-for-sorting-feature-gates
golangci plugin for sorting feature gates

Kubernetes-commit: 2e02a0088574d126c6d4a82ea472c7cb6c3252f5
2025-06-27 21:50:21 +00:00
xiaoweim af5056a3db Cleanup: Remove redundant detail messages in field.Required
Kubernetes-commit: 8632257c9340aeae824c99642376a78f69b3ea5d
2025-06-26 19:41:17 +00:00
Davanum Srinivas 8c685320dc Ensure all the files have the updated sorting
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: d50e1a684cd53950210041b875ae3a41dac05c33
2025-06-25 08:21:01 -04:00
Kubernetes Publisher 895f2b3947 Merge pull request #132355 from serathius/apf_estimate_size
Estimate average size of objects in etcd and plug it into request cost estimator

Kubernetes-commit: 025636181393cc95bf78a48238fff5c8b05d6404
2025-06-26 13:41:20 +00:00
Kubernetes Publisher 2ca55b4189 Merge pull request #132150 from serathius/watchcache-consistent-list
Handle consistent LIST in watch cache to avoid incorrect semantics while setting ResourceVersion on options

Kubernetes-commit: 24d9d184c2002256ce1e88c8f818a7ed007cfe51
2025-06-26 04:46:35 -07:00
Marek Siarkowicz 67c4a8a9ee Add benchmark for SizeBasedListCostEstimate feature
Kubernetes-commit: 1639b090839b37ab08c1d90d62f2c7a4c44faba4
2025-06-25 15:50:34 +02:00
Marek Siarkowicz df850b5a51 Override getKeys when cacher is enabled
Kubernetes-commit: e2c6b7fdf97206cc60d6c5f0e1cb306652ec189a
2025-06-23 16:35:06 +02:00
Marek Siarkowicz b805c4d851 Run background cleanup goroutine
Kubernetes-commit: 7cb241799935745277125db033ae217fe64e322c
2025-06-18 17:24:17 +02:00
Tim Hockin 19608fe81a WIP: Fix tests
Notes:
* For types that define String() - should we prefer that or JSON?
* metav1.Time has a MarshalJSON() and inhereits a String() and they are
  different
* Since validation runs on internal types, we still get some GoNames
  instead of goNames.

Kubernetes-commit: 4ca91a03052ebf31d373a0de6e12891ae15966b9
2025-06-18 10:23:01 +09:00
Marek Siarkowicz cf27dab482 Estimate average size of objects in etcd and plug it into request cost estimator
Kubernetes-commit: ec78b8305ad392f6faf4e5247ea33ceabb484c3f
2025-06-13 16:34:42 +02:00
Marek Siarkowicz eaf6e48501 Handle consistent LIST in watch cache to avoid incorrect semantics while setting ResourceVersion on options
Kubernetes-commit: 292679a28ae472da29c5d860afdb5c2250637d31
2025-06-06 17:16:46 +02:00
Kubernetes Publisher 07ec5c5838 Merge pull request #132357 from dims/drop-usage-of-forked-copies-of-goyaml.v2-and-goyaml.v3
Drop usage of forked copies of goyaml.v2 and goyaml.v3

Kubernetes-commit: c1afec6a0b15ca1ed853c1321ac2c972488bf5b8
2025-06-25 17:49:28 +00:00
Kubernetes Publisher 662ba26adb Merge pull request #132504 from jpbetz/name-formats
Introduce OpenAPI format support for k8s-short-name and k8s-long-name

Kubernetes-commit: 1d932bd6cc951b9182d07d701946aebaf667df94
2025-06-25 17:49:26 +00:00
Lukasz Szaszkiewicz a77ab41bc0 client-go/reflector: stop exposing UseWatchList (#132453)
* client-go/reflector: stop exposing UseWatchList

* apiserver/cacher: stop setting reflector.UseWatchList

* test/integration/watchlist: fix TestReflectorWatchListFallback

Kubernetes-commit: b8b3984874e930c92057589fd1a7668dbdffc117
2025-06-25 13:45:59 +00:00
Kubernetes Publisher 1a56a2f375 Merge pull request #132374 from PatrickLaabs/132358
Fixed large resourceversion and limit for storages

Kubernetes-commit: 49bff137a7211ff300028bfdba3ce1929a5dfb2b
2025-06-25 13:45:54 +00:00
Davanum Srinivas 8ee91f3b4d switch to latest sigs.k8s.io/yaml v1.5.0 (run update-gofmt.sh as well)
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: c5b4b133ce3252ee19b7167eb69a99d88fdefda8
2025-06-25 08:03:06 -04:00
Kubernetes Publisher 942549e2f9 Merge pull request #132497 from p0lyn0mial/upstream-watchlist-etcd-cacher
apiserver/storage/cacher/listwatcher: error when the WatchList FG is disabled

Kubernetes-commit: 8eda367f27b79adf5b41f019ede7708abee9602d
2025-06-25 09:40:56 +00:00
Lukasz Szaszkiewicz 0d3a31d996 apiserver/storage/cacher/listwatcher: error when the WatchList FG is disabled
Kubernetes-commit: 7e0d71fc14bcbe1fae42ccc91f2d48fc9b99a049
2025-06-25 08:50:03 +02:00
Kubernetes Publisher 3f2a0b72ba Merge pull request #132499 from liggitt/unwanted-json-patch-v5
Add json-patch v4 compatibility test

Kubernetes-commit: 8d2a5a2c9c40e3c4e6aa2804cf167ea7fe169d55
2025-06-24 17:51:50 +00:00
Kubernetes Publisher 75e69719a1 Merge pull request #132470 from michaelasp/fixInvalidObj
fix: Improve error messaging on updating a deleted object

Kubernetes-commit: 80474f7b4ab495d5cbda4d58df57bededc7cf8bc
2025-06-24 17:51:49 +00:00
Kubernetes Publisher 389ff851fc Merge pull request #132479 from p0lyn0mial/upstream-watchlist-cacher-listwatcher
apiserver/cacher: properly wire listwatch options to the listwatcher

Kubernetes-commit: ae15bc5613ef3c0f90c940caad56467ff817c962
2025-06-24 13:41:08 +00:00
Joe Betz bab2fbdc6f Bump to latest kube-openapi
Kubernetes-commit: dc323756cea2d1ebe32d7acb5a14a1769c14486f
2025-06-24 09:24:27 -04:00
Jordan Liggitt 3d4aed0ded Add json-patch v4 compatibility test
Kubernetes-commit: d180fe28ad5154eae7930a518640a8860e5a96d8
2025-06-24 09:23:39 -04:00
Lukasz Szaszkiewicz 0e36545f56 apiserver/cacher: properly wire listwatch options to the listwatcher
Kubernetes-commit: dce69afadeaf3fc8c98d1dfd56394fb77d52225b
2025-06-24 10:55:06 +02:00
Michael Aspinwall a47cc30656 fix: Improve error messaging on updating a deleted object
Kubernetes-commit: 56d97283afb538b9758e23438fcf1074bc96c1f6
2025-06-23 21:37:00 +00:00
Kubernetes Publisher 0dd61ee79d Merge pull request #132454 from serathius/watchcache-close
Move UnsafeCorruptObjectDeletion outside of etcd3.New function

Kubernetes-commit: 314af2a00b2dd41576be316a92b3d1a1c05484c1
2025-06-23 06:00:53 -07:00
Marek Siarkowicz baef40d3ec Move UnsafeCorruptObjectDeletion outside of etcd3.New function
By returning *store instead of storage.Interface we can expose Close()
function so in the future we can register it to destroyFunc in
newETCD3Storage.

Kubernetes-commit: 72305f82f9a20315d7ff4904a840bfd56478d44a
2025-06-23 10:50:06 +02:00
Kubernetes Publisher d16c916aad Merge pull request #129438 from pacoxu/apiserver-probe-etcd
add etcd server overrides to etcd probe factory for healthz and readyz

Kubernetes-commit: b569406b792fef24bb5613f1263ea354755bbae0
2025-06-19 21:40:54 +00:00
PatrickLaabs fdccb8b2dc fixing large resourceversion and limit for storages
Kubernetes-commit: ccdef28acd3a286e8d62222ddf804ae4042764e5
2025-06-18 16:22:13 +02:00
Kubernetes Publisher 7a9026cb3a Merge pull request #132253 from serathius/watchcache-fallback-test
Validate requests sent to etcd in TestList "test List with limit" scenario

Kubernetes-commit: 46e2c3fc2d2db16d44a9a21e0c6f8be51754ec88
2025-06-17 21:57:52 +00:00
Davanum Srinivas dd8cd79556 Drop usage of forked copies of goyaml.v2 and goyaml.v3
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 3827d3bc4f1c17ac816b37422dbd449c276e9ff0
2025-06-17 11:00:27 -04:00
Kubernetes Publisher d76c79a4e9 Merge pull request #132221 from dims/new-cmp-diff-impl
New implementation for `Diff` (drop in replacement for `cmp.Diff`)

Kubernetes-commit: 3e39d1074fc717a883aaf57b966dd7a06dfca2ec
2025-06-17 03:53:42 +00:00
Kubernetes Publisher 3b43a1a7f8 Merge pull request #132244 from hakuna-matatah/1.33-regression-w-test-validation
1.33 regression - Consistent paginated lists serve from cache

Kubernetes-commit: 74210dd399c14582754e933de83a9e44b1d69c69
2025-06-16 23:44:47 +00:00
Lukasz Szaszkiewicz 8162f5ea9d apiserver/handlers/watch: stop encoding initialEventsListBlueprint (#132326)
* apiserver/handlers/get: remove constructing versionedList

* endpoints/handlers/response: rm watchListTransformer

* endpoints/handlers/watch: unwire watchListTransformer

* storage/cacher: rm documentation about caching the serialization of bookmark events

Kubernetes-commit: fc198b92c0d5cece06fd4ecc24f6142532beff37
2025-06-16 16:02:59 +02:00
Kubernetes Publisher deb30e35ad Merge pull request #132177 from pgimalac/apiserver-avoid-template-for-dce
Remove use of html/template in apiserver to avoid disabling dead code elimination

Kubernetes-commit: 77bd3f89fbc389d5dfebbed880e08a1e4949312c
2025-06-13 18:09:22 +00:00
Marek Siarkowicz 9b695a5efa Validate requests sent to etcd in TestList "test List with limit" scenario
This adds a regression test to detect fallback to etcd as discovered in https://github.com/kubernetes/kubernetes/issues/132132.

Kubernetes-commit: 4cb6d3d77617f141fefd4994910380c095dac1ad
2025-06-12 11:33:02 +02:00
Davanum Srinivas 6db9ead72a Add a replacement for cmp.Diff using json+go-difflib
Co-authored-by: Jordan Liggitt <jordan@liggitt.net>
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 03afe6471bdbf6462b7035fdaae5aa0dd9545396
2025-06-10 23:08:41 -04:00
Pierre Gimalac 1f47d05362 chore(apiserver): avoid using html template which disables dce
Kubernetes-commit: 40c718864b7231addeeb1a336a88f8a71a024539
2025-06-08 01:03:42 +02:00
Kubernetes Publisher 081f25136a Merge pull request #132165 from gavinkflam/130690/fix-admission-control-response-codes
bug: Fix misleading response codes in admission control metrics

Kubernetes-commit: 060ed4e99aaa30ad65600427ec1b9ee4fa64d3d1
2025-06-13 00:12:29 +00:00
Kubernetes Publisher 89f1e0c9f7 Merge pull request #132269 from dims/update-to-latest-github.com/modern-go/reflect2
Update to latest github.com/modern-go/reflect2

Kubernetes-commit: d55b119d34883bbad2a3436dcb6c62339d963031
2025-06-12 20:23:25 +00:00
Jordan Liggitt 0e38ecf83f Expand webhook test to check rejection metrics
Kubernetes-commit: 9251b2a7a58e8232244508081bd3a6c06d5781d0
2025-06-12 13:41:30 -04:00
Davanum Srinivas a2f3ed4733 Update to latest github.com/modern-go/reflect2
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 3908550c0dc189cfa9de38a84bee508fa0659463
2025-06-12 11:20:39 -04:00
Gavin Lam e4c9db3825 bug: Fix misleading response codes in admission control metrics
Signed-off-by: Gavin Lam <gavin.oss@tutamail.com>

Kubernetes-commit: 601065a7c6b5445ed46fbcca8a888c98be318fec
2025-06-12 09:44:41 -04:00
Kubernetes Publisher 6bf08e15b6 Merge pull request #132135 from jpbetz/options-to-slice-master
Simplify options in declarative validation

Kubernetes-commit: c06a41b31667e83cbdd19734d5716775f9c3937e
2025-06-12 08:22:45 +00:00
Kubernetes Publisher d9e31a74fe Merge pull request #132238 from aramase/aramase/c/cleanup_deprecated_enc_config_metrics
Remove deprecated encryption config controller metrics

Kubernetes-commit: c7b3f5d0b9d3b557d333a7244ed228c8067cf2e6
2025-06-12 04:12:50 +00:00
Kubernetes Publisher 65bd2fadfd Merge pull request #132239 from dims/update-to-etcd-3.6.1-in-vendor
Update to etcd v3.6.1 in vendor/

Kubernetes-commit: 568f7300c9811f5c07a5b2a786fb1d043306c35c
2025-06-12 00:12:42 +00:00
Davanum Srinivas f294209f1e Update to etcd v3.6.1 in vendor/
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 5a2844a766bb0113b969b4519350c962d8dd8428
2025-06-11 16:46:03 -04:00
Anish Ramasekar dc0ee6eda8 Remove deprecated encryption config controller metrics
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: dc3836c49ed6683ebf6c13711a135752a0e611b0
2025-06-11 13:27:34 -07:00
Kubernetes Publisher 8aa4209508 Merge pull request #131916 from aramase/aramase/f/authn_config_ga
Mark StructuredAuthenticationConfiguration feature gate as GA

Kubernetes-commit: 5596cc64c2a1b6a2d0b44a9b7d1d5c5be9b9c8c9
2025-06-11 00:21:58 +00:00
Harish Kuna 80cef81846 Fix -Consistent paginated lists serve from cache
Kubernetes-commit: 2004ee50f5da851b08cca95b4a9c12c84794559d
2025-06-05 20:38:07 +00:00
googs1025 c5cc58d0de fix: data race for patchResource func
Signed-off-by: googs1025 <googs1025@gmail.com>

Kubernetes-commit: 2fd93c0898856ba764adef8efcaace9ff0c49f23
2025-06-01 13:44:23 +08:00
Anish Ramasekar f8343759d9 Update tests for StructuredAuthenticationConfiguration feature gate GA
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 512f000d0f321c568b76b175bc4a44ea9dd73cf3
2025-05-22 17:00:24 -07:00
Anish Ramasekar 6b794dbd82 Set StructuredAuthenticationConfiguration feature gate to GA in v1.34
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: efebfe9e3a5aba82c1c3b23711cc24d8fe457d78
2025-05-13 16:06:37 -07:00
Kubernetes Publisher 8a7311dce3 Merge pull request #132209 from dims/update-github.com/spf13/cobra-v1.9.1eksctl
update github.com/spf13/cobra v1.9.1

Kubernetes-commit: dc19f0b6b9cd14ece6b1929cb4e7ea2c9d322b95
2025-06-10 20:14:05 +00:00
Kubernetes Publisher d9b6bd1d4c Merge pull request #131654 from vinayakankugoyal/anonGA
KEP-4633: Graduate to Stable.

Kubernetes-commit: fa8c5acf5e42891d543c2933e8f3149798ca76be
2025-06-10 20:14:04 +00:00
Davanum Srinivas ed9f8fce83 update github.com/spf13/cobra v1.9.1
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 449320a54a2dac04f953d5f6401d875ea9b7e8de
2025-06-10 10:50:54 -04:00
Kubernetes Publisher f55aaff838 Merge pull request #132191 from AwesomePatrol/pass-ctx-to-count
Add context to Count() method of storage interface

Kubernetes-commit: 78edda21a307ee193fb2ab52210638520af0a1b1
2025-06-10 12:14:05 +00:00
Vinayak Goyal eadbc19007 KEP-4633: Graduate to Stable.
Kubernetes-commit: 26b188aaa8f1f4b37263d028d8e0b95bb73540bb
2025-05-07 17:56:39 +00:00
Joe Betz 13e6a3c329 Change option to a slice
Kubernetes-commit: 501393810064f96808eba093ae5c00780020c667
2025-03-29 08:59:30 -04:00
Paco Xu 9388b76b21 add seperate health check/probe for multi etcd override servers
- grouping health checks for exclusion purposes & add exclude integration test

Signed-off-by: Paco Xu <paco.xu@daocloud.io>

Kubernetes-commit: 891e7fec6e17382389b01f590888375a29491afc
2025-01-26 15:05:59 +08:00
Aleksander Mistewicz 7cd49caffc Add context to Count()
Passing the same context will let us associate etcd traces with those
from apiserver.

Signed-off-by: Aleksander Mistewicz <amistewicz@google.com>

Kubernetes-commit: 96b39187c5fac62e462dc348ccc1e3938464d9e1
2025-01-15 11:42:29 +01:00
Kubernetes Publisher 92c5638afd Merge pull request #132103 from nojnhuh/typed-ring-buffer
Replace queue.FIFOs with k8s.io/utils/buffer.Ring

Kubernetes-commit: 5090812df4fb6cf09a9181635d90c2e154eab8cc
2025-06-06 20:28:32 +00:00
Kubernetes Publisher 2c9afbf9bc Merge pull request #131979 from serathius/watchcache-initialization-ga
Gradute ResilientWatchCacheInitialization to GA

Kubernetes-commit: 7f50c56b1aee651924f39317329d33977f3c736a
2025-06-05 12:29:35 +00:00
Jon Huhn 053a8b2a4c Update k8s.io/utils for new generic ring buffer
Kubernetes-commit: 8cdbbf5cdaef7e37cfd432e9044aa52f4d42adcd
2025-06-04 12:09:53 -05:00
Kubernetes Publisher 7d6aa8a395 Merge pull request #132082 from aramase/aramase/t/kep_3331_revocation_unit_test
KEP-3331: Add test to simulate revocation via user validation rule using unique identifier (jti)

Kubernetes-commit: caa9324842e8f96702661505226a2c97ef62742f
2025-06-04 00:09:37 +00:00
Kubernetes Publisher 02450b6f98 Merge pull request #132061 from miltosdoul/fix_typeprovider_datarace
fix: datarace in ResolverTypeProvider

Kubernetes-commit: bd87e4d2a6f91c64980e0c4b9afe1f48ca57c56f
2025-06-04 00:09:36 +00:00
Anish Ramasekar 851e917827 KEP-3331: Add test to simulate revocation via user validation rule using unique identifier (jti)
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 6a495241ef3dfb828d4ae1112123a7fc98a493b9
2025-06-03 14:40:17 -07:00
Miltiadis Doulgeris 99e4fe9de1 fix: ResolverTypeProvider data race
Kubernetes-commit: de1d6fbf2422eb1da7a1fe38e0d0d6b3bfc53fd1
2025-06-02 23:19:44 +03:00
Kubernetes Publisher b50caf5d66 Merge pull request #131798 from aramase/aramase/f/authn_metrics_beta
Promote automatic_reloads of authn config metrics to BETA

Kubernetes-commit: d70229fac7c2aec4e6a69e0783fa0860a90e1144
2025-05-28 05:54:11 +00:00
Kubernetes Publisher 73e127faf5 Merge pull request #131951 from dims/drop-usages-of-deprecated-otelgrpc-methods
Drop usages of deprecated otelgrpc methods (update to v0.60.0)

Kubernetes-commit: d9c1b4ec9b3df7f09dc23a0cc2b3daf2506d3688
2025-05-27 17:57:57 +00:00
Kubernetes Publisher 255fa69bd1 Merge pull request #131664 from jpbetz/subresources-enable-replicas
Migrate to declarative validation: ReplicationController/scale spec.replicas field

Kubernetes-commit: 144926558984ae41a7328d53bd9fc8602328f10e
2025-05-27 17:57:55 +00:00
Marek Siarkowicz 784d2892e6 Gradute ResilientWatchCacheInitialization to GA
Kubernetes-commit: 741d0e2b81a0820892db18b90f5b691a19676010
2025-05-27 13:17:32 +02:00
Kubernetes Publisher 4b98f13de7 Merge pull request #131768 from aramase/aramase/f/authz_metrics_stable
Promote automatic_reloads of authz config metrics to BETA

Kubernetes-commit: f6530285a85d6f4280711301613a7d3215a25818
2025-05-26 14:06:35 +00:00
Davanum Srinivas b887c9ebec Drop usages of deprecated otelgrpc methods
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 7c0f968ab256486b524ea37014ccf580b12c73e4
2025-05-23 19:40:36 -07:00
Kubernetes Publisher 7937de32ff Merge pull request #131845 from serathius/unify-metrics
Unify references to group resource in metrics

Kubernetes-commit: b5608aea94cfb54fea3a63e1d74235759d036c51
2025-05-22 18:06:37 +00:00
Kubernetes Publisher 08959fe41f Merge pull request #131752 from aramase/aramase/f/kep_3331_v1_api
Duplicate v1beta1 AuthenticationConfiguration to v1

Kubernetes-commit: 6da56bd4b782658a4060f65c24df5830ec01c6c1
2025-05-21 07:48:50 +00:00
Kubernetes Publisher 2055ba24cd Merge pull request #131838 from dims/bump-google.golang.org/grpc-to-google-v1.72.1
Bump google.golang.org/grpc to google v1.72.1

Kubernetes-commit: 444e2b4eb079727d7cdd81ad25041502656370b8
2025-05-20 15:59:23 +00:00
Kubernetes Publisher df39bcd7dd Merge pull request #131704 from karlkfi/karl-watch-subtests
test: Use sub-tests in watch tests

Kubernetes-commit: 4e80b05087cf26188208f1c80d133566be4eae18
2025-05-19 23:49:18 +00:00
Kubernetes Publisher 73a9ddd3c3 Merge pull request #131766 from serathius/flake-TestGetCacheBypass
Separate getList and watch errors to prevent TestGetCacheBypass flakes

Kubernetes-commit: f80233f24bcc49305aab2043b2bc6d66a606c6ab
2025-05-19 19:50:56 +00:00
Joe Betz d6a8a1af27 Clarify errors and improve tests
Kubernetes-commit: 9715c90b31ccc121ba910044c03ecb9936b1f858
2025-05-19 14:12:09 -04:00
Joe Betz 2f8595c47b Clarify group version lookup in validateDeclaratively
Kubernetes-commit: 4cac349f86b730a9e7eebffaad9dd92d77dda594
2025-05-19 11:45:59 -04:00
Joe Betz 5009571787 Add +k8s:isSubresource and +k8s:supportsSubresource tags
Kubernetes-commit: 6ca6b7bb6ab7865ace03601c5d34571196126c8b
2025-05-19 11:18:06 -04:00
Marek Siarkowicz a559cb8be1 Unify references to group resource in metrics
Skip apiserver_storage_objects as it's a stable metrics that doesn't
have "group" label. As defined in KEP-1209 adding a label is considered
a breaking change so this change should be done via deprecation and
introduction of new metric.

Tested via:
```
kubectl get --raw /metrics | grep -i apiservice | grep -v APIServiceRegistrationController | grep -v aggregator_unavailable_apiservice | python3 -c "import sys; print('\n'.join([k+' ' + v for k,v in {a.split('{')[0]:'{'+a.split('{')[1].split('}')[0]+'}' for a in sys.stdin.readlines() if '#' not in a and '_sum' not in a and '_bucket' not in a}.items()]))"
```

Before:
```
apiserver_cache_list_fetched_objects_total {index="",resource_prefix="/apiregistration.k8s.io/apiservices"}
apiserver_cache_list_returned_objects_total {resource_prefix="/apiregistration.k8s.io/apiservices"}
apiserver_cache_list_total {index="",resource_prefix="/apiregistration.k8s.io/apiservices"}
apiserver_longrunning_requests {component="apiserver",group="apiregistration.k8s.io",resource="apiservices",scope="cluster",subresource="",verb="WATCH",version="v1"}
apiserver_request_body_size_bytes_count {resource="apiservices.apiregistration.k8s.io",verb="create"}
apiserver_request_duration_seconds_count {component="apiserver",dry_run="",group="apiregistration.k8s.io",resource="apiservices",scope="resource",subresource="",verb="POST",version="v1"}
apiserver_request_sli_duration_seconds_count {component="apiserver",group="apiregistration.k8s.io",resource="apiservices",scope="resource",subresource="",verb="POST",version="v1"}
apiserver_request_total {code="201",component="apiserver",dry_run="",group="apiregistration.k8s.io",resource="apiservices",scope="resource",subresource="",verb="POST",version="v1"}
apiserver_response_sizes_count {component="apiserver",group="apiregistration.k8s.io",resource="apiservices",scope="cluster",subresource="",verb="LIST",version="v1"}
apiserver_selfrequest_total {resource="apiservices",subresource="",verb="POST"}
apiserver_storage_events_received_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_storage_list_evaluated_objects_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_storage_list_fetched_objects_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_storage_list_returned_objects_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_storage_list_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_storage_objects {resource="apiservices.apiregistration.k8s.io"}
apiserver_watch_cache_events_dispatched_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_watch_cache_events_received_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_watch_cache_initializations_total {resource="apiservices.apiregistration.k8s.io"}
apiserver_watch_cache_resource_version {resource="apiservices.apiregistration.k8s.io"}
apiserver_watch_events_sizes_count {group="apiregistration.k8s.io",kind="APIService",version="v1"}
apiserver_watch_events_total {group="apiregistration.k8s.io",kind="APIService",version="v1"}
etcd_request_duration_seconds_count {operation="listWithCount",type="/registry/apiregistration.k8s.io/apiservices/"}
etcd_requests_total {operation="listWithCount",type="/registry/apiregistration.k8s.io/apiservices/"}
watch_cache_capacity {resource="apiservices.apiregistration.k8s.io"}
```

After:
```
apiserver_cache_list_fetched_objects_total {group="apiregistration.k8s.io",index="",resource="apiservices"}
apiserver_cache_list_returned_objects_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_cache_list_total {group="apiregistration.k8s.io",index="",resource="apiservices"}
apiserver_longrunning_requests {component="apiserver",group="apiregistration.k8s.io",resource="apiservices",scope="cluster",subresource="",verb="WATCH",version="v1"}
apiserver_request_body_size_bytes_count {group="apiregistration.k8s.io",resource="apiservices",verb="create"}
apiserver_request_duration_seconds_count {component="apiserver",dry_run="",group="apiregistration.k8s.io",resource="apiservices",scope="resource",subresource="",verb="POST",version="v1"}
apiserver_request_sli_duration_seconds_count {component="apiserver",group="apiregistration.k8s.io",resource="apiservices",scope="resource",subresource="",verb="POST",version="v1"}
apiserver_request_total {code="201",component="apiserver",dry_run="",group="apiregistration.k8s.io",resource="apiservices",scope="resource",subresource="",verb="POST",version="v1"}
apiserver_response_sizes_count {component="apiserver",group="apiregistration.k8s.io",resource="apiservices",scope="cluster",subresource="",verb="WATCH",version="v1"}
apiserver_selfrequest_total {group="apiregistration.k8s.io",resource="apiservices",subresource="",verb="WATCH"}
apiserver_storage_events_received_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_storage_list_evaluated_objects_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_storage_list_fetched_objects_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_storage_list_returned_objects_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_storage_list_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_storage_objects {resource="apiservices.apiregistration.k8s.io"}
apiserver_watch_cache_events_dispatched_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_watch_cache_events_received_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_watch_cache_initializations_total {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_watch_cache_resource_version {group="apiregistration.k8s.io",resource="apiservices"}
apiserver_watch_events_sizes_count {group="apiregistration.k8s.io",resource="apiservices",version="v1"}
apiserver_watch_events_total {group="apiregistration.k8s.io",resource="apiservices",version="v1"}
etcd_bookmark_counts {group="apiregistration.k8s.io",resource="apiservices"}
etcd_request_duration_seconds_count {group="apiregistration.k8s.io",operation="listWithCount",resource="apiservices"}
etcd_requests_total {group="apiregistration.k8s.io",operation="listWithCount",resource="apiservices"}
watch_cache_capacity {group="apiregistration.k8s.io",resource="apiservices"}
```

Kubernetes-commit: f712b01ddb55f6569b930ca714499051ba8cb311
2025-05-19 11:41:09 +02:00
Davanum Srinivas 569dd0421b Bump google.golang.org/grpc v1.72.1
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 9b3830fba234bc4a4f09a1ad4417e4d18b74d6dc
2025-05-18 12:52:05 -04:00
Kubernetes Publisher 062128c843 Merge pull request #128419 from liggitt/etcd-3.6
etcd 3.6 client update

Kubernetes-commit: 09ca440a450e9103a8f835f598c09237dba6ecbb
2025-05-16 04:00:16 +00:00
Anish Ramasekar 2ce736f1ce Promote automatic_reloads of authn config metrics to BETA
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 60b5338d46027c26cd29a829bea5dbff822b3b23
2025-05-15 11:23:10 -07:00
Anish Ramasekar 12fab5de10 Promote automatic_reloads of authz config metrics to BETA
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 5314fcece315c6bab115de4ac723baa43f8d6ec0
2025-05-14 09:51:20 -07:00
Marek Siarkowicz 6764e31cac Separate getList and watch errors to prevent TestGetCacheBypass flakes
The TestGetCacheBypass requires watch cache to be initialized after
which it will want backend to return errors to check what requests are
cached and which are not.

The watch cache is marked as ready after a list succeeds, but before a watch request is executed.
If watch request fail it will immidietly flip back to unready.

Injecting error after watch cache was ready didn't guarantee that watch
request started, sometimes causing watch to be called after and fail.
This immidietly caused the watch cache to be again unready for the rest
of the test.

The fix is just to separate failure injection for List and Watch
responses.

Kubernetes-commit: 70e05132645d5c49cf0ac61a3ae8869dc60f0bde
2025-05-14 15:52:06 +02:00
Anish Ramasekar 9333a5f386 Duplicate v1beta1 AuthenticationConfiguration to v1
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: aea874e5e47c1740d12f1dbea6bba8249407c9f8
2025-05-13 15:48:04 -07:00
Karl Isenberg 80961690c3 test: Use sub-tests in watch tests
- Use sub-tests to avoid side-effects between test cases,
  cancel the context between cases, and make it easier to
  determine the failing case in the test logs.
- Use anonymous closures in benchmarks, instead of sub-tests,
  so the times still include all cases.

Kubernetes-commit: 0e5eab042c314851ae4e653f0037fe901baf94d8
2025-05-01 12:16:42 -07:00
Jordan Liggitt 067a2139bb Set non-experimental WatchProgressNotifyInterval config field
Kubernetes-commit: 6b0ebedccc19f3fbf877487cffa34c3694d0c275
2025-03-27 11:54:39 -04:00
Jordan Liggitt 057e0d26a0 bump etcd client to 3.6
hack/pin-dependency.sh go.etcd.io/etcd/api/v3 v3.6.0
hack/pin-dependency.sh go.etcd.io/etcd/client/pkg/v3 v3.6.0
hack/pin-dependency.sh go.etcd.io/etcd/client/v3 v3.6.0
hack/pin-dependency.sh go.etcd.io/etcd/pkg/v3 v3.6.0
hack/pin-dependency.sh go.etcd.io/etcd/server/v3 v3.6.0

hack/pin-dependency.sh github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0

hack/update-vendor.sh

Kubernetes-commit: cf0bbf1171e918d5d7ba1d3c83b5f347fc8333b0
2025-05-15 21:19:11 -04:00
Kubernetes Publisher e80da78881 Merge pull request #131706 from karlkfi/karl-close-watch-tests
test: Close response body in watch tests

Kubernetes-commit: 5f1a5174c508f6fad9d6c06dc8b5675ef04b9a03
2025-05-15 20:23:50 +00:00
Kubernetes Publisher 9e1714f200 Merge pull request #131725 from dims/avoid-encoding-in-log-response-object-when-we-dont-need-it
Avoid encoding in LogResponseObject when we are not going to use it

Kubernetes-commit: 832be9538ec49cb2b496612eff7c1dff68d8b6ba
2025-05-12 12:15:15 -07:00
Davanum Srinivas bc489d7b1b Update staging/src/k8s.io/apiserver/pkg/audit/request_log_test.go
Co-authored-by: Tim Allclair <timallclair@gmail.com>
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 721947a5703f2ffaafdc9bef983a6b60c6253ca8
2025-05-12 13:29:57 -04:00
Davanum Srinivas b45c4547ed review from tallclair
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 153233c677d62c0254d54c1e7013645a081ac03d
2025-05-12 12:50:38 -04:00
Davanum Srinivas bda7e28bbb Avoid encoding in LogResponseObject when we are not going to use it
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: e418ee3a92ca6c670d26f775b0f669e8a5fe233c
2025-05-12 09:29:22 -04:00
Kubernetes Publisher 9b509bf53b Merge pull request #131694 from dims/eliminate-audit-context-set-event-level
Eliminate AuditContext`s SetEventLevel

Kubernetes-commit: e94babb2aa0e54950c5b1adbadccf59d24436e56
2025-05-10 03:39:42 +00:00
Kubernetes Publisher 2ffdf9039f Merge pull request #131680 from karlkfi/karl-test-http-context
test: Pass test context to http requests

Kubernetes-commit: 43be8301ed4041e7b6d6d517badf95473696d03a
2025-05-09 15:32:55 +00:00
Kubernetes Publisher f2b80d742a Merge pull request #131676 from karlkfi/karl-ioutil
refactor: Stop using ioutil in apiserver

Kubernetes-commit: 7ce0e1ca2b176bc11855944f721a9f6cf73cae54
2025-05-09 11:32:01 +00:00
Davanum Srinivas 1ffdd2403f Eliminate AuditContext`s SetEventLevel
Signed-off-by: Davanum Srinivas <davanum@gmail.com>
Co-Authored-By: Jordan Liggitt <liggitt@google.com>

Set event level during context init

Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 960a4939f2502f2a8f2b923203e9075354e4bdc0
2025-05-09 06:57:31 -04:00
Kubernetes Publisher 2bc51918fe Merge pull request #129472 from dims/api-server-crashes-on-concurrent-map-iteration-and-write
Fix API server crash on concurrent map iteration and write

Kubernetes-commit: f603a0ada810d2671c6440b109c7fe29f67c81ff
2025-05-09 03:39:44 +00:00
Karl Isenberg 46dd96ca03 refactor: Stop using ioutil in apiserver
- The ioutl package is deprecated. Migrate to os package functions.

Kubernetes-commit: f93e4645c18c6f56bfddc158ef7b3f674b3c41dd
2025-05-08 11:28:52 -07:00
Kubernetes Publisher 1527793495 Merge pull request #131656 from karlkfi/karl-request-methods
refactor: Add request method constants

Kubernetes-commit: 0f9987650cca2f24787e4cd38d257b77aabf68db
2025-05-08 11:38:15 +00:00
Kubernetes Publisher 93c8ec73c8 Merge pull request #131616 from jpbetz/typeconverter-cleanup
Reorganize scheme type converter into apimachinery utils

Kubernetes-commit: 7cb2bd78b22c4ac8d9a401920fbcf7e2b240522d
2025-05-08 03:47:17 +00:00
Joe Betz 5f14a1e225 Reorganize scheme type converter into apimachinery utils
This removes a dependency from generated applyconfigurations to a testing
package. To do this, the type converter in the testing package has been
moved out to the apimachinery package and the utilities the converter
depend on have been reorganized.

Kubernetes-commit: 4821604f83a6f4764497879b666087ba7cb05060
2025-05-07 10:07:55 -04:00
Kubernetes Publisher 007464aa0f Merge pull request #131575 from ali-a-a/deflake-watch-not-hanging-on-startup-failure
pkg/storage/cacher/cacher_whitebox_test: deflake TestWatchNotHangingOnStartupFailure when ResilientWatchCacheInitialization is on

Kubernetes-commit: 417662ca933ca58437d3d2d902f2cfdc4294ac45
2025-05-06 23:39:17 +00:00
Kubernetes Publisher 97250949cd Merge pull request #131596 from jpbetz/cel-reflect-extra-tests
Expand CEL UnstructuredToVal and TypedToVal has() tests

Kubernetes-commit: 0da4ada321999ff1d782d77a3101157629b493cb
2025-05-06 19:48:48 +00:00
Kubernetes Publisher 724cb010da Merge pull request #131560 from jpbetz/validation-gen-subresource-simplification
Declarative validation: Simplify handling of subresources

Kubernetes-commit: c6739dd54d56549a8460737f66b1c6aa0fa697bf
2025-05-06 19:48:43 +00:00
Kubernetes Publisher 11e35f7e67 Merge pull request #131627 from dims/mutating-webhook-error
[Attempt #2] apiserver: Treat error decoding a mutating webhook patch as error calling the webhook

Kubernetes-commit: 3aa66a2ba01529a3c5754cab222693d80241d5ac
2025-05-06 07:45:13 -07:00
Davanum Srinivas 71463feb30 Adding test case for the webhook behavior change
Authored-by: Jordan Liggitt <liggitt@google.com>
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 3be3051fb77be1a1ad250c04a68efe8c6bf61940
2025-05-06 09:23:46 -04:00
Joe Betz 62514be921 Apply feedback
Kubernetes-commit: 10c5bdd6a1a98222940c72e9c13ed0ccbb65322e
2025-05-05 15:03:11 -04:00
Joe Betz 7e75755a5a Expand has() tests for omitempty and omitzero
Kubernetes-commit: 47fddb08f75939ce6e40d0217ea1f4d5072b1ed0
2025-05-02 20:52:08 -04:00
Ali Abbasi Alaei b6cfd80a0d pkg/storage/cacher/cacher_whitebox_test: deflake TestWatchNotHangingOnStartupFailure when ResilientWatchCacheInitialization is on
Kubernetes-commit: 2073ba2372b2cd1c53b327a50656a2cdd13decba
2025-05-01 16:55:49 -04:00
Karl Isenberg 32e916b79d test: Close response body in watch tests
- Close response body after http calls in the watch tests
- Add apitesting utility methods for closing and testing errors
  from response body and websocket.
- Validate read after close errors.

Kubernetes-commit: 9d963298a3b7b828f01a9b02af57863a7480eb0b
2025-04-29 17:32:48 -07:00
Karl Isenberg 1552f6cea3 test: pass the test context to http requests
This handles canceling the request after the test completes, cleaning
up resources on the client and server.

Kubernetes-commit: 080d6f9ead740ec1358e320e388f79cc4de97697
2025-04-29 17:05:13 -07:00
Karl Isenberg 61451f57b1 refactor: Add request method constants
This avoids linter errors from using inline strings as http request
methods.

Kubernetes-commit: e81887276c65acccc5486f2ff69d8bc54ac3c6ca
2025-04-29 16:45:30 -07:00
Joe Betz 0de899d1ed Add subresource to operation, do not special case subresources in validation-gen
Kubernetes-commit: 2119555e02b357db58e460cd8f38bf187b5f837b
2025-03-26 21:26:14 -04:00
Joe Betz fd89bf895d Add subresource mapping support to ValidateDeclaratively and introduce configs
# Conflicts:
#	staging/src/k8s.io/apiserver/pkg/registry/rest/validate_test.go

Kubernetes-commit: 48e1079cf0301f93ae669695c147e1365f9a7dd0
2025-03-19 22:50:51 -04:00
Paco Xu 2bff744497 add etcd server overrides to etcd probe factory for healthz and readyz
Kubernetes-commit: 8bc7e6c10e0a4dceb738318a3a34f6af94e06158
2024-12-31 16:29:35 +08:00
Kevin Conner 8cf49c7c86 Simplify span handling
Co-authored-by: David Ashpole <dashpole@google.com>

Kubernetes-commit: 9f50740b7bcb41c81ec5b7939581b2bb10d641e4
2024-11-12 12:43:53 -08:00
Kevin Conner 8285a27f7b Trace across start handler invocations, nesting spans
Signed-off-by: Kevin Conner <kev.conner@gmail.com>

Kubernetes-commit: c64b6f80eb7bcf9f0d24e7f56fcced807a263b98
2024-11-11 20:55:05 -07:00
Davanum Srinivas 0f91510ab0 Treat error decoding a mutating webhook patch as error calling the webhook
Co-Authored-By: Matthew Wong <mattwon@amazon.com>
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: cf82fd7e9ceac60b7a336c90958835865c4f2f0a
2024-10-23 15:52:41 -07:00
Kubernetes Publisher 23913f0bf4 Merge pull request #131559 from jpbetz/fix-unstructured-to-val-equality
Fix UnstructuredToVal map equality to respect nil fields

Kubernetes-commit: 3f808638076e9e2be5f53a4aec9d2d5d00b997e1
2025-05-06 03:34:58 +00:00
Kubernetes Publisher eeced267ad Merge pull request #131595 from aojea/utils_fake_clock
update k8s.io/utils to bring fakeClock.Waiters()

Kubernetes-commit: e3e1f80c0110c847acf4381b1790c1c667395010
2025-05-03 03:42:20 +00:00
Kubernetes Publisher d8f5cf79ad Merge pull request #131574 from enj/enj/t/oidc_cel_unescape
jwt: support CEL expressions with escaped names

Kubernetes-commit: 43a5c18ebacf57b23f8431f270bef6c361631f20
2025-05-03 03:42:17 +00:00
Kubernetes Publisher 3dae57efb5 Merge pull request #130989 from liggitt/creationTimestamp-omitzero
Omit null creationTimestamp

Kubernetes-commit: 01899a7c86337b05a16a4155c9351cf947beaee9
2025-05-02 23:43:09 +00:00
Antonio Ojea 73b2a2235b update k8s.io/utils to bring fakeClock.Waiters()
Change-Id: I7e25338df225c2c27457403fbc2f158d08638f87

Kubernetes-commit: c2c003a71fc52fa79c2fff0109afad58573d0216
2025-05-02 11:21:11 +00:00
Monis Khan f2b320dc52 jwt: support CEL expressions with escaped names
This is purely for consistency with other uses of CEL in the
project.  Using `[` for accessing claims or user data is preferred
when names contain characters that would need to be escaped.  CEL
optionals via `?` can be used in places where `has` cannot be used,
i.e. `claims[?"kubernetes.io"]` or `user.extra[?"domain.io/foo"]`.

Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 7b50c8a510f2645219ee05da5195042c02552932
2025-05-01 13:22:52 -04:00
Joe Betz c199f9d392 Fix CEL equality bug for structs will nil field not marked as omitempty
Kubernetes-commit: 66b8a8427cf0be6f6a87ea3384e7213696bdfd4c
2025-04-30 09:16:44 -04:00
Jordan Liggitt 0d11839195 Drop null creationTimestamp from test fixtures
Kubernetes-commit: 6bb6c9934294d8265197c9dfc4c9dd3adaca147a
2025-03-24 09:37:26 -04:00
Jordan Liggitt b5e431bd82 bump cbor to add omitzero support
Kubernetes-commit: bc6051717137cef288b82305588e675de4a32c0d
2025-03-25 12:27:43 -04:00
Jordan Liggitt d22318d3d2 bump structured-merge-diff to add omitzero support
Kubernetes-commit: 06b0784062f68566daa8eed83c475b738dcf620c
2025-03-24 16:34:01 -04:00
Kubernetes Publisher 8776678b52 Merge pull request #131573 from enj/enj/t/oidc_nested_cel
jwt: add unit tests for using CEL with deeply nested claims

Kubernetes-commit: 03a3c0c89161935bc2338f5754ebb1104f779af1
2025-05-01 12:41:56 -07:00
Monis Khan cb5a7a865d jwt: add unit tests for using CEL with deeply nested claims
Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 5441f5fdef781298cd7d924eecd00e20e08831ce
2025-04-30 00:03:25 -04:00
Kubernetes Publisher a18370ff46 Merge pull request #131536 from enj/enj/r/oidc_cel_activation
jwt: refactor CEL eval to drop `unstructured` and `map[string]any`

Kubernetes-commit: 1b517e55013479f674f3fac7196f3af307d6e23f
2025-04-30 03:30:37 +00:00
Kubernetes Publisher de95d3511f Merge pull request #131460 from jpbetz/cel-value-reflect
Add lazy reflective CEL object wrapper

Kubernetes-commit: 25f124886c37d07d5550d94306dcd3e677f97b10
2025-04-29 13:43:54 -07:00
Joe Betz 847f0cb7d3 Appease linters
Kubernetes-commit: ea64418271408a6db07b3e6c30f5e42504222af5
2025-04-29 15:37:26 -04:00
Monis Khan dbbb6a075e jwt: refactor CEL eval to drop unstructured and map[string]any
This prepares us to add support for distributed claims support in
CEL expressions.

Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 43d6ea12e3f757e46e17311801a596aa5e70b06e
2025-04-28 17:19:54 -04:00
Joe Betz f52dfa0eb4 Add lazy reflective CEL object wrapper
Kubernetes-commit: 064074c07ac3c521483b592f37f2a58d4583ee39
2025-04-04 10:03:38 -04:00
Kubernetes Publisher a50d13fe86 Merge pull request #131434 from pacoxu/fsnotify
bump fsnotify v1.9.0

Kubernetes-commit: 1b509a888327c0a7bcbae21b78b1bf3c447cc666
2025-04-25 19:37:03 +00:00
Kubernetes Publisher 201e7749f2 Merge pull request #131444 from erdii/update-cel-go
chore: update github.com/google/cel-go dependency to v0.25.0

Kubernetes-commit: e9379e92db19d826d46edd502dd3a61609b32c52
2025-04-25 11:36:08 +00:00
Josh Gwosdz 3a062dc7cb chore: update github.com/google/cel-go dependency to v0.25.0
Signed-off-by: Josh Gwosdz <jgwosdz@redhat.com>

Kubernetes-commit: 7a24c4ce5d96aab2e9a33c4d62617cfb1c83c9f8
2025-04-24 14:59:35 +02:00
Kubernetes Publisher 4a106441d6 Merge pull request #131435 from wojtek-t/fix_watcher
Fix etcd3 watcher flake

Kubernetes-commit: 6e1d5e310ae1c37667a20655720b3cecc62b50ef
2025-04-24 09:18:31 -07:00
Wojciech Tyczyński fc69168d19 Fix etcd3 watcher flake
Kubernetes-commit: c3bb59d078a023153c6ea0c9a93e535b48f2557d
2025-04-24 11:32:49 +02:00
Paco Xu f541759309 bump fsnotify v1.9.0
Kubernetes-commit: a2281f97bb06233ece3f601f73a7ac1137433610
2025-04-24 17:01:00 +08:00
Kubernetes Publisher 85b0cb4ae1 Merge pull request #129515 from nojnhuh/typos
Fix comment typos

Kubernetes-commit: e0f9955130fd007b66e89594dbed3d9067a301b4
2025-04-24 07:29:44 +00:00
Kubernetes Publisher 42db0bf60a Merge pull request #131366 from xigang/resource_encoding
Fix typo in ResourceEncodingConfig comment

Kubernetes-commit: 25edc4237a346ffaf5afd4fbc1eea6e8e6321c6b
2025-04-24 03:39:17 +00:00
Kubernetes Publisher fe06bdc873 Merge pull request #131323 from karlkfi/karl-watch-test-move
chore: move watch handler tests to handlers pkg

Kubernetes-commit: 360a3bfb3c36a19066d7c93f347d6d029b9aaa16
2025-04-24 03:39:14 +00:00
Kubernetes Publisher cab2303b49 Merge pull request #131215 from tosi3k/cleanup-leader-election
Remove FlowSchemas handling non-leases-backed leader election

Kubernetes-commit: 27de9a82b7de600fa40b4b2add081cefaec6cdc0
2025-04-24 03:39:12 +00:00
Kubernetes Publisher c160237b46 Merge pull request #131162 from wojtek-t/simplify_etcd3_watcher
Simplify etcd3 watcher

Kubernetes-commit: db21f3df3f44b5b4545c8164d0d6030b01db2ed0
2025-04-24 03:39:11 +00:00
Kubernetes Publisher 492c873a80 Merge pull request #130995 from xigang/utils
bump k8s.io/utils for improvements

Kubernetes-commit: 43a7d3be12425cc80ca6ad3599809a19728c5566
2025-04-23 23:44:15 +00:00
Kubernetes Publisher 6da3566330 Merge pull request #130994 from BenTheElder/host-network-no-port
Remove inaccurate doc comment from podspec hostNetwork field

Kubernetes-commit: b775f9b92f98f7b3acbb3864ed53c2f5b835e917
2025-04-23 23:44:14 +00:00
Rafael Franzke 213eed6ea1 Allow disabling caching for webhook authorizers when using `apiserver.config.k8s.io/v1{alpha1,beta1}.AuthorizationConfiguration` (#129237)
* Introduce new boolean `cache{Una,A}uthorizedRequests` field

* Run `hack/update-codegen.sh`

* Respect legacy flags values for caching

With the legacy `--authorization-webhook-cache-{un}authorized-ttl`
flags, caching was disabled when the TTL was set to `0`, so let's
continue doing so when building the authz configuration struct.

* Pass TTL=0 to webhook authz plugin when cache disabled

Kubernetes-commit: fa8e37f7805d608c121f07da5259d3086436d397
2025-04-23 22:30:52 +02:00
Kubernetes Publisher 82f6fe39b0 Merge pull request #127126 from mengqiy/patch-2
Correct etcd override flag help text

Kubernetes-commit: f16176317310b5b920236feb9fd4cb7c6b7df6a2
2025-04-23 23:44:10 +00:00
Kubernetes Publisher 8181c85b8b Merge pull request #131359 from deads2k/disable
Stop exposing list-via-watch from the server

Kubernetes-commit: 66931f07d9c3a4b8b7aecb5649d710896001047c
2025-04-18 07:55:08 -07:00
xigang 056896e970 Fix typo in ResourceEncodingConfig comment
Signed-off-by: xigang <wangxigang2014@gmail.com>

Kubernetes-commit: f844abfc6f950fd90a3b66355641ec53879d79a2
2025-04-18 15:55:38 +08:00
David Eads f5da7d2a32 Stop exposing list-via-watch from the server
With StreamingCollectionEncodingToJSON and
StreamingCollectionEncodingToProtobuf, the WatchList must re-justify its
necessity.  To prevent an ecosystem from building around a feature that
may not be promoted, we will stop serving list-via-watch until
performance numbers can justify its inclusion.

This also stops the kube-controller-manager from using the
list-via-watch by default.  The fallback is a regular list, so during
the skew during an upgrade the "right" thing will happen and the new
StreamingCollectionEncoding will be used.

Kubernetes-commit: 660df229bf3929741cf31659187060d0c651dcf9
2025-04-17 16:34:46 -04:00
Karl Isenberg 4c858d18eb chore: move watch handler tests to handlers pkg
- Move the watch handler unit tests to the same package as the
  WatchServer implementation.
  k8s.io/apiserver/pkg/endpoints -> k8s.io/apiserver/endpoints/handlers
- Copy over minimal scheme and codec test setup
- Refactor the tests to use testify assert and require

This unblocks making WatchServer private, if we decide to do that.

Kubernetes-commit: 7fcc1bcf1d1fdb2da6ea1c5b49798a7c7eeb6e6d
2025-04-15 16:34:28 -07:00
Kubernetes Publisher 03ddc411f7 Merge pull request #131196 from siyuanfoundation/forward-api
bug fix: fix version order in emulation forward compatibility.

Kubernetes-commit: 640489ae0cefea2358b4d248aaae9b3b2128cf7d
2025-04-08 22:46:41 +00:00
Kubernetes Publisher 2a113dbe6b Merge pull request #131204 from dims/move-to-released-version-of-prometheus/client_golang-v1.22.0-from-rc.0
Move to released version of prometheus/client_golang v1.22.0 from rc.0

Kubernetes-commit: 92af6ab6926f192a3d4543a1d6fa39f20edad3ea
2025-04-08 22:46:39 +00:00
Davanum Srinivas e87c9dbf8f Move to released version of prometheus/client_golang v1.22.0 from rc.0
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 2ef4a8426c2c1b6e3495de08c4686382a752f8f7
2025-04-08 08:35:18 -04:00
Antoni Zawodny cd728bc91b Remove FlowSchemas handling non-leases-backed leader election
Kubernetes-commit: 2800c16c8b97991b228c00b86934daa134f08add
2025-04-08 13:57:46 +02:00
Siyuan Zhang 5a4ec0a797 bug fix: fix version order in emulation forward compatibility.
Signed-off-by: Siyuan Zhang <sizhang@google.com>

Kubernetes-commit: b1a9cc347311012bade7230b55ab95229a1e22c9
2025-04-07 18:38:43 -07:00
Kubernetes Publisher d4f2fc56b5 Merge pull request #131020 from wojtek-t/fix_asynchronous_error
Fix race for sending errors in watch

Kubernetes-commit: b4d139094698687043b36f1c378dfeb1b654198a
2025-04-02 22:40:48 +00:00
Kubernetes Publisher beaef1d3ec Merge pull request #131103 from ahrtr/etcd_sdk_20250328
Bump etcd 3.5.21 sdk

Kubernetes-commit: f4d1686120d2367dd4c00df53e93dad51c414435
2025-04-01 10:59:01 +00:00
Jordan Liggitt 1776f0c3f2 Parallelize cacher list tests
Kubernetes-commit: eca90dab3f553c5794e780c67e17ae75d9acb65b
2025-03-31 15:17:04 -04:00
Marek Siarkowicz e228aeaf39 Don't start etcd for skipped test
Kubernetes-commit: 1a15d582ae5fc84177f305d93ff473cca5de6f93
2025-03-31 21:06:54 +02:00
Marek Siarkowicz 29a5d82129 Stop cacher in TestWatchStreamSeparation to speed up shutdown
Kubernetes-commit: 75186095c58630fde0b3f89892c69c8ef91fffab
2025-03-31 21:28:21 +02:00
Marek Siarkowicz 30b60eb0a6 Fix flake, non-consistent list doesn't give any guarantees about staleness
Kubernetes-commit: 38d5cb368dd95b7f185dbba97fc3e193f48a83f2
2025-03-31 08:10:42 +02:00
Marek Siarkowicz aed144f141 Ensure that cacher is terminated in TestGetListRecursivePrefix
Kubernetes-commit: 9b5c4504ee49c366be3d7c806482ef4665dc5f70
2025-03-31 18:17:08 +02:00
Benjamin Wang 51e9232644 bump etcd 3.5.21 sdk
Signed-off-by: Benjamin Wang <benjamin.ahrtr@gmail.com>

Kubernetes-commit: f3b80a858225178e3f7a3ae07bd1b9894e7b3456
2025-03-28 14:30:47 +00:00
Wojciech Tyczyński 4f42467c04 Simplify etcd3 watcher
Kubernetes-commit: 6d6abaab7d1b8f288ed949a115cc769d83bf0fe2
2025-03-24 14:02:07 +01:00
Wojciech Tyczyński b9e86eb851 Fix race for sending errors in watch
Kubernetes-commit: c8c2844aaf1d04835624ff2d46417492e10dec11
2025-03-24 14:02:07 +01:00
xigang 8ff7962ff8 bump k8s.io/utils
Kubernetes-commit: fe14689f221a968806b771b226581efb834654cd
2025-03-22 10:14:01 +08:00
Benjamin Elder 0710a6fd0a make update
Kubernetes-commit: 1c3dc397ae137cc8b1d2095ea33217a239b81b55
2025-03-21 18:19:43 -07:00
Benjamin Elder 81c5d13c4d remove inaccurate hostNetwork doc comment
also remove from copies in example / test APIs

Kubernetes-commit: 8af1629f7aeeabeaec21f3fbcee5bc60d9ad2015
2025-03-21 18:19:29 -07:00
Kubernetes Publisher 26bd744afc Merge pull request #127053 from dashpole/tracing_context_propagation
APIServerTracing: Respect trace context only for privileged users

Kubernetes-commit: b2b6c4d0235085d090ca73b62a1361302db81afd
2025-03-21 05:24:21 +00:00
Kubernetes Publisher 37736ec2c1 Merge pull request #129872 from seans3/websocket-https-proxy
WebSocket HTTPS Proxy support

Kubernetes-commit: 6f13ba03dac1865174b4edf2b43f6a0f453a8ac7
2025-03-21 05:24:20 +00:00
Kubernetes Publisher b1b5987c22 Merge pull request #130937 from serathius/watchcache-unify-delegate-list
Unify should delegate list

Kubernetes-commit: 03e818744a8f92eb87155f2576dad982da7e6a43
2025-03-20 21:45:25 +00:00
Marek Siarkowicz 6811fdeb2d Unify should delegate list
Kubernetes-commit: 8fdd6fe4768d81da09f339c1dda831a05bb26f00
2025-03-20 11:08:37 +01:00
Kubernetes Publisher b1020cee14 Merge pull request #130423 from serathius/watchcache-continue
Serve LISTs with exact RV and continuations from cache

Kubernetes-commit: aa35eff1b636f587f418f9cc16a020353735d125
2025-03-20 13:45:34 +00:00
Kubernetes Publisher 3a206e1457 Merge pull request #130930 from siyuanfoundation/help
chore: update emulation version help msg.

Kubernetes-commit: b4c6895d0b0a913e3461bdc78358aa9514604b8f
2025-03-20 01:45:09 +00:00
Kubernetes Publisher a11f874f2c Merge pull request #130121 from yongruilin/featuregate-unversion-clean
[compatibility version] clean using unversioned featuregate

Kubernetes-commit: 67bdb110b44319a45188c034f817afb8ddab9cc9
2025-03-20 01:45:08 +00:00
Siyuan Zhang b94acad08e chore: update emulation version help msg.
Signed-off-by: Siyuan Zhang <sizhang@google.com>

Kubernetes-commit: 0ec6566c68c4e10b0379518bb91fdc4edfce7adc
2025-03-19 10:29:42 -07:00
yongruilin 8702b38eb1 chore: Remove unused unversioned feature gate map by consolidating feature gate files
Kubernetes-commit: 7b5cbbb9618ae369fc5d5379703b3c2bda3f9d73
2025-02-12 13:41:01 -08:00
Kubernetes Publisher 7d24a9eb1b Merge pull request #130925 from serathius/watchcache-snapshotter-interface
Create Snapshotter interface to fake the implementation

Kubernetes-commit: f9b27edf392b283ab5c4437aa2803046635927e5
2025-03-19 17:45:38 +00:00
Kubernetes Publisher 23be1a4749 Merge pull request #130924 from serathius/watchcache-delegate-precedense
Change precedence order for continue and legacy exact match

Kubernetes-commit: 593906d607a9a48b2e8c18c68d15fb64eb259440
2025-03-19 17:45:37 +00:00
Kubernetes Publisher dfc7cb5fe1 Merge pull request #130922 from serathius/watchcache-delegate-state
Extend shouldDelegateList testing incorportating state of cacher

Kubernetes-commit: 32260dfa7b0603816619702858ba49201e0cc3c0
2025-03-19 17:45:36 +00:00
Kubernetes Publisher a581683e3c Merge pull request #130899 from serathius/watchcache-error
Implement watchcache returning error from etcd that caused cache reinitialization

Kubernetes-commit: e5558a81c93fef5463b02ae7c2a8c0c4b15ecc3a
2025-03-19 17:45:35 +00:00
Marek Siarkowicz fa558b198a Create Snapshotter interface to fake the implementation
Kubernetes-commit: 3edeb60c089106229b582a8c6800388de433426a
2025-03-19 14:04:47 +01:00
Marek Siarkowicz 91724cfd41 Change precedence order for continue and legacy exact match
This doesn't matter for shouldDelegateList, but matters when picking
source of RV. RV from continue takes precedence.

Kubernetes-commit: 7da942ca7486310893d4f11f3af062957f953555
2025-03-19 13:58:43 +01:00
Marek Siarkowicz edd1d1f6ba Extend shouldDelegateList testing incorportating state of cacher
Kubernetes-commit: 929a9c0cad9b5c2c411a325fd4f356a2b5e01f13
2025-03-19 12:23:08 +01:00
Kubernetes Publisher 1a83f0ce07 Merge pull request #128402 from richabanker/mvp-agg-discovery
KEP 4020: Replace StorageVersionAPI with aggregated discovery to fetch served resources by a peer apiserver

Kubernetes-commit: a6227695ab10a79219c253c94e65c0ee1c4cf18d
2025-03-19 05:52:39 +00:00
Kubernetes Publisher 2566cd2659 Merge pull request #130115 from danmillwood/danmillwood-dispatcher-test-patch
Fix intermittent failure in TestDispatcher test

Kubernetes-commit: f287bc21b731ce31375a3c2348404ef8a199698e
2025-03-19 05:52:38 +00:00
Kubernetes Publisher 7db8aee1f8 Merge pull request #130906 from serathius/streaming-validation
Update kube-openapi and integrate streaming tags validation

Kubernetes-commit: 32b1819423de505da855cf7544e871a04e63d6ed
2025-03-19 05:52:36 +00:00
Marek Siarkowicz f50e0ed3bf Update kube-openapi and integrate streaming tags validation
Kubernetes-commit: 75a4d136abac241f728407515e3d0d8305594675
2025-03-18 21:26:22 +01:00
Kubernetes Publisher 98865c84e6 Merge pull request #130863 from serathius/watchcache-negative-RV-consistent
Extend tests for negative RV with consistent reads

Kubernetes-commit: 4b848a555f3626f62dce39fda5c303e60cf98121
2025-03-18 17:45:02 +00:00
Kubernetes Publisher 0f51ac53c8 Merge pull request #130560 from stlaz/remote-uid-config-beta
RemoteRequestHeaderUID: bump to beta, enabled by default

Kubernetes-commit: 8312d8e85eb7e9590a0bf2e31dd6295b0486ac0c
2025-03-18 17:45:01 +00:00
Marek Siarkowicz 50f48ce4b3 Implement watchcache returning error from etcd that caused cache reinitialization
Kubernetes-commit: c09d87f79c90a5ebb0ef6a99abd13dec82b497e3
2025-03-18 16:48:33 +01:00
Kubernetes Publisher 52e218fc61 Merge pull request #130873 from serathius/watchcache-consistency-typo
Panic on failed consistency check and fix typo in logs

Kubernetes-commit: bce5886c975c933c02583213f72ea9a27c9e2296
2025-03-18 13:45:26 +00:00
Kubernetes Publisher 3482808f92 Merge pull request #130866 from serathius/watchcache-delegate-helper
Extract delegator.Helper interface to allow making delegate decision based on cache state

Kubernetes-commit: fd347070332137fa291e70f594667384d4505982
2025-03-18 13:45:25 +00:00
Kubernetes Publisher aac1558d35 Merge pull request #130875 from aramase/aramase/f/fix_email_verified_godoc
Fix godoc for `claims.email_verified` usage in claim validation rules

Kubernetes-commit: c608791a110a96b15a45c17d22f6ed29b01d7e9b
2025-03-18 05:53:12 +00:00
Anish Ramasekar 3a95207b18 Add unit test to validate email_verified in claim validation rules
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: af291a44c3a2e31ef58851d27aaa70e9a02fedaa
2025-03-17 15:38:35 -07:00
Anish Ramasekar 67dfc24d13 fix godoc for email_verified requirement when username contains claims.email
Using 'claims.?email_verified.orValue(true) == true' in the example
validation rule. By explicitly comparing the value to true, we let type-checking see the result
will be a boolean, and to make sure a non-boolean email_verified claim will be caught at runtime.

Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 916c7867f7ea766824728851a25b01ebbc600491
2025-03-17 15:18:05 -07:00
Marek Siarkowicz 4da0062093 Fix missing recursive in consistency check, enable panic on failed check and fix typo in logs
Kubernetes-commit: 4f1912abf2d139c57036c5333f62bd5bddd289fe
2025-03-17 19:24:16 +01:00
Kubernetes Publisher d750e2f2bf Merge pull request #130595 from tkashem/omit-admission
KEP-3926: skip admission validation for unsafe delete

Kubernetes-commit: 61529d54f22802523e28fd7b6b5d4483d381bfad
2025-03-17 17:56:55 +00:00
Kubernetes Publisher ba3fbbb53e Merge pull request #130688 from serathius/watchcache-validate
Use ValidateListOptions in watch cache

Kubernetes-commit: abaa73bb76855de032920831112409920ceda408
2025-03-17 17:56:53 +00:00
Kubernetes Publisher fa03ee3ea0 Merge pull request #130347 from Jefftree/remove-v2beta1-agg-discovery
Add a deprecated feature gate to stop serving apidiscovery.k8s.io/v2beta1

Kubernetes-commit: 8906223b000d94ad3947c1582c5ba452c6766e6c
2025-03-17 17:56:52 +00:00
Kubernetes Publisher 544ce3df68 Merge pull request #130741 from googs1025/fix/data_race
flake: fix data race for TestApfWatchHandlePanic unit test

Kubernetes-commit: fd0a3484176f8f29f038b4b28132dd3c7583eb00
2025-03-17 17:56:48 +00:00
Marek Siarkowicz cc5ef43352 Extract delegator.Helper interface to allow making delegate decision based on cache state
Kubernetes-commit: 984b475e74904dd61c10b23472798a21496edc8f
2025-03-17 15:46:02 +01:00
Kubernetes Publisher 5df5358ff9 Merge pull request #130811 from serathius/watchcache-test-negative-rv
Add test cases for negative resource version in TestList

Kubernetes-commit: 5a6ace2aa0342eb3103351bb7681066e0b4225af
2025-03-17 13:45:58 +00:00
Kubernetes Publisher 2265efa65a Merge pull request #130815 from serathius/watchcache-simplify-bypass-test
Simplify bypass test by just testing shouldDelegateList function

Kubernetes-commit: e2a77c2a0527da148dacd5febf15dab62d5e63cb
2025-03-17 13:45:57 +00:00
Kubernetes Publisher 8aa636f1b5 Merge pull request #130813 from serathius/watchcache-consistent-list-flake
Fix flaky RunTestConsistentList

Kubernetes-commit: d2ef120924eb289997a0755a47d1a516a42f5b8f
2025-03-17 13:45:56 +00:00
Marek Siarkowicz c59961a007 Test continue with negative RV for reading consistent RV
Kubernetes-commit: 6d21d8f2376b6e25064b10148b4f416e8d89a508
2025-03-17 12:10:54 +01:00
Kubernetes Publisher 05ed2e066d Merge pull request #130437 from srivastav-abhishek/fix-unsafe-conversions-test
Removed parallel execution for test/subtest where AllocsPerRun is used

Kubernetes-commit: e7eb076349dbb1343c71da8d31ea208457e6684d
2025-03-14 17:53:30 +00:00
Kubernetes Publisher b6404642e4 Merge pull request #130588 from serathius/watchcache-test-recursive
Test recursive in TestGetListCacheBypass and separate overrides

Kubernetes-commit: 611abd3bcdeb8ee513ab7814c4ac251575e48cbd
2025-03-14 13:46:25 +00:00
Marek Siarkowicz 3a2e1b53e5 Simplify bypass test by just testing shouldDelegateList function
Kubernetes-commit: d263344a9b400890409166c5823598a92bbb79fb
2025-03-14 14:26:44 +01:00
Marek Siarkowicz 6ace22f694 Fix flaky RunTestConsistentList
Noticed that cache might not nesseserly observe the write causing test
to flake. Fixed that changing the logic to require LessOrEqual of
writeRV instead of equal to writeRV. Also added comments explaining
edge cases.

Kubernetes-commit: 86169a7a1e09c120cadafc0213afbf9630f0d8af
2025-03-14 13:45:55 +01:00
Marek Siarkowicz 6f6da8e97b Add test cases for negative resource version in TestList
Kubernetes-commit: c4d77a07993302057441a886125c1c887e7869f1
2025-03-14 12:22:17 +01:00
Marek Siarkowicz a67992576e Test bypass for negative RV
Kubernetes-commit: 58d9b5c7b6467bb2cb14d49247cdada02e8f4a83
2025-03-14 14:34:43 +01:00
Kubernetes Publisher 84cc815637 Merge pull request #130354 from siyuanfoundation/forward-api
KEP-4330: add forward compatibility for compatibility mode

Kubernetes-commit: 8b08487283d563efa0bc849ac3a3701463bc49bd
2025-03-14 05:45:39 +00:00
Kubernetes Publisher 70820c9c98 Merge pull request #130019 from yongruilin/version-intro
KEP-4330: extend version information with more detailed version fields

Kubernetes-commit: 23d63770284cb30a2eb90b79ace0f1c7e32fb16f
2025-03-14 01:45:55 +00:00
Kubernetes Publisher a91b0013af Merge pull request #130788 from jpbetz/fix-subresources
Fix subresource parsing for declarative validation

Kubernetes-commit: dabb4e2445c0d9f8ccf9e8c596060c62307b80ef
2025-03-14 01:45:53 +00:00
Kubernetes Publisher 543428d1c4 Merge pull request #130775 from serathius/watchcache-consistent-read
Fix detecting consistent read when watchcache starts handling continue

Kubernetes-commit: 54e7d2760d1c941c8db50d059dbbe0cfb54a909a
2025-03-14 01:45:52 +00:00
Kubernetes Publisher d88392f1f6 Merge pull request #130754 from aaron-prindle/validation-gen-add-metric-and-runtime-verification-review-comments-upstream
[Declarative Validation] chore: change Info->Error log level related to declarative validation runtime tests and refactor panic wrapper names

Kubernetes-commit: 020c4b7c655f63cd0ab1b8466492da528961f930
2025-03-13 21:53:43 +00:00
Kubernetes Publisher c31bd9905e Merge pull request #130751 from Jefftree/cle-promote-beta
[KEP-4355] Promote Coordinated Leader Election to Beta

Kubernetes-commit: be127ae0e27644edead7007834b5c8437fb6eb0b
2025-03-13 21:53:39 +00:00
Jefftree cba5376d4f Gate apidiscovery/v2beta1 serving with a feature gate
Kubernetes-commit: 95d3d4a22d705ef6bf2d494c065743d356914e8d
2025-03-13 17:58:06 +00:00
Kubernetes Publisher 79feac10d8 Merge pull request #130777 from serathius/watchcache-consistency-test
Fix typo and pass the environment variable required to enable watchcache consistency checking in GCE tests

Kubernetes-commit: 9475c92a6efd1402f00e8b72e03473c5c93b9b78
2025-03-13 17:44:17 +00:00
Kubernetes Publisher 7b991eb8ee Merge pull request #124360 from carlory/kep-3751-quota-2
Add quota support for PVC with VolumeAttributesClass

Kubernetes-commit: 68899f8e6d5861e7b6197c51b0dee9f8a486e3e0
2025-03-13 17:44:16 +00:00
Joe Betz 24d512c91f Fix subresource parsing for declarative validation
Kubernetes-commit: d9a2dee622c0d2f2079194ff9700310041c10ed2
2025-03-13 13:05:54 -04:00
Kubernetes Publisher 8e8b957b64 Merge pull request #130693 from novahe/fix/test-cases
Fix test cases that may potentially cause a panic.

Kubernetes-commit: 336a32a270997731873b6d8b616f4ae91d903fcf
2025-03-13 13:52:00 +00:00
Kubernetes Publisher 6056c27674 Merge pull request #130752 from serathius/watchcache-simplify-delagate
Simplify shouldDelegateList

Kubernetes-commit: 2e9bb32ee8defbd37ce9754ae8362356ce4c3796
2025-03-13 13:51:56 +00:00
Marek Siarkowicz 36c1a58d7e Fix typo and pass the environment variable required to enable watchcache consistency checking in GCE tests
Kubernetes-commit: 8b0294daed236dcaf2b2f74ad4a38405118ebbaa
2025-03-13 11:55:23 +01:00
Kubernetes Publisher ae901d5b33 Merge pull request #130648 from jpbetz/semver-tolerant
Enable Semver CEL library, add normalization support

Kubernetes-commit: 69467d354737025482a1b2a5af34e56245f1be49
2025-03-12 21:45:20 +00:00
Kubernetes Publisher 96b9726a3c Merge pull request #130705 from aaron-prindle/validation-gen-add-metric-and-runtime-verification-upstream
[Declarative Validation] feat: add declarative validation metrics and associated runtime verification tests

Kubernetes-commit: 21f7eaa8e2b9c1a70b607cc42d0f038a9efc1906
2025-03-12 21:45:18 +00:00
Aaron Prindle 4fe686a430 chore: change Info->Error log level related to declarative validation runtime tests and refactor panic wrapper names
Kubernetes-commit: 08745086e2df0cf1a91cbe5bb305c968f1d5bf2a
2025-03-12 17:40:33 +00:00
Marek Siarkowicz 3cb2448d98 Fix detecting consistent read when watchcache starts handling continue
Kubernetes-commit: 8f83f2446a5e2f11eb751fb56067c663b51cfd12
2025-03-12 18:37:10 +01:00
Marek Siarkowicz 6ed423348f Simplify shouldDelegateList
When ResourceVersionMatch is set to NotOlderThan, there is no need to handle continue or resourceVersion="".
The validation in apimachinery will not pass and return:
* "resourceVersionMatch is forbidden when continue is provided"
* "resourceVersionMatch is forbidden unless resourceVersion is provided"

Kubernetes-commit: a0cc02e264ead76dfb0ae75a505e4d2e54219def
2025-03-12 18:20:47 +01:00
Jefftree 1623f6691b Promote CLE to beta
Kubernetes-commit: 3b88db4f2350498513c994710bfba65c074eaf2e
2025-03-12 17:07:29 +00:00
Kubernetes Publisher 3832c300e8 Merge pull request #130708 from fuweid/reduce-spans-in-writer
*: reduce tracing events during streaming JSON objects

Kubernetes-commit: c28e7ffe2467efef443ff83952280368c1fd9fce
2025-03-12 06:17:50 +00:00
googs1025 7af25044da flake: fix data race for TestApfWatchHandlePanic unit test
Signed-off-by: googs1025 <googs1025@gmail.com>

Kubernetes-commit: 1a660d3d0c9e93279c8faae56a4f8e1cca0003a4
2025-03-12 10:44:49 +08:00
Kubernetes Publisher 1a6db11afb Merge pull request #129407 from serathius/streaming-proto-list-encoder
Implement streaming proto list encoder

Kubernetes-commit: 1b6e321e2311757a521615917f99dbe8e58f623c
2025-03-12 01:57:40 +00:00
Wei Fu 9ff9bbbc47 *: reduce tracing events during streaming JSON objects
If apiserver is handing heavy traffic volume, it's likely to trigger
tracing events. After streaming JSON objects, the number of tracing events
are same to object number. It's unneccessary to log each write call.
This patch is to reduce tracing events.

Signed-off-by: Wei Fu <fuweid89@gmail.com>

Kubernetes-commit: 003f2157671a694aefecd4dbb7df786e27200a18
2025-03-11 13:26:55 -04:00
Kubernetes Publisher b2bc62b37f Merge pull request #130475 from serathius/watchcache-consistency
Implement consistency checking

Kubernetes-commit: 4c311c9fcf6a67c665127d67fb30cd602ba5b88d
2025-03-11 13:44:28 +00:00
Kubernetes Publisher 67d2550df7 Merge pull request #130530 from pacoxu/v1.33-fg-cleanup
v1.33 feature gate cleanup

Kubernetes-commit: 8f97ac7fcf1a5db05e0bd418266d5759ef3b77a7
2025-03-11 09:44:27 +00:00
novahe a2c8b5531e Fix test cases that may potentially cause a panic.
Kubernetes-commit: 9e53371ddaaeab4083fde45e43c803071238e686
2025-03-11 17:41:37 +08:00
Joe Betz 408f50382f Apply feedback
Kubernetes-commit: 2d810ddfa9c8ee55ebdb001f78b832169204fc79
2025-03-10 18:56:54 -04:00
Kubernetes Publisher dccab55151 Merge pull request #130637 from serathius/watchcache-unify-validation
Unify ListOptions validation between cache and etcd

Kubernetes-commit: 9d2fc46556af162ff42a4773349d0f92f9ef8d50
2025-03-10 13:43:54 +00:00
Marek Siarkowicz 7a33f524c6 Use ValidateListOptions in watch cache
Kubernetes-commit: 9e7c080b863896ffbe9eff2a7edc63aa72ec30cf
2025-03-10 14:29:24 +01:00
Kubernetes Publisher 855c0d44c8 Merge pull request #130555 from thockin/k_k_randfill
Use randfill in k/k

Kubernetes-commit: 0f2bde7745f3b4eadcf317bc5056dfeb96859bd3
2025-03-09 13:54:52 +00:00
Joe Betz 92171c8c10 Add normalization support to CEL semver library, enable in base env
Kubernetes-commit: 41469004282b2ad9034993427ce4ec9d1c7f88bb
2025-03-07 11:10:43 -05:00
Joe Betz 782d90765e Add tolerant parse option to semver
Kubernetes-commit: c510b93d28faf8dbce5d761675de9b5d258ae485
2025-03-07 10:10:57 -05:00
Marek Siarkowicz 73e72d16c5 Unify ListOptions validation between cache and etcd
Kubernetes-commit: ccb607f06b91496d02a3b94253261e03e3280630
2025-03-07 12:26:05 +01:00
Aaron Prindle b8750e7396 feat: add declarative validation metrics and associated runtime verification tests
Kubernetes-commit: de904f8099252fd740b4d93e5a661b20aad12ef1
2025-03-06 21:33:12 +00:00
Aaron Prindle 97b6cb1aeb chore: change error_matcher.go to use test interface instead of importing testing pkg
Kubernetes-commit: cd9df2f115a95835e07cddf740861dbd8f6f3988
2025-03-11 05:24:07 +00:00
yongruilin 2b4f068bdb refactor: detach Info from apimachinery util version
- Remove `info` field from `Version` struct
- Modify `WithInfo` and `Info` methods to be deprecated
- Update version information retrieval to use base version info
- Simplify version information generation in compatibility tests
- Remove unnecessary version info passing in build and test scenarios

Kubernetes-commit: 14934b481ef6522d6c1003ded19002ea45abe5d1
2025-03-05 23:55:08 +00:00
Abu Kashem 4c88a5c5ae skip admission for unsafe delete
Kubernetes-commit: ef3cb5c5afa0c9bedf52bd6c271af05430183f97
2025-03-05 13:45:45 -05:00
Marek Siarkowicz f76f81aa6f Test recursive in TestGetListCacheBypass and separate overrides
Kubernetes-commit: 6d3bff83213b35797b23e89c90bfe939dd30c2cd
2025-03-04 20:07:41 +01:00
Tim Hockin 7ee837dd68 Vendor randfill
Kubernetes-commit: 0ce4268b1fe4f78d77249e329b0349b9d2dd2c65
2025-03-03 23:46:48 -08:00
Paco Xu d44a862c51 remove feature gate AdmissionWebhookMatchConditions that was GAed in v1.30
Kubernetes-commit: f16745437182ec50f51253cef39cd5c89041ee95
2025-03-03 10:54:46 +08:00
Paco Xu bd0e774416 remove AggregatedDiscoveryEndpoint that was GAed in v1.30
Kubernetes-commit: 8195f82fe82e7bcf36ba50ddc8b78c00fa8df2bc
2025-03-03 11:01:02 +08:00
Paco Xu 01307e4b8d remove feature gate RemainingItemCount that was GAed in v1.29
Kubernetes-commit: bb79c29dd51f46003e0d35cd3d3683a2338448b1
2025-03-03 10:45:39 +08:00
Marek Siarkowicz 23e9b2c9d4 Implement consistency checking
Kubernetes-commit: e4d73c56cd055a6e3a23068bd70c424579df40fe
2025-02-27 17:53:06 +01:00
Abhishek Kr Srivastav 1b0427a576 Removed parallel execution for test/subtest where AllocsPerRun is used
Kubernetes-commit: bed838955fa1b15169e6d1923ed4da4c09ca555a
2025-02-26 10:57:02 +05:30
Tim Hockin 9641d30242 Use randfill, do API renames
Kubernetes-commit: e54719bb6674fac228671e0786d19c2cf27b08a3
2025-02-20 09:45:22 -08:00
Kubernetes Publisher c2e94ca503 Merge pull request #130569 from dims/update-to-latest-cadvisor-v0.52.0
Update to latest cadvisor @ v0.52.1 and new opencontainer/cgroups and drops opencontainers/runc

Kubernetes-commit: 0eaee48ecb8669dc65bfdf9a3583326ab88fc39d
2025-03-08 01:52:06 +00:00
Davanum Srinivas e36558fb20 update to v1.22.0-rc.0
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 97a54dc4b04b7d2938d11c5ae9a6233348e854ef
2025-03-07 13:45:34 -05:00
Kubernetes Publisher 7377dfa3e5 Merge pull request #130417 from serathius/watchcache-compact
Separate compactWatchCache from compactStore

Kubernetes-commit: 07e65dac2d969cffdde6416927f4884005dcda43
2025-03-06 06:53:45 -08:00
Davanum Srinivas 337542d65b update to latest cadvisor @ v0.52.0
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 5ecddb65715af7e2afc4f3cbb1abe393bfb4346a
2025-03-04 14:29:08 -05:00
Marek Siarkowicz 4305a14262 Separate compactWatchCache from increaseRV
Kubernetes-commit: 15cb82b3b3fa95ba5b914a5a02ac01d42eda892f
2025-02-25 14:44:50 +01:00
Kubernetes Publisher d3e44dff6c Merge pull request #130589 from serathius/watchcache-opts
Pass storage.ListOptions to WaitUntilFreshAndList

Kubernetes-commit: c30b1eb09b6355f88ac514ec97cb7d87bdf6c2c3
2025-03-06 09:43:45 +00:00
Kubernetes Publisher c5b8df417a Merge pull request #130543 from thockin/error_matcher_and_origin
Fix up ErrorMatcher from feedback

Kubernetes-commit: 4696667025efa26a7b192b1cb5cf79cec276f2b4
2025-03-06 09:43:44 +00:00
Kubernetes Publisher 163865fa38 Merge pull request #130320 from Jefftree/relax-openapi-verify
Relax OpenAPI Verification to pass for both strict and non-strict alpha enforcement

Kubernetes-commit: 4f32f64036e43122cc34448fda8ab845932d5c6c
2025-03-05 15:49:44 -08:00
Tim Hockin 0b0eaa35e1 Fix up ErrorMatcher from feedback
a) Rename the type and drop the constructor
b) Make MatchErrors() into a Test() method

For followup:

c) Consider making ByType() assumed
d) Consider making ByField() assumed and handle nil as "don't care"
e) Consider making ByValue() assumed and handle nil as "don't care"

Kubernetes-commit: 0a9f492eedf6dd68fee12e4606d3fef4d608d88f
2025-03-03 10:23:18 -08:00
Jefftree b12d7a1290 Allow OpenAPI verification to pass both with and without strict alpha
handling

Kubernetes-commit: c597cc1f30977bcfaba5aebb2ed7983f578b1f2c
2025-02-20 15:57:37 +00:00
Kubernetes Publisher 5edb11aa43 Merge pull request #130571 from hakuna-matatah/debug-upstream
Help debug latencies in authn and authz phase of Request

Kubernetes-commit: 2b025e645975d6d51bf38c008f972c632cf49657
2025-03-05 17:47:14 +00:00
Kubernetes Publisher 821b679880 Merge pull request #130587 from serathius/watchcache-subtests
Run lists tests in subtests

Kubernetes-commit: 91d6fd3455c4a071408df20c7f48df221f2b6d30
2025-03-05 06:03:46 -08:00
Marek Siarkowicz 79ec45326d Run lists tests in subtests
Kubernetes-commit: 7a84e7630f91014fa3b632d8800e9a943003001c
2025-03-03 20:50:37 +01:00
Kubernetes Publisher 345ad05c0c Merge pull request #130549 from jpbetz/validation-gen-pr2
KEP-5073: Add declarative validation to scheme

Kubernetes-commit: 89d0b7022a81dd8b54efcc203b0cfa4502171cae
2025-03-05 01:47:23 +00:00
Joe Betz 7c0ba21066 Add declarative validation utility for use from strategies
Kubernetes-commit: ffc1b32c660e3480381f4b13d0fbaa1313cf1318
2025-03-03 19:37:11 -05:00
Kubernetes Publisher 70fe4e2735 Merge pull request #128919 from dashpole/update_otel
Update go.opentelemetry.io dependencies to v1.33.0/v0.58.0

Kubernetes-commit: eea2f78e61fe91bb8fcd3c4a357ea3a10d1389db
2025-03-02 00:23:54 +00:00
David Ashpole 9b3bebdbac update go.opentelemetry.io dependencies to v1.33.0/v0.58.0
Kubernetes-commit: 29c219dcebe30be99d6917623f8d8707a47194c1
2025-03-01 19:17:16 +00:00
Kubernetes Publisher 990bed7c73 Merge pull request #130450 from JoelSpeed/fix-contains-cidr
Fix implementation of ContainsCIDR to allow non-equal addresses

Kubernetes-commit: c9d54b92ca8d52048e1d59deae9a6c4bb5ed9db7
2025-03-01 04:18:02 +00:00
Kubernetes Publisher 6a65641968 Merge pull request #129334 from serathius/streaming-json-list-encoder
Streaming json list encoder

Kubernetes-commit: 2fc329c857035676492aa6e6a995ef31448465f0
2025-03-01 00:18:21 +00:00
Kubernetes Publisher f2b1ab6bbc Merge pull request #130474 from dims/bump-x/crypto-and-x/oauth2
Bump x/oauth2 and x/crypto

Kubernetes-commit: 01ed8ed4ff0a0cbea99370c7a268019829d19e82
2025-02-28 20:25:28 +00:00
Kubernetes Publisher 69cfb424ed Merge pull request #129688 from cpanato/update-main-go124
[go] Bump images, dependencies and versions to go 1.24.0

Kubernetes-commit: b8c95e1954ef222988c0dfe5b45d5cc96c09bcb8
2025-02-27 20:27:02 +00:00
Davanum Srinivas e1e2202f0a Bump x/oauth2 and x/crypto
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 0fede7b8a2fb4c7f120876c9ef1e826f8ef28da2
2025-02-27 10:59:37 -05:00
Kubernetes Publisher 205c0f56b5 Merge pull request #130281 from z1cheng/issue_130264
Implement chunking for gzip encoder in deferredResponseWriter

Kubernetes-commit: 25dc6c98209b50db1f0a023020003a4051b06138
2025-02-26 21:46:14 +00:00
Joel Speed 64791740ae Fix implementation of ContainsCIDR to allow non-equal addresses
Kubernetes-commit: b7c80f7f1592356e796a64958bec9a05e0fe3ba1
2025-02-26 14:24:58 +00:00
Kubernetes Publisher 82c8af5dbe Merge pull request #130443 from serathius/watchcache-limit
Remove limit support from btree store

Kubernetes-commit: 126a5824de4086d4749c7a9f178fc559c30e7564
2025-02-26 13:45:04 +00:00
Kubernetes Publisher e03f7e4910 Merge pull request #130400 from serathius/storage-current-rv
Move GetCurrentResourceVersion to storage.Interface

Kubernetes-commit: 2e073d6334f4d70a3fc8d0b0c29bc052b8c0c06e
2025-02-26 03:40:30 -08:00
Marek Siarkowicz 9e523945a2 Remove limit support from btree store
We cannot use limit as it would apply it before filtering, which is done
in cacher. Limit is not currently used, but let's remove it to be save,
until filtering is implemented in store.

Kubernetes-commit: 168c338f7cb44a08f3d1a7e1d0e72cac241e9a29
2025-02-25 19:17:30 +01:00
cpanato 585eff559f bump go.mod to set min go1.24
Signed-off-by: cpanato <ctadeu@gmail.com>

Kubernetes-commit: 88300c406b9199ed017e1bada29951fc18e66ae1
2025-02-25 13:21:52 +01:00
Harish Kuna 6faeace534 Debug latencies in request handler
Kubernetes-commit: 186e6ee2bd55da286f39eef56c83376f3f79e09d
2025-02-25 01:33:14 +00:00
Marek Siarkowicz 3ad93853da Move GetCurrentResourceVersion to storage.Interface
Kubernetes-commit: fea89f25d1bfd7290bb0165f49a6d288ab5f717a
2025-02-24 19:49:45 +01:00
Kubernetes Publisher 2ebe70a0a7 Merge pull request #130412 from serathius/watchcache-progress
Move watch progress to separate package.

Kubernetes-commit: 3d9fcb7c0107d888722dd2a08da3326bcce0723a
2025-02-26 09:45:19 +00:00
Kubernetes Publisher 0230ffb631 Merge pull request #130399 from serathius/cache-delegator
Rename CacheProxy to CacheDelegator

Kubernetes-commit: 6ff0354c15c5b8ca1db0bec3a645b49843f1c300
2025-02-26 00:16:29 -08:00
Marek Siarkowicz a4587170bd Move watch progress to separate package.
Kubernetes-commit: 740db0f01d9bceaa7bd6c0b6c5e5f96fc78c04dd
2025-02-25 13:53:17 +01:00
Marek Siarkowicz a64613cb08 Rename CacheProxy to CacheDelegator
Kubernetes-commit: 4c635ecf8250c7493481b9b8fb88e384de1b41db
2025-02-24 19:07:25 +01:00
Kubernetes Publisher ed3efcd313 Merge pull request #130060 from carlory/fix-quota-scope
Fix the `ResourceQuota` admission plugin does not respect ANY scope change

Kubernetes-commit: ffad281e3eab096f87b53ae7e533469e513db4b2
2025-02-25 17:45:59 +00:00
Kubernetes Publisher 1e7b28d116 Merge pull request #129657 from p0lyn0mial/upstream-cacher-resilient-init-back-off
storage/cacher/ready: dynamically calculate the retryAfterSeconds

Kubernetes-commit: 49f419e84ef5db6a4c5f14bab542bd53f58bf89a
2025-02-25 17:45:57 +00:00
Kubernetes Publisher fcfce5aef7 Merge pull request #130279 from serathius/watchcache-snapshot
Watchcache snapshot

Kubernetes-commit: 4b12e89d0d1b8eefbe94f615ede351ccb9501164
2025-02-25 13:45:55 +00:00
Kubernetes Publisher 4a787e9b76 Merge pull request #130344 from tosi3k/wc-upper-bound-capacity
Add watch cache capacity upper bound adjusting logic

Kubernetes-commit: c13f6b9d7604e839702b32ccaa6fa09f621de35e
2025-02-25 04:21:10 +00:00
Kubernetes Publisher 710763dd43 Merge pull request #130118 from lucasrattz/update-anp
Bump konnectivity-client to v0.31.2

Kubernetes-commit: 7c59b07533ba4bca1766c10763d8b4a6d7ad00c4
2025-02-24 16:15:46 +00:00
Kubernetes Publisher 8dccd76acd Merge pull request #130359 from my-git9/assertion1
fix wrong assertion on tests

Kubernetes-commit: 06d81cfc7a485487375814fd8d5cefdf644c2d28
2025-02-24 16:15:44 +00:00
xin.li a3d85c236b enable go-required check
Signed-off-by: xin.li <xin.li@daocloud.io>

Kubernetes-commit: d92c70b82693f3c974e63dcf7abd2d5068c0530c
2025-02-23 16:54:43 +08:00
Kubernetes Publisher 95d27c97c9 Merge pull request #130187 from mansikulkarni96/129084
fix:  Sweep and fix stat, lstat, evalsymlink usage for go1.23 on Windows

Kubernetes-commit: ef54ac803b712137871c1a1f8d635d50e69ffa6c
2025-02-23 00:07:50 +00:00
xin.li afb79f83b3 fix wrong assertion on tests
Signed-off-by: xin.li <xin.li@daocloud.io>

Kubernetes-commit: bc4ae15d77beab23f321bf6547f82c04ba27c3fa
2025-02-22 12:39:01 +08:00
Antoni Zawodny efe7a1b26d Add watch cache capacity upper bound adjusting logic
Kubernetes-commit: 2173a0fafd448e55b7738b88fbbab392125dd975
2025-02-21 15:07:01 +01:00
z1cheng c576a626de Implement chunking for gzip encoder in deferredResponseWriter
Signed-off-by: z1cheng <imchench@gmail.com>

Kubernetes-commit: 2472f4965fe2f2013b993b6b56539946a21a3740
2025-02-20 18:36:47 +08:00
Marek Siarkowicz 09c0e7d0cd Add snapshotting of watch cache behind a feature gate
Kubernetes-commit: 2de2093bcef6544ef17c84116fbd751fc429820a
2025-02-19 17:03:48 +01:00
Marek Siarkowicz d522031d97 Ensure that btree threadedStoreIndexer implements orderedLister
Kubernetes-commit: 99881453838a63730fb34b6bd02775ae08320184
2025-02-19 11:47:26 +01:00
Marek Siarkowicz f422062046 Pass storage.ListOptions to WaitUntilFreshAndList
Kubernetes-commit: e6cf9dd1663a9a54fa673873e8e97641f69bcefd
2025-02-19 11:20:04 +01:00
Lucas Rattz 4831145b6d Bump konnectivity-client to v0.31.2
Kubernetes-commit: f8728b62228faec0048e55100747d7aee92d14ca
2025-02-12 18:47:51 +00:00
Dan Millwood 10c2760fab Fix intermittent test failure
Start the informerFactory before WaitForCacheSync

Kubernetes-commit: b9cd017a2146996677aa28ec992f7ed796f88c9b
2025-02-12 16:22:47 +00:00
carlory 13a27b8dd7 Fix a bug where the `ResourceQuota` admission plugin does not respect ANY scope change when a resource is being updated. i.e. to set/unset an existing pod's `terminationGracePeriodSeconds` field.
Kubernetes-commit: eb0f003d25209c850b47a275358aea53252274b4
2025-02-10 14:30:50 +08:00
Siyuan Zhang 942f114031 Add option to explicitly enable future gv or gvr in runtime-config.
Signed-off-by: Siyuan Zhang <sizhang@google.com>

Kubernetes-commit: 3d2d8db83509eadcad0529fda1f8ef81e1682ca5
2025-02-07 16:43:58 -08:00
yongruilin b6717882da feat: extend version information with more detailed version fields
- Add new version fields to version.Info struct:
  * EmulationMajor and EmulationMinor to track emulated version
  * MinCompatibilityMajor and MinCompatibilityMinor for compatibility tracking
- Update related code to populate and use these new fields
- Improve version information documentation and OpenAPI generation
- Modify version routes and documentation to reflect new version information structure

Kubernetes-commit: a3094ccbe6f9f134da29aedf4d6d87a9a97bf463
2025-02-06 16:11:12 -08:00
Jordan Liggitt 3a5523026c Drop winsymlink go 1.23 workaround
(cherry picked from commit 3990b6324d0427eaf9ff970da2be02711567ef5f)

Kubernetes-commit: 1f642c79c3192994e76bbe8e7360fd661cd21ab4
2025-02-06 12:45:52 -05:00
Kubernetes Publisher 644f8e67b2 Merge pull request #130242 from serathius/watchcache-test
Test continuations and exact revision LISTs

Kubernetes-commit: 05b526c2d7b1ed7f2eb099dd06bc57cce828a12a
2025-02-21 20:02:43 +00:00
Marek Siarkowicz 476d81d4ba Test continuations and exact revision LISTs
Kubernetes-commit: 034285dc4774f2217ca4df4626ca44b8f5f4f261
2025-02-18 17:32:41 +01:00
Kubernetes Publisher b9bc02fc91 Merge pull request #130280 from serathius/watchcache-test-refactor
Refactor TestList to allow testing continuations and exact

Kubernetes-commit: afc57a752110ac0633ff5df896ebe02ee1b19ec5
2025-02-20 16:20:34 +00:00
Kubernetes Publisher b9f760c579 Merge pull request #130297 from 249043822/br0004
Fix non-recursive list should also read RequestWatchProgress feature when consistent list from cache is enabled

Kubernetes-commit: 49bbe194605dd7468b7db07cc803bbdcdbb8c276
2025-02-20 16:20:33 +00:00
张可10140699 9da16fa551 Fix non-recursive list should also read RequestWatchProgress feature when consistent list from cache is enabled
Kubernetes-commit: bdf2e2d0646fcb6fa56289d85222e2de0b686244
2025-02-20 08:39:36 +08:00
Kubernetes Publisher f98455cb25 Merge pull request #122646 from liggitt/deletionTimestamp
prevent deletionTimestamp from moving into the past

Kubernetes-commit: 2ca9e2d28f0146fe881c8b9007f2926b07ff403d
2025-02-20 00:05:59 +00:00
Kubernetes Publisher 8dd4460107 Merge pull request #130047 from HirazawaUi/modify-loopback-cert-valid-period
Extending loopback certificate validity in kube-apiserver

Kubernetes-commit: 77667834b072db7e26a69d78c5e9f3181e12959f
2025-02-19 16:35:01 +00:00
Marek Siarkowicz 1e6e6f0a46 Refactor TestList and validate continuations to allow testing pagination and more exact RVs in the future
Kubernetes-commit: 764e13e27aedfd9e304e6014af23f20b5619216b
2025-02-19 17:10:58 +01:00
Kubernetes Publisher b6fda29776 Merge pull request #130190 from nkeert/test-validate-deferredResponseWriter-for-multiple-writes
Add a test to validate deferredResponseWriter on multiple writes

Kubernetes-commit: 7fc8a86381d874e3bb47b6d343c20f93ace42981
2025-02-19 12:02:06 +00:00
Kubernetes Publisher 938d466e86 Merge pull request #130249 from seans3/bump-websockets-version
Update gorilla/websockets library from 1.5.0 to latest 1.5.3

Kubernetes-commit: 728dc0d8c7a4d68157b2106330b65abcd5faac87
2025-02-19 04:10:41 +00:00
Sean Sullivan ff5a72e86f Update gorilla/websockets library from 1.5.0 to latest 1.5.3
Kubernetes-commit: 3100bbab2f7f013b08910f28d8a3debc28a57ea9
2025-02-19 00:26:21 +00:00
Kubernetes Publisher 52dde339a1 Merge pull request #130126 from fuweid/fix-128314
proxy: should add PingPeriod for websocket translator

Kubernetes-commit: fc876787b81a54f144bf4750136fad229056cccc
2025-02-18 20:03:21 +00:00
Kubernetes Publisher 35a4ee03bf Merge pull request #129852 from p0lyn0mial/upstream-clock-test-cache
cacher: decrease the running time of TestConsistentReadFallback

Kubernetes-commit: e279ae43358b4a95773647ff7644e9585201208d
2025-02-17 12:01:22 +00:00
nkeert 11288ef6c1 Add a test to validate deferredResponseWriteron multiple write calls
Signed-off-by: nkeert <197718357+nkeert@users.noreply.github.com>

Kubernetes-commit: 45e2f3e438e18b74f3b7a6645ff2073862ef0e38
2025-02-15 10:23:21 +05:30
Wei Fu 2a04234bdc proxy: should add PingPeriod for websocket translator
IIUC, before using the translator handler, the ping data can be delivered from
the client to the runtime side since kube-apiserver does not parse any client
data. However, with WebSocket, the server responds with a pong to the client
without forwarding the data to the runtime side. If a proxy is present, it may
close the connection due to inactivity. SPDY's PingPeriod can help address this
issue.

Signed-off-by: Wei Fu <fuweid89@gmail.com>
Co-authored-by: Antonio Ojea <aojea@google.com>

Kubernetes-commit: dc59c0246fb407dcf035afc224f63fcf0da8244e
2025-02-12 21:39:59 -05:00
Kubernetes Publisher 08a05f9af7 Merge pull request #130113 from AwesomePatrol/129931-fix-3
Make ResourceQuota LIST requests only when Informer is not synced

Kubernetes-commit: 75909b89201386c8a555eadc79d14fb11f91747c
2025-02-12 16:03:12 +00:00
Aleksander Mistewicz 87cccf7779 Limit ResourceQuota LIST requests to times when informer is not synced
This should reduce the number of slow (100ms) LIST requests when there
are no ResourceQuota objects present in the namespace. The behavior
stays virtually the same.

Kubernetes-commit: b346ac0f8e013cedb8d91b4065d385f84e81c43e
2025-02-12 13:34:09 +01:00
Kubernetes Publisher c4717788c4 Merge pull request #129934 from serathius/graduate-btree
Graduate BtreeWatchCache feature gate to GA

Kubernetes-commit: e2b0cfa3a1fb2c425a975b8c6ba0e9509bd35452
2025-02-10 21:02:27 +00:00
HirazawaUi c533eff8e7 adjusting loopback certificate validity in kube-apiserver
Kubernetes-commit: 553e9bf84d199be3d6a3da6675671859723219f4
2025-02-08 19:43:48 +08:00
Kubernetes Publisher 4628bb89a6 Merge pull request #129416 from siyuanfoundation/refactor
KEP-4330: Refactor compatibility version code

Kubernetes-commit: e6be5f96022ea6d4a2e370a56a3d28859e1db72d
2025-02-06 21:02:03 +00:00
Kubernetes Publisher 9bb5fd51d1 Merge pull request #129929 from serathius/deprecate-separate-rpc
Flip SeparateCacheWatchRPC feature gate to false and deprecate it

Kubernetes-commit: 9a03243789677637762eb0f907e1b4e45a0136c1
2025-02-06 05:02:19 +00:00
Kubernetes Publisher 7b8dc61f0b Merge pull request #129930 from serathius/deprecate-watch-from-storage
Deprecate WatchFromStorageWithoutResourceVersion

Kubernetes-commit: 925cf7db71c5e36072f99e8b7129523f659ee3a1
2025-02-05 21:02:52 +00:00
Kubernetes Publisher 0198fdbe95 Merge pull request #129921 from srivastav-abhishek/fix-etcd-test
Additional timeout to receive all watchEvents

Kubernetes-commit: 1527a145b110f9907e3efde483d784fe362901f4
2025-02-05 21:02:51 +00:00
Stanislav Láznička a9904eef9e bump RemoteRequestHeaderUID featuregate to Beta, on by default
Kubernetes-commit: b3890d9fa0d054b9b97b3496423664c0baf1c567
2025-02-05 15:32:57 +01:00
Siyuan Zhang 5a72bc815b Add emulation forward compatibility into api enablement and RemoveDeletedKinds.
Signed-off-by: Siyuan Zhang <sizhang@google.com>

Kubernetes-commit: 819cb8fe22fe37bf691f460bc32d0f03f53cce09
2025-02-04 10:11:56 -08:00
Kubernetes Publisher 2ce508cc43 Merge pull request #129844 from cici37/bumCEL
Bump cel-go to v0.23.2

Kubernetes-commit: 28ba942659a6fb8c8b9a22234176250fe004af06
2025-02-04 04:48:31 +00:00
Cici Huang 69f7857a7a Update the env option.
Kubernetes-commit: 8a3d0d68a20958d82f119c56036750f18bc52963
2025-02-03 18:07:23 +00:00
Marek Siarkowicz c1a2d5992c Graduate BtreeWatchCache feature gate to GA
Kubernetes-commit: e0f548183c46b1a488afa788666638b99499fb3a
2025-01-31 15:33:07 +01:00
Marek Siarkowicz 0167eb5d20 Deprecate WatchFromStorageWithoutResourceVersion
Around the 1.31 release, we discovered that a change introduced in 1.27 allowead
clients to open WATCH requests directly to etcd. This had detrimental consequences,
enabling abusive clients to bypass caching and overwhelm etcd.
Unlike the API server, etcd lacks protection against such behavior.

To mitigate this, we redirected all WATCH requests to be served from the cache.
The WatchFromStorageWithoutResourceVersion feature gate was retained as an escape hatch.
However, since we have no plans to allow direct WATCH requests to etcd again,
this flag is now obsolete.

Direct WATCH requests to etcd offer no advantage, as they don't provide stronger
consistency guarantees. WATCH operations are inherently inconsistent; unlike LIST
operations, they do not confirm the resource version with a quorum. While Kubernetes
uses the WithRequireLeader option on WATCH requests to prevent maintaining connections
to isolated etcd members, the API server provides the same level of guarantee through
its health checks, which fail if it cannot connect to etcd member.  Therefore,
the WatchFromStorageWithoutResourceVersion feature gate can be deprecated and removed.

Kubernetes-commit: 065bf2004d27e5e3f1be3c0f128347d4060d8954
2025-01-31 11:49:28 +01:00
Marek Siarkowicz 66f788143e Flip SeparateCacheWatchRPC feature gate to false and deprecate it.
Watch requests to etcd are mapped to a single stream that has a limited throughput.
By opening a lot of concurrent watch requests to single resource, users
could starve other watches from getting any events.

Separating the RPC was meant to protect the watch opened by cache.
However, as we are no longer planning to allow users to open watch directly to etcd,
the flag is not needed.

Kubernetes-commit: 4a5bbc4c159ec7d185d5fc39b95c48dbf3fab7d9
2025-01-31 11:38:58 +01:00
Abhishek Kr Srivastav 55da1afddb Additional timeout to receive all watchEvents
Kubernetes-commit: f6b527cb54e37513bbe60edb52890f3de481d000
2025-01-31 10:19:19 +05:30
Kubernetes Publisher 39a73cd3bd Merge pull request #129813 from yongruilin/golangci-featuregate-add
feat: add a lint rule to prevent Add unversioned featuregate

Kubernetes-commit: 547654a8a1da26aaf75e9506a57c210711dcaec9
2025-01-30 04:37:41 +00:00
Sean Sullivan 3b2a820e45 Websocket HTTPS proxy support
Kubernetes-commit: f73945aae56b51078318199ff2f0ecae91bc489e
2025-01-29 03:56:55 +00:00
Lukasz Szaszkiewicz 23e8a6cc2f cacher: decrease the running time of TestConsistentReadFallback
Kubernetes-commit: 601c0e359dc959bbfaf42c5d8a8a0a9a2175db74
2025-01-28 13:31:43 +01:00
Kubernetes Publisher 52f2fda3f7 Merge pull request #129596 from cici37/cvTest
Add tests for CEL library with compatible version

Kubernetes-commit: 964e5e0f192724de4c2a8e28ad8b511fd71a78b8
2025-01-27 20:41:34 +00:00
Lukasz Szaszkiewicz 8a149c9296 cacher/cacher_whitebox_test: newTestCacherWithoutSyncing allow passing a clock
Kubernetes-commit: cfd6d9a2b4476d4256959dc2ebdd517f20191ea3
2025-01-27 19:51:35 +01:00
Cici Huang 7e0cbb5924 Bump cel-go to v0.23.2.
Kubernetes-commit: c1e0443232f9c6da9119ae71e8bd7d7ef8271088
2025-01-27 18:43:12 +00:00
Kubernetes Publisher 6c0d90b944 Merge pull request #129751 from pacoxu/EfficientWatchResumption
remove GAed EfficientWatchResumption

Kubernetes-commit: 3f26d005571dc5903e7cebae33ada67986bc40f3
2025-01-27 16:36:00 +00:00
Kubernetes Publisher 2911f5b534 Merge pull request #129815 from dims/linter-to-ensure-go-cmp/cmp-is-used-only-in-tests
Linter to ensure go-cmp/cmp is used ONLY in tests

Kubernetes-commit: d36322f8d76c8e2a456e381bcc6bb43e4bbe602c
2025-01-26 00:42:24 +00:00
Davanum Srinivas 0a703e3517 Linter to ensure go-cmp/cmp is used ONLY in tests
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 4e05bc20db99ff89b2d2205218d24b9935a7fdd7
2025-01-24 17:03:29 -05:00
yongruilin ee59411d80 feat: add a lint rule to prevent Add unversioned featuregate
Kubernetes-commit: 8a0937c03467d04a25e10473e9570d2c8286cf4b
2025-01-24 11:02:16 -08:00
Kubernetes Publisher f6d3d889a6 Merge pull request #129786 from JoelSpeed/drop-netip-variables
Drop declaration of IP/CDIR type CEL variables

Kubernetes-commit: 6b7b8e89caafa8291e9992912583977e2f0c1d4c
2025-01-23 10:14:59 -08:00
Joel Speed a3f27ceda4 Drop declaration of IP/CDIR type CEL variables
Kubernetes-commit: 2b24c518b04b67070c2e6bdbeb9f8ee74eb429c8
2025-01-23 15:15:42 +00:00
Kubernetes Publisher e35b5a7595 Merge pull request #129732 from dims/switch-to-v2.6.3-of-gopkg.in/go-jose/go-jose.v2
Switch to gopkg.in/go-jose/go-jose.v2 @ v2.6.3 and github.com/coreos/go-oidc @ v2.3.0

Kubernetes-commit: a444a5bfedb2807632447bfb8350bd41fd1a3d77
2025-01-23 00:36:41 +00:00
Kubernetes Publisher cf7237e44d Merge pull request #127709 from pohly/log-client-go-rest
client-go/rest: finish context support

Kubernetes-commit: 427cd18f726be3e3c4f657258dc17a97beca92d5
2025-01-22 20:59:02 +00:00
Davanum Srinivas 736476d22b update github.com/coreos/go-oidc to v2.3.0
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: faf30b30679e51f45ed70c714a0e9937d226329a
2025-01-22 13:41:04 -05:00
Paco Xu a2ab8dc0e3 remove GAed EfficientWatchResumption since v1.24
Kubernetes-commit: 69964319d1466d39a190a634a78440247ef83316
2025-01-22 16:50:26 +08:00
Davanum Srinivas 6e7ca2c0dc Switch to gopkg.in/go-jose/go-jose.v2 @ v2.6.3
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: c9e81cd84ca12d0a60c8e11eb318c13f657217c1
2025-01-21 09:21:27 -05:00
Kubernetes Publisher 6885e995e1 Merge pull request #129538 from hzxuzhonghu/http2-clean
Cleanup: only initiate http2 server options when http2 is not disabled

Kubernetes-commit: 2c9153576ec0eef9dfb4acac591874a71ff72cbb
2025-01-20 08:35:51 +00:00
Kubernetes Publisher 0e1bb963f7 Merge pull request #129679 from pacoxu/remove-APIListChunking
remove APIListChunking which was GAed since v1.29

Kubernetes-commit: bd55d18c75f4648b934ad0b548c017c523dd705b
2025-01-17 12:35:40 +00:00
Paco Xu b66aedfe5c remove APIListChunking which was GAed since v1.29
Kubernetes-commit: 8d7aed698b772d38087103f385569ab769647923
2025-01-17 15:56:19 +08:00
Kubernetes Publisher 626adbf67c Merge pull request #129303 from sttts/sttts-vap-jsonpatch-typeresolver-tests
apiserver/admission/cel: add unit tests for JSONPatch expressions

Kubernetes-commit: a935bb769eef444f7fe087b62ee04d5a7ef44f6a
2025-01-16 20:42:22 +00:00
Kubernetes Publisher ae92d91104 Merge pull request #129628 from 249043822/br004
remove duplicate getAttrsFunc calls to reduce temporary memory allocations

Kubernetes-commit: 63cb5837ddf5a9fdc542f6f26e84a35d19caa0ec
2025-01-16 08:35:25 +00:00
Kubernetes Publisher 2387b5d4a9 Merge pull request #129633 from skitt/revert-go-difflib-go-spew
Revert to go-difflib and go-spew releases

Kubernetes-commit: 6d570c923f66a1f214d0c3ba3eddd9a0cd0fae68
2025-01-15 20:42:29 +00:00
Kubernetes Publisher 2c1a1fa4ee Merge pull request #129547 from serathius/watchcache-bypass-test-valid
Only test requests that pass validation

Kubernetes-commit: ec2e0de35a298363872897e5904501b029817af3
2025-01-15 16:34:31 +00:00
Stephen Kitt 695a3a4c4e Revert to go-difflib and go-spew releases
The last dependency pulling in the tips of go-difflib and go-spew has
reverted to the last release of both projects, so k/k can revert to
the releases too. As can be seen from the contents of vendor, this
doesn't result in any actual change in the code.

Signed-off-by: Stephen Kitt <skitt@redhat.com>

Kubernetes-commit: 3986472b3c7202716f92e586ccfaa4b4fe573dc5
2025-01-15 09:07:27 +01:00
张可10140699 e802cf6daf remove duplicate getAttrsFunc calls to reduce temporary memory allocations
Kubernetes-commit: 479ff5a02b026caec40e8262785e8ffffb42085a
2025-01-15 10:15:02 +08:00
Kubernetes Publisher cd99eadfc3 Merge pull request #129622 from dims/update-to-latest-kustomize-v5.6.0
Update to latest kustomize v5.6.0 to drop `github.com/asaskevich/govalidator`

Kubernetes-commit: 42811635adc4840a6769d7a9f7fd47be8df99c43
2025-01-15 00:35:45 +00:00
Kubernetes Publisher 37d4665f53 Merge pull request #127375 from omerap12/issue_126311
Add test for CEL reserved symbols without double underscore

Kubernetes-commit: 165da9ad0fc7e895a9b72a463062cbf754f89f2b
2025-01-14 21:26:46 +00:00
Davanum Srinivas 5f060d93ca Drop github.com/asaskevich/govalidator
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 43d86c2a3ef056789c67b0f80bd1ebfcd375a0d4
2025-01-14 13:33:54 -05:00
Omer Aplatony 7ca4de7334 lint: removed empty line
Signed-off-by: Omer Aplatony <omerap12@gmail.com>

Kubernetes-commit: c0b49120e5318202b79a28212db150079acdfb01
2025-01-14 11:42:50 +02:00
Kubernetes Publisher 76f1672b11 Merge pull request #129590 from wojtek-t/cleanup_feature_gates
Remove WatchBookmark feature gate

Kubernetes-commit: f3cbd79db7f0c86a2d3602fdff6b174543d2cf1c
2025-01-14 00:42:32 -08:00
Cici Huang 3a132bfa52 Add test for compatible version
Kubernetes-commit: e179f0e364d58681377286c76af66e6084310f02
2025-01-13 19:11:32 +00:00
Wojciech Tyczyński 30363fdd34 Remove WatchBookmark feature gate
Kubernetes-commit: a7937f5391598b50bc5d6eafdad69a462bc55dec
2025-01-13 15:54:17 +01:00
Kubernetes Publisher 5edcd3c1b7 Merge pull request #124087 from krzysdabro/tests-apiserver-options-kms
apiserver: decrease timeout for TestKMSHealthzEndpoint

Kubernetes-commit: 36d316ebc524a47d05e479b848148c1acf1cee7b
2025-01-13 08:37:10 +00:00
Kubernetes Publisher e62b626c0f Merge pull request #129443 from serathius/watchcache-proxy
Watchcache proxy

Kubernetes-commit: cace64ab7ea82d9f9aa6a4913a500fb30f92a214
2025-01-10 20:47:37 +00:00
Kubernetes Publisher 6c470468a0 Merge pull request #129439 from serathius/refactor-delegate-2
Refactor shouldDelegateList

Kubernetes-commit: 20e1944f88484284a03b6e029bb2d64f53cf3e9d
2025-01-10 20:47:36 +00:00
Marek Siarkowicz a6c0914bb9 Only test requests that pass validation
Kubernetes-commit: 1b2bacda5bd978b68a6dc704606495b29b181690
2025-01-09 12:55:45 +01:00
Zhonghu Xu 29de537bfa Cleanupï: only initiate http2 server options when http2 is not disabled
Kubernetes-commit: a2a0a7521027c2887855dcc0c16857c79f720826
2025-01-09 11:28:51 +08:00
Jon Huhn a4a670721b Fix comment typos
Kubernetes-commit: 006ebbc33a842ed102983c390892d03e908593b9
2025-01-07 21:18:10 -06:00
Marek Siarkowicz 74be087390 Extract and unify cache bypass logic by creating a CacheProxy struct
Kubernetes-commit: 4a4fc9da801f299176c7200e66224aa79b1c0817
2024-12-31 14:04:00 +01:00
Marek Siarkowicz b43177846d Refactor shouldDelegateList
Kubernetes-commit: e5a3bdb3a71575af0d165e8b08cfaba7d572b802
2024-12-31 11:57:43 +01:00
Kubernetes Publisher a2cdf2cd0f Merge pull request #129540 from serathius/test-list-cache-bypass
Test all possible combinations of input for shouldDelegateList

Kubernetes-commit: 75531ccc9ccd70f59207bd22e91938c4ba5c47da
2025-01-09 17:00:13 +00:00
Kubernetes Publisher 2241ea0052 Merge pull request #129542 from serathius/watchcache-benchmark-namespace
Add benchmarking of namespace index

Kubernetes-commit: 30de989fb57fb5921a7ae3e3203cf7ecac9cf3f0
2025-01-09 13:06:56 +00:00
Marek Siarkowicz 3c47ed7b1d Test all possible combinations of input for shouldDelegateList
Kubernetes-commit: fe895563d92f55068c6090e29dfbd21291b203d8
2024-12-31 11:42:28 +01:00
Siyuan Zhang 9bb4aa730a Refactor compatibility version code
Replace DefaultComponentGlobalsRegistry with new instance of componentGlobalsRegistry in test api server.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

move kube effective version validation out of component base.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

move DefaultComponentGlobalsRegistry out of component base.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

move ComponentGlobalsRegistry out of featuregate pkg.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

remove usage of DefaultComponentGlobalsRegistry in test files.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

change non-test DefaultKubeEffectiveVersion to use DefaultBuildEffectiveVersion.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

Restore useDefaultBuildBinaryVersion in effective version.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

rename DefaultKubeEffectiveVersion to DefaultKubeEffectiveVersionForTest.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

pass options.ComponentGlobalsRegistry into config for controller manager and scheduler.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

Pass apiserver effective version to DefaultResourceEncodingConfig.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

change statusz registry to take effective version from the components.

Signed-off-by: Siyuan Zhang <sizhang@google.com>

Address review comments

Signed-off-by: Siyuan Zhang <sizhang@google.com>

update vendor

Signed-off-by: Siyuan Zhang <sizhang@google.com>

Kubernetes-commit: 8fc3a33454ba38783bb63de41ecf5343e2ced67c
2024-12-20 07:03:03 +00:00
Marek Siarkowicz 67ec836891 Implement streaming proto encoding
Kubernetes-commit: f5dd7107f7144c4f76ca6159c1eeddb48a12feaa
2024-12-19 12:30:39 +01:00
Dr. Stefan Schimanski ca7102a0c4 apiserver/admission/cel: add unit tests for JSONPatch expressions
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@gmail.com>

Kubernetes-commit: 78f9f214c02f8476b49749d23ffcd2fc312b5c39
2024-12-19 11:59:52 +01:00
Marek Siarkowicz 38b01a1f78 Streaming JSON encoder for List
Kubernetes-commit: e7c743b2ebfaed1e3132027c0369ac25b14b6f47
2024-12-19 10:38:30 +01:00
Marek Siarkowicz 8161d73803 Add benchmarking of namespace index
Kubernetes-commit: 13a21d5854855671ca0256e39b1657dacd301371
2024-11-05 10:32:59 +01:00
Kubernetes Publisher c7ea66111e Merge pull request #129441 from serathius/watchcache-benchmark
Improve benchmark to handle multiple dimensions

Kubernetes-commit: b56d38e7d47c1ca29670699d7fde9223886c0eeb
2025-01-08 16:39:45 +00:00
Kubernetes Publisher 893803dc85 Merge pull request #129440 from serathius/watchcache-extract-list-response
Extract list response struct to manage all the response fields

Kubernetes-commit: 0e78cf18d19592e63d069e9a8bd72c2e87dbee0c
2025-01-08 01:06:27 +00:00
Kubernetes Publisher 2cad252f0c Merge pull request #129430 from MadhavJivrajani/go124-webhook-regex-ut
[go1.24] webhook: alter regex to account for x509sha1 GODEBUG removal

Kubernetes-commit: c3f3fdc1aa62002a58bec1141fe69e86bbb27491
2025-01-07 00:34:43 +00:00
Kubernetes Publisher e6eb58901f Merge pull request #129349 from dims/bump-x/net-to-v0.33.0
Bump x/net to v0.33.0

Kubernetes-commit: b7ef173c59065f9a5f68eb514ef0483c6f3887ae
2025-01-06 20:41:37 +00:00
Kubernetes Publisher 006dd593ee Merge pull request #128872 from alvaroaleman/generics
Use generic btree in watchcache

Kubernetes-commit: 8f8c94a04d00e59d286fe4387197bc62c6a4f374
2025-01-03 16:41:31 +00:00
Madhav Jivrajani 7c924dc3c8 webhook: alter regex to account for x509sha1 GODEBUG removal
go1.24 removes the x509sha1 GODEBUG variable, and with it the
support for SHA-1 signed certs. This commit alters the regex
in unit tests to account for that and prep for go1.24.

Signed-off-by: Madhav Jivrajani <madhav.jiv@gmail.com>

Kubernetes-commit: cff0f4009f17c84141553c143872d47756209f4d
2024-12-30 13:39:17 -08:00
Davanum Srinivas 825f36b139 Bump x/net to v0.33.0
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 0b6e3718340fa7e3846cf9b7d5a0f7a684a6fa5a
2024-12-20 14:30:57 -05:00
Marek Siarkowicz 62c90b1dff Extract list response struct to manage all the response fields
Kubernetes-commit: 78a6402755905f14d72ee47fdb1e169dbdaa6853
2024-11-24 13:10:17 +01:00
Alvaro Aleman 84b3c4c640 Use generic btree in watchcache
This change makes us use the generic btree available in newer versions
of github.com/google/btree. This avoids a bunch of type assertions and
makes the code easier to read.

Kubernetes-commit: b1cb471982b74c13c26dbcc0f4e1b5ae92ea47e6
2024-11-19 22:04:08 -05:00
Alvaro Aleman a53d8f189c Update github.com/google/btree
Kubernetes-commit: a97ed3c98bcd2c520260aa04c516a24e975e7d69
2025-01-02 19:44:07 -05:00
Kubernetes Publisher 494066f4a4 Merge pull request #128279 from Jefftree/compat-133
Bump DefaultKubeBinaryVersion to 1.33

Kubernetes-commit: 9d82148924751d350080ffb681b4d5265aa01f6f
2024-12-21 00:32:54 +00:00
Jefftree 15b54ba29c Expand emulated version range to n-3 with 1.31 floor
Kubernetes-commit: cf28c0405c02e82e1028193a4ce1aa75d473ae8e
2024-12-10 16:34:34 +00:00
Kubernetes Publisher c7fb780f6b Merge pull request #129074 from siyuanfoundation/fgv
Add Validation to versioned feature specs.

Kubernetes-commit: 4a0b0365efd6a4c072a1545f7beed3b6664497c2
2024-12-18 06:04:08 +00:00
Siyuan Zhang 0daeb9f98b Add Validation to versioned feature specs.
Co-authored-by: Jordan Liggitt <liggitt@google.com>
Co-authored-by: Siyuan Zhang <sizhang@google.com>

Signed-off-by: Siyuan Zhang <sizhang@google.com>

Kubernetes-commit: 00dab9dffa6a35fbfaad4ebcdd17be00f40e423c
2024-12-03 18:48:07 +00:00
Kubernetes Publisher b3597c01bb Merge pull request #129205 from tosi3k/wc-configurable-retention
Configure watch cache history window based on request timeout

Kubernetes-commit: 107be8feccfce7259b402ed75415309268744a24
2024-12-17 22:05:11 +00:00
Kubernetes Publisher f5b4a60379 Merge pull request #129213 from Jefftree/k-openapi
Bump kube-openapi

Kubernetes-commit: 13eb074ddd231d127709f0410185eeca68a69c8a
2024-12-14 02:10:08 +00:00
Kubernetes Publisher 665a8df3fd Merge pull request #128343 from Jefftree/responsewriter-test
Add tests for InMemoryResponseWriter

Kubernetes-commit: c644f4d18506fb4055858646a01ec8a00802d97e
2024-12-14 02:10:06 +00:00
Jefftree 68ddbb0384 bump kube-openapi
Kubernetes-commit: 3269f4bb94c58dfe577621c42f88ea06fbdd79a7
2024-12-13 20:50:49 +00:00
Antoni Zawodny 0dcf3e9d26 Configure watch cache history window based on request timeout
Kubernetes-commit: 4a2b7ee5699331df31b7483be082c201a1e7f51f
2024-12-13 15:40:03 +01:00
Kubernetes Publisher b62222be7c Merge pull request #129195 from dims/update-x/crypto/ssh-dependency
Update x/crypto/ssh dependency to v0.31.0

Kubernetes-commit: b21ab179c74a270cd276d2dbb5f4b55730838096
2024-12-13 10:10:13 +00:00
Kubernetes Publisher 6ecac9f4d1 Merge pull request #128844 from cheftako/updateANP
Bump konnectivity-client to v0.31.1

Kubernetes-commit: f7b1d107c6127781ef2a4d11381e59198151f1e2
2024-12-13 06:04:17 +00:00
Davanum Srinivas dbfbe37046 Update x/crypto/ssh dependency
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 80735180ab2c61232dcc4646e693ddcaeaf96ca3
2024-12-12 20:46:15 -05:00
Kubernetes Publisher 988c0f82a8 Merge pull request #129054 from pohly/remove-import-name
remove import doc comments

Kubernetes-commit: e8615e27125518f0ed0ba06244b7ecee21451bb0
2024-12-12 10:07:15 +00:00
Kubernetes Publisher 0a3c89578c Merge pull request #128890 from kei01234kei/output_log_when_server_shutdown_is_failed
📝 output log when server shutdown is failed

Kubernetes-commit: 516b2c2e41799046363bf48444624bdda91bffb6
2024-12-12 06:13:13 +00:00
Kubernetes Publisher 47024d970a Merge pull request #128862 from MariamFahmy98/map-unit-tests
fix: remove duplicated unit tests in JSONPatch and ApplyConfiguration

Kubernetes-commit: 43f50b4e51302e443b4836c45666bfedbaf70406
2024-12-12 06:13:12 +00:00
Kubernetes Publisher ea0b9ced4d Merge pull request #128621 from kmala/master
update the github.com/golang-jwt/jwt/v4 dependency

Kubernetes-commit: a892f0fd80c548c4caedc35933bb441572135c35
2024-12-12 06:13:11 +00:00
Kubernetes Publisher df1723ca5c Merge pull request #127897 from modulitos/add-x509-uid-to-user
Set User.UID from x509 cert

Kubernetes-commit: ed8999ed64d4f6e05859f83456f279949bac7907
2024-12-12 06:13:09 +00:00
Kubernetes Publisher 3658357fea Merge pull request #129103 from liggitt/drop-winreadlinkvolume
Drop use of winreadlinkvolume godebug option

Kubernetes-commit: bfe431b53e600c9a36c46eef0f6ecfcf37265d60
2024-12-06 18:57:54 +00:00
Jordan Liggitt 9add4d0573 Drop use of winreadlinkvolume godebug option
Kubernetes-commit: 3046fe23d4fe4ba86713ffd61bf0e07156b2b7c3
2024-12-06 02:40:53 -05:00
Kubernetes Publisher 99abadede2 Merge pull request #129081 from stlaz/fg_remote_uid
featuregate UID in RequestHeader authenticator

Kubernetes-commit: 1504f10e7946f95a8b1da35e28e4c7453ff62775
2024-12-05 02:51:03 +00:00
Kubernetes Publisher 79e6f1fbcf Merge pull request #129083 from liggitt/go1.23windows
Revert to go1.22 windows filesystem stdlib behavior

Kubernetes-commit: 6fc64a261c1dca857a5a7fd1bc87fae38dbe1c8a
2024-12-04 22:41:51 +00:00
Jordan Liggitt aaeb01be6d Revert to go1.22 windows filesystem stdlib behavior
Kubernetes-commit: 3878a3a6de64660e356a35f70471c27a09698090
2024-12-04 09:52:56 -05:00
Stanislav Láznička 5047b8fe41 featuregate UID in RequestHeader authenticator
Kubernetes-commit: a051b067cdffc92fbe40bcc5a8e8f1bf974348c4
2024-12-04 15:44:45 +01:00
Patrick Ohly b84662911c remove import doc comments
The "// import <path>" comment has been superseded by Go modules.
We don't have to remove them, but doing so has some advantages:

- They are used inconsistently, which is confusing.
- We can then also remove the (currently broken) hack/update-vanity-imports.sh.
- Last but not least, it would be a first step towards avoiding the k8s.io domain.

This commit was generated with
   sed -i -e 's;^package \(.*\) // import.*;package \1;' $(git grep -l '^package.*// import' | grep -v 'vendor/')

Everything was included, except for
   package labels // import k8s.io/kubernetes/pkg/util/labels
because that package is marked as "read-only".

Kubernetes-commit: 8a908e0c0bd96a3455edf7e3b5f5af90564e65b0
2024-12-02 14:43:58 +01:00
Keisuke Ishigami 753940e964 📝 output log when server shutdown is failed
Kubernetes-commit: dd1300df98682f524a2f028e3b296b3e8783a711
2024-11-21 00:58:27 +09:00
Mariam Fahmy b858201395 fix: remove duplicated unit tests in JSONPatch and ApplyConfiguration
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

Kubernetes-commit: f87bcd9b3a8166acb62e3f39e207bda3673f0ed6
2024-11-19 15:42:29 +02:00
Walter Fender c8d9720a72 Bump konnectivity-client to v0.31.1
Bump konnectivity-client to v0.31.1
Includes a feature which allows KNP server count to be determined by
lease.

Kubernetes-commit: 588408df1cd733f1bceeb69632b010b83e1f21df
2024-11-18 14:10:03 -08:00
Kubernetes Publisher cccad306d6 Merge pull request #128722 from dims/possible-fix-for-alpha-feature-breaking-tests
Fix for alpha CI jobs failing with AllowUnsafeMalformedObjectDeletion switched on

Kubernetes-commit: 1b08de5f7f964323f1e65a7db5e861429087efe6
2024-11-09 17:17:38 +00:00
Davanum Srinivas 51b4b4971a Possible fix for alpha CI jobs failing with AllowUnsafeMalformedObjectDeletion switched on
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 9c6126e29c2dd63668c5d2320a4c26ff173fc499
2024-11-09 08:56:55 -05:00
Kubernetes Publisher 3423727e46 Merge pull request #127581 from richabanker/flagz-apiserver
Add flagz endpoint for apiserver

Kubernetes-commit: 4d91d502832ce55204343d7864fe28019d87b2bb
2024-11-08 13:01:39 +00:00
Kubernetes Publisher b8357e72dd Merge pull request #128618 from knrc/error-check-fix
Fix error check

Kubernetes-commit: e1e92bc40373fa01be05c64e1aee3bdee5d74040
2024-11-08 13:01:37 +00:00
Kubernetes Publisher 0b01a72aa3 Merge pull request #127513 from tkashem/delete-undecryptable
KEP-3926: unsafe deletion of corrupt objects

Kubernetes-commit: 4d10ae8fdc579e2bb09789507cae7b8d32cbd306
2024-11-08 13:01:36 +00:00
Kubernetes Publisher f983148f8c Merge pull request #125577 from richabanker/statusz
Add statusz endpoint for apiserver

Kubernetes-commit: 60651eb17257356a2be70ac8d2c14218c1af0834
2024-11-08 13:01:33 +00:00
Keerthan Reddy Mala d513755309 update the github.com/golang-jwt/jwt/v4 dependency
Kubernetes-commit: 7e977d824cf2740c4ce7f71fdaeecd39f4bb04b1
2024-11-06 11:02:32 -08:00
Abu Kashem fbb5ab0d70 handle watch for unsafe delete
Kubernetes-commit: 25efc8f2d136a9574166be02789ac727c5b4a3fd
2024-11-05 20:36:56 -05:00
Marek Siarkowicz fd21d18170 Improve benchmark to handle multiple dimensions
Kubernetes-commit: 4a0578e3dee093aec23e16556ac8fb8642060362
2024-11-05 10:32:59 +01:00
Abu Kashem 8b8b5c0f78 add access control check for unsafe delete
add access control check to ensure that the user has permission to do
'unsafe-delete-ignore-read-error' on the resource being deleted

Kubernetes-commit: 9932dbef5787bfdc88d8ae35e90dc0965bbc6c80
2024-10-31 16:01:58 -04:00
Richa Banker ae08979dcc Replace StorageVersion API with aggregated discovery to fetch served resources by a peer for MVP
Co-authored-by: Joe Betz <jpbetz@google.com>

Co-authored-by: Jordan Liggitt <jordan@liggitt.net>

Kubernetes-commit: 8b2cee83c15b1fbc304123902e1bd8e8e06f0a12
2024-10-28 23:04:26 -07:00
Jefftree ada3ee1529 add test for inmemory response writer
Kubernetes-commit: bf1b9c45ea976a9e871b35b644f34ec508f3d57d
2024-10-25 20:54:18 +00:00
Kevin Conner a69b1ea2d5 Fix error check
Signed-off-by: Kevin Conner <kev.conner@gmail.com>

Kubernetes-commit: e7e1fb7234264b6f0785ad9fdd29e90c5da36002
2024-10-21 08:09:55 -07:00
Lukasz Szaszkiewicz 3aea7778be storage/cacher/ready: dynamically calculate the retryAfterSeconds
retryAfterSeconds is based on the time elapsed since the state (ready, unready) was last changed.

Kubernetes-commit: 04f0bd4e83bbc0a24b8a924333544be86b252c97
2024-10-14 08:17:14 +02:00
modulitos 026bd8791b set user.DefaultInfo.UID from x509 cert
Kubernetes-commit: b577972a551ea0dbc22f29ac97f0a0e621d42e1b
2024-10-06 19:28:21 -07:00
Abu Kashem 1f27993c44 implement unsafe deletion, and wire it
- implement unsafe deletion, and wire it
- aggregate corrupt object error(s) from the storage LIST operation
- extend storage error:
a) add a new type ErrCodeCorruptObj to represent a corrupt object:
b) add a new member 'InnerErr error' to StorageError to hold
   the inner error
- add API status error

Kubernetes-commit: 5d4b4a160dc551dc8979012eeabea1a098945603
2024-09-20 17:36:27 -04:00
Abu Kashem 4ce1c97be7 api: add a new field to meta/v1 DeleteOptions
- add a new boolean field
  IgnoreStoreReadErrorWithClusterBreakingPotential to meta/v1 DeleteOptions

- add validation for the new delete option
add validation for the new field in the delete options
ignoreStoreReadErrorWithClusterBreakingPotential

- prevent the pod eviction handler from issuing an unsafe pod delete
prevent the pod eviction handler from enabling the
'ignoreStoreReadErrorWithClusterBreakingPotential' delete option

Kubernetes-commit: b6773f15897dc31190b2be7cb49dd02015440465
2024-09-23 12:22:53 -04:00
Omer Aplatony 81ab7e0c68 Add test for CEL reserved symbols without double underscore
Signed-off-by: Omer Aplatony <omerap12@gmail.com>

Kubernetes-commit: 1b371d0d4693da66343aa7f8642a694ea318692d
2024-09-15 18:14:25 +03:00
David Mengqi Yu c9834df380 Add example in etcd override flag help text
Kubernetes-commit: 44d31af29bf442520722024f7fa58a64b44d68de
2024-09-04 12:28:53 -07:00
Patrick Ohly c2262d518b client-go/rest: backoff with context support
The BackoffManager interface sleeps without considering the caller's context,
i.e. cancellation is not supported. This alone is reason enough to deprecate it
and to replace it with an interface that supports a context parameter.

The other reason is that contextual logging needs that parameter.

Kubernetes-commit: b15a1943d51adfb8c5e0185d58d25e038c3d6ade
2024-09-02 20:18:47 +02:00
David Ashpole 6898b8387d restrict trace context propagation to system:master and system:monitoring
Kubernetes-commit: 3842d74d97d803372eb8e2d6cfc3d3d56a4c7a0d
2024-09-01 18:03:26 +00:00
Marek Siarkowicz 5a059075db Serve LISTs with exact RV and continuations from cache
Kubernetes-commit: f82c9e56d928d1028d4b298578f275a2e5e69490
2024-07-03 21:36:51 +02:00
Richa Banker d520e75de3 add statusz implementation and enablement in apiserver
Kubernetes-commit: 8bf6eecedffaa85c6d06ef2c8ad412ded77cb309
2024-06-18 20:30:43 -07:00
Kubernetes Publisher 43b9e23f22 Merge pull request #127360 from knight42/feat/split-stdout-stderr-server-side
API: add a new `Stream` field to `PodLogOptions`

Kubernetes-commit: 9660e5c4cd41700eae41a316dd236090bd7bf6e9
2024-11-08 13:01:31 +00:00
Kubernetes Publisher 67b987f568 Merge pull request #128637 from jpbetz/fix-mutating-admission-defaulting
Bug fix: MutatingAdmissionPolicy should default builtin types after each mutation

Kubernetes-commit: 9729ac8c6f233850b0624b8663577647bf4c37b2
2024-11-08 13:01:27 +00:00
Kubernetes Publisher a16a110639 Merge pull request #128166 from yongruilin/test-allow-label
test: add integration test for allow-metric-label

Kubernetes-commit: 154b756e2ed850d2e64baea269dbb749ac02a77d
2024-11-08 13:01:25 +00:00
Kubernetes Publisher 3a2942bdff Merge pull request #128593 from jpbetz/bump-cel-123
Introduce CEL two variable comprehensions

Kubernetes-commit: dc01fa9d2b6632dc932eb760573659608add1b8f
2024-11-07 03:24:36 +00:00
Kubernetes Publisher b5e6c92c1d Merge pull request #128503 from benluddy/cbor-codecs-featuregate
KEP-4222: Wire serving codecs to CBOR feature gate.

Kubernetes-commit: 6399c32669c62cfbf7c33b14b77d6781ce1cce27
2024-11-07 03:24:34 +00:00
Joe Betz 490aa4761a Add defaulting to tests
Kubernetes-commit: a6e0a7b17bfd8e95e7cc1206f6c5e67aebec6495
2024-11-06 21:45:55 -05:00
Joe Betz 6b49c2289e Fix defaulting for native types
Kubernetes-commit: fd6900131446465bb6f1216b0de53279c5b84209
2024-11-06 20:42:27 -05:00
Joe Betz accfd98e20 Wrap unversioned CEL library initializer calls with guard
Kubernetes-commit: dc5e2f3fa295276029535359246154021861fdd6
2024-11-06 15:32:15 -05:00
Joe Betz ba14b9c42a Add cost testing for two variable comprehensions
Kubernetes-commit: 3d4a5dac4e7921c5e41824dd130b646580a7c3f5
2024-11-05 20:57:13 -05:00
Joe Betz 454f4fe2a7 hack/pin-dependency.sh github.com/google/cel-go v0.22.0
Kubernetes-commit: b0180a9a376caee4a12cbf549b3187d21cbdc07d
2024-11-05 19:21:09 -05:00
Ben Luddy 0b5852eb9a Wire serving codecs to CBOR feature gate.
Integration testing has to this point relied on patching serving codecs for built-in APIs. The
test-only patching is removed and replaced by feature gated checks at runtime.

Kubernetes-commit: 439d2f7b4028638b3d8d9261bb046c3ba8d9bfcb
2024-11-01 16:05:32 -04:00
Kubernetes Publisher 88f051a068 Merge pull request #128554 from ritazh/webhookmatchcondition-doc-comment
Add WebhookMatchCondition doc comment

Kubernetes-commit: 7a1f8aaa5256bf6ff8afca322e2cf0935c21dbd2
2024-11-06 23:25:00 +00:00
Kubernetes Publisher be81d72259 Merge pull request #128501 from benluddy/watch-cbor-seq
KEP-4222: Use cbor-seq content-type for CBOR watch responses.

Kubernetes-commit: a885e446d6f6f5530da4923a3872eb27ca47bdc0
2024-11-06 23:24:59 +00:00
Rita Zhang 882c7896e7 Add webhookmatchcondition doc comment
Signed-off-by: Rita Zhang <rita.z.zhang@gmail.com>
Co-authored-by: Jordan Liggitt <jordan@liggitt.net>

Kubernetes-commit: 151599d47a9b866a7d7b8dffc5714557817bbcf2
2024-11-04 16:26:42 -08:00
Ben Luddy dc2e401d84 Use application/cbor-seq media type in streaming CBOR responses.
The media type application/cbor describes exactly one encoded item. As a new (to Kubernetes) format
with no existing clients, streaming/watch responses will use the application/cbor-seq media
type. CBOR watch responses conform to the specification of CBOR Sequences and are encoded as the
concatenation of zero or more items with no additional framing.

Kubernetes-commit: 504f14998e920ca8837b3310094b3da11c62a070
2024-11-01 13:14:06 -04:00
Kubernetes Publisher e906dc9707 Merge pull request #128266 from AnishShah/resize-subresource
[FG:InPlacePodVerticalScaling] Introduce  /resize subresource to request pod resource resizing

Kubernetes-commit: 648717cc740cafbce778e983f195dc6879bf22e3
2024-11-06 11:24:06 +00:00
yongruilin 15884a5fd0 feat: Add function to reset label allow list for apiserver endpoints metrics
Kubernetes-commit: d3795eaf76e5ecc632325cfb598533c4e3c31704
2024-10-30 09:28:24 -07:00
Anish Shah cb02f4a386 support for resize subresource in ResourceQuota admission.
Kubernetes-commit: 1b98fe6079059cb1d7bfda4f4b318f614449fee5
2024-10-24 14:00:57 -07:00
Kubernetes Publisher 04fa4ade1a Merge pull request #128539 from benluddy/cbor-feature-gates
KEP-4222: Add CBOR feature gates.

Kubernetes-commit: a28f14089cfa47ef9c57f9f283e1504a68f616d6
2024-11-06 03:22:48 +00:00
Kubernetes Publisher 7338177523 Merge pull request #128580 from jpbetz/bump-kube-openapi
Bump kube-openapi to latest

Kubernetes-commit: 9a2a7537f035969a68e432b4cc276dbce8ce1735
2024-11-05 23:23:51 +00:00
Kubernetes Publisher 4cb4306ed2 Merge pull request #128415 from serathius/watchcache-btree-2
Use btree for watch cache storage to serve LIST more efficiently

Kubernetes-commit: 19d6337d593773397164f25fad0ea5daba05333e
2024-11-05 23:23:50 +00:00
Kubernetes Publisher 982944365e Merge pull request #127134 from jpbetz/mutating-admission
KEP-3962: MutatingAdmissionPolicy Alpha

Kubernetes-commit: 2d6c8a129df97caf5bda3a186a8f7f3e41591352
2024-11-05 19:23:26 +00:00
Joe Betz 5fff35ea12 hack/pin-dependency.sh k8s.io/kube-openapi 32ad38e42d3faf1ce94eb29f4ea6d763339b258e
Kubernetes-commit: f2157ff73e3c9b7c2a36bf371e388e8976d93975
2024-11-05 10:18:57 -05:00
Kubernetes Publisher 4605e42ff2 Merge pull request #128507 from dims/use-k8s.io/utils/lru-instead-of-github.com/golang/groupcache/lru
Use k8s.io/utils/lru instead of github.com/golang/groupcache/lru

Kubernetes-commit: 7a4d755644e83dfade7bbc4c240c204a9e54d9c0
2024-11-04 23:24:19 +00:00
Kubernetes Publisher e3e5839136 Merge pull request #126894 from carlory/ZeroLimitedNominalConcurrencyShares
remove generally available feature-gate ZeroLimitedNominalConcurrencyShares

Kubernetes-commit: 6a1a6fd85fb80fd9c4b3f823741853822a4270db
2024-11-04 19:18:06 +00:00
Joe Betz f7ccc75f9f Reorganize and expand unit test coverage
Also apply reviewer feedback

Kubernetes-commit: 0dc08eded95c2d620de70648dee07254f2e771b3
2024-11-04 10:50:53 -05:00
Ben Luddy ca9c3d965e Add CBOR feature gates.
For alpha, there is one apiserver feature gate and two client-go feature gates controlling
CBOR. They were initially wired to separate test-only feature gate instances in order to prevent
them from being configurable at runtime via command-line flags or environment variables (for
client-go feature gates outside of Kubernetes components). All of the integration tests required by
the KEP as alpha criteria have been implemented. This adds the feature gates to the usual feature
gate instances and removes the temporary code to support separate test-only feature gate instances.

Kubernetes-commit: 072dfcb416fd4e1ddab0a89ac4faf519e268bc96
2024-11-04 10:40:19 -05:00
Davanum Srinivas cae3853011 Use k8s.io/utils/lru instead of github.com/golang/groupcache/lru
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 2b0592ee77d0a0bb3017df042066ecb8c83d2fb3
2024-11-01 22:19:11 -04:00
Kubernetes Publisher 6df5cd720d Merge pull request #128481 from carlory/dependencies-ginkgo-gomega
dependencies: ginkgo v2.21.0, gomega v1.35.1

Kubernetes-commit: 88a997ac01cd8a386df9318d81b89257307f53fb
2024-11-01 19:24:43 +00:00
carlory 5d1ea16030 dependencies: ginkgo v2.21.0, gomega v1.35.1
Kubernetes-commit: 80b1a297865500891bd823005ace761becae5dbf
2024-11-01 11:35:24 +08:00
Kubernetes Publisher 0b45fb4d77 Merge pull request #128416 from jpbetz/reset-filter
Add optional ResetFieldsFilterStrategy interface for storage

Kubernetes-commit: b831df733e5cf244331f61fffb0ba86787b27236
2024-11-01 03:55:40 +00:00
Kubernetes Publisher 97c3c25661 Merge pull request #128463 from knrc/fix_vap_elapsed_time_tracking
Fix elapsed time tracking for validating admission policies

Kubernetes-commit: 34ce75749e7400b30b0dda6659ad31801d528b2b
2024-10-31 23:02:06 +00:00
Kubernetes Publisher 1ae041730c Merge pull request #128468 from wojtek-t/fix_miss_events_tests
Fix TestCacherDontMissEventsOnReinitialization test

Kubernetes-commit: d76a8fae674c972b5d2e2cfc226c607c9f63a412
2024-10-31 23:02:05 +00:00
Kubernetes Publisher f1894425ee Merge pull request #128454 from jpbetz/fix-emulated-storage-version-encoding-config
Don't select versions that have a replacement as storage version for APIs

Kubernetes-commit: 7a43edefa13fdaf598eb73380151b8a8b7b49e2d
2024-10-31 19:02:07 +00:00
Wojciech Tyczyński 0679efd5ba Fix TestCacherDontMissEventsOnReinitialization test
Kubernetes-commit: d35ea217fa7541a2b1942aaf533697a2e4e7a222
2024-10-31 12:33:04 +01:00
Kubernetes Publisher 9270d97c6f Merge pull request #128456 from benluddy/nondeterministic-response-encoding
KEP-4222: Allow nondeterministic object encoding in HTTP response bodies.

Kubernetes-commit: dc1d7f41ef4765552193c10cf1c1ed2b0c4e149b
2024-10-30 23:02:04 +00:00
Kubernetes Publisher 2e7093fa33 Merge pull request #128273 from benluddy/cbor-apply
KEP-4222: Support CBOR encoding for apply requests.

Kubernetes-commit: 16f9fdc7057e1f69ff1a44e3dbbcf7b994c3cd29
2024-10-30 19:08:47 +00:00
Joe Betz 7d5891a801 Unit test for emulated storage version selection
Kubernetes-commit: d5517b7a518c60e8e6f1aadc1806f4e2a254d6a9
2024-10-30 13:15:35 -04:00
Joe Betz f0baf72379 Don't pick versions that have a replacement as storage version
Kubernetes-commit: af811be190b7d04a49ecf47bf55a3858fa7e7e96
2024-10-30 11:47:43 -04:00
Kubernetes Publisher 28f9eed685 Merge pull request #126875 from serathius/watchcache-test-indexers
Adding tests for using indexers in tests

Kubernetes-commit: a0e5e244b3fcfc60d2bf2296c63a72e015f5884b
2024-10-30 03:01:53 +00:00
Kubernetes Publisher d93c90f116 Merge pull request #128274 from eddycharly/fix-cel-type-provider
fix: cel type provider should return a type type

Kubernetes-commit: 42b7cfecece002f13b53a293589c3f02595f4d32
2024-10-30 03:01:52 +00:00
Kubernetes Publisher 918f350339 Merge pull request #126754 from serathius/watchcache-btree
Reimplement watch cache storage with btree

Kubernetes-commit: c83250d1040af039455ed8a4b030896578a55720
2024-10-29 18:49:42 +00:00
Kubernetes Publisher 14c29cd5d7 Merge pull request #128013 from seans3/admission-configuration-strict
AdmissionConfiguration now uses strict validation

Kubernetes-commit: eb5c8965befeefad027d0c1684fe6d34f6f31c19
2024-10-29 18:49:40 +00:00
Kubernetes Publisher 76c064061d Merge pull request #128122 from aojea/storage_error
use same name for package than the folder

Kubernetes-commit: e15d5b9da913a2e7e7d190a8aa1d6c577872f015
2024-10-29 18:49:38 +00:00
Kubernetes Prow Robot 4bece4d457 Merge pull request #128196 from richabanker/move-version
Move k8s.io/apiserver/pkg/util/version to component-base

Kubernetes-commit: 119f114f012ae6d1cd851bdb2c3f849307e83258
2024-10-29 18:49:35 +00:00
Kubernetes Publisher fb9f1a8075 Merge pull request #127898 from modulitos/modulitos/fix-metrics-docs
Fix docs for apiserver admission metrics

Kubernetes-commit: 1947bf57ef0635e8764eee25e5726a8ec4189b7f
2024-10-29 18:49:34 +00:00
Kevin Conner b91c4326ba Fix elapsed time tracking for validating admission policies
Signed-off-by: Kevin Conner <kev.conner@gmail.com>

Kubernetes-commit: 9538747d4d6f7f3bc01e79d2d4781df1d7535f16
2024-10-29 11:26:46 -07:00
Joe Betz 15adc430e7 Add ResetFieldsFilterStrategy
Kubernetes-commit: 2bc17d1cf03f2f2bcd683e7e79f01c929951cca3
2024-10-29 12:03:32 -04:00
Joe Betz 125611e8cd hack/pin-dependency.sh sigs.k8s.io/structured-merge-diff/v4 v4.4.2
Kubernetes-commit: 6fe51403665f1b6e820226004817b92e3118cabc
2024-10-31 21:19:15 -04:00
Ben Luddy 30be0a3c20 Allow nondeterministic object encoding in HTTP response bodies.
Kubernetes-commit: dee76a460ec80f15dc199c93e506586687d42291
2024-10-28 12:09:02 -04:00
Joe Betz 9843fd95fb Rename dispatcher Run to Start to match naming conventions
Kubernetes-commit: 0dfbc85cd9f7c0578ed385928e6990a496112e6c
2024-10-25 18:44:10 -04:00
Joe Betz b738c82d70 Fix comment in mutating webhook dispatcher
Kubernetes-commit: 1031e07e8325fc043d741b697c197d5416bfcc98
2024-10-25 14:37:39 -04:00
Joe Betz c259217340 Improve error messaging for validating admission policy authz
Kubernetes-commit: 1ad6fd7a0fa454cc3302b579dc73eb5c9afec49a
2024-10-25 13:52:34 -04:00
Joe Betz 9da95682e0 Add jsonpatch.escapeKey CEL function
Kubernetes-commit: 712cc20996aedad5ac525184d76ea581f98d6e35
2024-10-25 13:50:25 -04:00
Joe Betz a5e93cb37c Add MutatingAdmissionPolicy plugin to admission chain
This expands the generic plugin support to both validating and mutating policies.  It also adds the
mutating policy admission plugin using the generics plugin support.

This also implements both ApplyConfiguration and JSONPatch support.

Co-authored-by: Alexander Zielensk <alexzielenski@gmail.com>

Kubernetes-commit: 25e11cd1c143ef136418c33bfbbbd4f24e32e529
2024-10-25 13:46:58 -04:00
Joe Betz 0e6467b270 Add mutation support into CompositedCompiler and reorganize for clarity
Kubernetes-commit: 081353bf8ad963d43c5da6714a24f62cfe0b8401
2024-10-25 14:37:17 -04:00
Joe Betz 9ead80d1bb Add MutatingAdmissionPolicy API
This is closely aligned with ValidatingAdmissionPolicy
except that instead of validations that can fail with
messages, there are mutations, which can be defined
either with as an ApplyConfiguration or JSONPatch.

Co-authored-by: cici37 <cicih@google.com>

Kubernetes-commit: 3a1733f302d0fe9994bcc8e91fa2191c94606c2b
2024-10-25 13:25:46 -04:00
Joe Betz bd808a01a1 Add feature gate
Co-authored-by: cici37 <cicih@google.com>

Kubernetes-commit: 4b13362dda020f80cf961dbeefae750f63f803f9
2024-10-25 13:22:58 -04:00
Joe Betz 8794780f6a Clean up Object initialization support
The initial work of this had been merged before
this PR but was not yet in use. This simplifies
the implementation and adds some basic type
sanity checking.

Co-authored-by: Jiahui Feng <jhf@google.com>

Kubernetes-commit: 9ee1ea9d37c25d8151aad18d5a2a959836dcbe12
2024-10-25 13:22:06 -04:00
Joe Betz b09b8016b6 Move caching authorizer to shared location to be used by mutating and validating policy
Kubernetes-commit: 910c2e2dad58b08d7a5ee6c59a2970454e241e2a
2024-10-25 13:20:14 -04:00
Kubernetes Publisher 78628824cd Merge pull request #128243 from benluddy/cbor-dynamic-integration
KEP-4222: Add CBOR variant of admission webhook integration test.

Kubernetes-commit: 5147eebf224ae41892b736179ca91c47fd794565
2024-10-25 02:15:46 +00:00
Charles-Edouard Brétéché 00c7d5a4e1 use require
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Kubernetes-commit: ea1bd956c57f23b4ac5feb0b24ea79bbbe3b31ff
2024-10-24 22:25:37 +02:00
Charles-Edouard Brétéché b432ca9bc1 add test
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Kubernetes-commit: 0d825361e6d31171b77a00665fdb63816b8b6e00
2024-10-24 21:57:51 +02:00
Marek Siarkowicz 5913dac984 Implement btree based storage indexer
Kubernetes-commit: 50d2fab27903c66837d625b6ff702cf7873e3c7f
2024-10-24 18:17:56 +02:00
Kubernetes Publisher 781f771b86 Merge pull request #125258 from serathius/etcd-kubernetes-interface
Etcd kubernetes interface

Kubernetes-commit: cadb1508a99580a3fec27da8d75c432756275fc4
2024-10-24 14:08:46 +00:00
Ben Luddy f27bb5491e Wire test-only feature gate for CBOR serving.
To mitigate the risk of introducing a new protocol, integration tests for CBOR will be written using
a test-only feature gate instance that is not wired to runtime options. On alpha graduation, the
test-only feature gate instance will be replaced by a normal feature gate in the existing apiserver
feature gate instance.

Kubernetes-commit: 0cad1a89b6721308746cc1a12f12de31a259a0d3
2024-10-23 16:36:25 -04:00
Charles-Edouard Brétéché 15220968e1 fix: cel type provider should return a type type
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Kubernetes-commit: 1518c4ec16d06b7281f0b6c9a0e8c40952475f6b
2024-10-22 22:21:51 +02:00
Ben Luddy cf0dbba4e6 Support application/apply-patch+cbor in patch requests.
Kubernetes-commit: 37ed906a33211c7d578cab2d681941ebfd2f2f23
2024-10-22 16:08:24 -04:00
Antonio Ojea 81796a85a6 use same name for package than the folder
Kubernetes-commit: aa7d0b00944958c9898bace86f5bc77cdcba24c9
2024-10-16 10:38:57 +00:00
Sean Sullivan 2bfdf1d736 AdmissionConfiguration now uses strict validation
Kubernetes-commit: ae20937ea6e8532da05899ed158152288d764ee0
2024-10-11 16:58:04 -07:00
Jian Zeng 641a07c997 feat(apiserver): set stream param in LogLocation
Signed-off-by: Jian Zeng <anonymousknight96@gmail.com>

Kubernetes-commit: d9687a8c3adaf48d398237328a8db510f3b9399d
2024-09-13 22:22:59 +08:00
carlory 9b88185823 remove general avaliable feature-gate ZeroLimitedNominalConcurrencyShares
Kubernetes-commit: f68dc137609d6ceb2b4e66bd746bbf9f9e92ce25
2024-09-05 10:53:51 +08:00
Marek Siarkowicz 518e8ed25c Adding tests for using indexers in tests
Kubernetes-commit: 711772a1e15288d813a830780317a134df9acb5c
2024-08-22 20:04:37 +02:00
Marek Siarkowicz e85d45c0a4 Use btree for watch cache storage to serve LIST more efficiently
Can be disabled via BtreeWatchCache feature flag.

Kubernetes-commit: 5ea427ebb508ce2871d4d0f9869959275c0d3bce
2024-08-17 11:01:41 +02:00
modulitos 334b2d8b10 Fix docs for apiserver admission metrics
Kubernetes-commit: 97872c48a9a8dbc76b0d5f869215cd53a12fe773
2024-08-05 16:42:09 -07:00
Marek Siarkowicz eb7f648085 Migrate GetList to Kubernetes client
Kubernetes-commit: a16a364324c218b703d033edf89187aa60d9dd87
2024-05-31 14:36:22 +02:00
Marek Siarkowicz de27d754e6 Migrate Count to Kubernetes client
Kubernetes-commit: e192ac31a425e186230285829947df3854d08125
2024-05-31 11:08:48 +02:00
Marek Siarkowicz fd6565d201 Migrate Delete and GuaranteedUpdate to Kubernetes client
Kubernetes-commit: 2fcd321c426d2567e74b97da38b4860638385007
2024-05-31 11:07:38 +02:00
Marek Siarkowicz fc6b2587cc Migrate Create to Kubernetes client
Kubernetes-commit: 53ca81da29511596cac0301e7a4f527309d380e3
2024-05-31 10:57:41 +02:00
Marek Siarkowicz 187d1ad94a Migrate Get to Kubernetes client
Kubernetes-commit: 092a6d1e0d9f91d543369f40a1fb65e9c3bff034
2024-05-31 10:54:49 +02:00
Marek Siarkowicz 37f422396a Update recorders to wrap kubernetes.Client
Kubernetes-commit: 066c1c05d73690b48c872f3fbc23b7722cd44fe3
2024-07-05 14:19:49 +02:00
Marek Siarkowicz b43a2467d5 Add etcd kubernetes interface package to vendor
Kubernetes-commit: 249ad2a6137cc8f1e0ccb7f0aef9ff4ba38927b9
2024-05-31 10:27:51 +02:00
Kubernetes Publisher 7dd4904f18 Merge pull request #127341 from mjudeikis/mjudeikis/deprecate.ch.fully
Fully deprecate StopCh

Kubernetes-commit: 035e272cb1efb6ad22171b2eb44fca353583112c
2024-10-23 14:21:14 +00:00
Kubernetes Publisher 9d850afe6f Merge pull request #127915 from omerap12/apiserver-util-PollUntilContextTimeout
Use PollUntilContextTimeout in flowcontrol

Kubernetes-commit: 9c430b2f7760eca131a56b9c6b96ca4a66d26bf8
2024-10-23 02:17:03 +00:00
Kubernetes Publisher 54b4c4f8a1 Merge pull request #124945 from hoskeri/remove-unused-field
Remove unused field from APIGroupVersion

Kubernetes-commit: 447fca25b1bf01894fca3bf296f4ffb67e359d42
2024-10-23 02:16:53 +00:00
Kubernetes Publisher c6b840aa24 Merge pull request #126900 from deveshgoyal1000/fix/typo-flowcontrol-request-width
Updated width.go

Kubernetes-commit: 43ad87b85a39344db31323b59c2eab4adc73b66e
2024-10-23 02:16:52 +00:00
Kubernetes Publisher d0f54d5993 Merge pull request #128172 from liggitt/3221-ga
KEP-3221: Promote StructuredAuthorizationConfiguration to GA

Kubernetes-commit: 79cca2786e037d8c8ae7fe856c5ae158b100ce71
2024-10-18 20:41:47 +00:00
Kubernetes Publisher ec228a4021 Merge pull request #128168 from liggitt/4601-beta
KEP-4601: AuthorizeNodeWithSelectors / AuthorizeWithSelectors to beta

Kubernetes-commit: d7bd7284035e7debd5406d375cd366b164cf358f
2024-10-18 20:41:46 +00:00
Kubernetes Publisher 1ea7befac3 Merge pull request #128165 from liggitt/prune-self-require
Drop self-referencing replace directives

Kubernetes-commit: a8fc7ae761c19ab436cf513c9eed877f08961cf7
2024-10-18 04:49:01 +00:00
Kubernetes Publisher 6600cfa976 Merge pull request #126347 from vinayakankugoyal/kep2862impl
KEP-2862: Fine-grained Kubelet API Authorization

Kubernetes-commit: f5ae0413cadb1d2d3d3d47857cdd65b4b6d194ba
2024-10-18 04:49:00 +00:00
Jordan Liggitt 08766af90d KEP-3221: Promote StructuredAuthorizationConfiguration to GA
Kubernetes-commit: ad808e609a599723cf17f7fcdfb73ca37bcf78fc
2024-10-17 21:48:30 -04:00
Kubernetes Publisher e784492b06 Merge pull request #127504 from sttts/sttts-authz-cel-compiler-once
apiserver/authconfig: make CEL compiler shareable

Kubernetes-commit: 90ee68948d23b9a22b3b48ecf819604bd4f27217
2024-10-18 00:49:07 +00:00
Kubernetes Publisher b907ccabbe Merge pull request #128144 from cheftako/updateANP
Bump konnectivity-client to v0.31.0

Kubernetes-commit: bd00406951a04967577d5ac7c2fbe7989f3653da
2024-10-18 00:49:06 +00:00
Kubernetes Publisher 08c82454ed Merge pull request #128120 from aojea/celmetrics
use same name as the folder for the package

Kubernetes-commit: 42b388b068b23b8ebf6e54e0fb4b9b1ddb22dea0
2024-10-18 00:49:05 +00:00
Jordan Liggitt 1e62dc23aa KEP-4601: AuthorizeNodeWithSelectors / AuthorizeWithSelectors to beta
Kubernetes-commit: 9caca7312645b5ffba964cc8170484b4e7f7b602
2024-10-17 19:51:07 -04:00
Jordan Liggitt d1d8d5e02d Drop self-referencing replace directives
Kubernetes-commit: 3be1109829d4b0921972bb8b5f66a4d179ff6255
2024-10-17 15:51:15 -04:00
Lukasz Szaszkiewicz aeae19662f Promote WatchList feature to Beta (#128053)
* e2e/apimachinery/watchlist: always run WatchList e2e tests

* kube-controller-manager: enable WatchListClient

* kube-apiserver: promote WatchList feature to beta

Kubernetes-commit: 06a15c5cf96131faaf44f93f1be228a013ae5c0d
2024-10-17 12:39:53 +00:00
Walter Fender 1f585b076f Bump konnectivity-client to v0.30.0
Bump konnectivity network proxy to v0.30.0.
Lease-based server counting logic for agent and lease controller for ANP server

reran hack/update-vendor.sh

Kubernetes-commit: f72c37418725457bc8ce1e58d40264bca05b6054
2024-10-16 22:30:10 -07:00
Kubernetes Publisher 608821ca42 Merge pull request #128136 from enj/enj/t/non_global_kms_kdf_via_name
kmsv2: run KDF tests in parallel

Kubernetes-commit: a8fd407d2f982267d847eeedc2d8097beedccc9e
2024-10-17 00:39:38 +00:00
Kubernetes Publisher c8517e5aa2 Merge pull request #128076 from AmarNathChary/Removed_Alpha_Beta_Ga
Removed_alpha_beta_ga

Kubernetes-commit: b7fb82f9d8b1c31724afe46d5a79569267a09bfe
2024-10-16 20:41:05 +00:00
Monis Khan 4f8e2cfe4c kmsv2: run KDF tests in parallel
This change updates the KDF "feature flag" to be per KMS provider
instead of global to the API server.  This allows integration tests
that use distinct provider names to run in parallel.

Locally this change reduced the runtime of
test/integration/controlplane/transformation by 3.5 minutes.

Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 43740c0def10f22a0ab7f522c1569188913b35a3
2024-10-16 15:01:35 -04:00
AmarNathChary 54c7d50dd6 removed_comments_from_kube_feature_of_alpha_beta_ga
Kubernetes-commit: ff7c708207738c80c7a1185faa20f7f84afe5fb3
2024-10-16 15:36:34 +05:30
Antonio Ojea 402e1cdd43 use same name as the folder for the package
Kubernetes-commit: 62bdb27420bbc3c45b0232612acf9ecf3816c805
2024-10-16 09:55:10 +00:00
Kubernetes Publisher 576854da2e Merge pull request #128064 from dims/update-to-last-versions-of-some-very-infrequently-updated-repos
Update to last versions of some very infrequently updated repos

Kubernetes-commit: 07e73682b9a5259cacac978346ed4567588e4165
2024-10-16 06:17:54 +00:00
Kubernetes Publisher d089101190 Merge pull request #128073 from seans3/tracing-config-strict-validation
TracingConfiguration is now validated strictly

Kubernetes-commit: 3694a02bb9b5c1dd216485d2e25bd2c8f24916dd
2024-10-15 18:16:20 +00:00
Kubernetes Publisher d52f63db87 Merge pull request #127982 from tkashem/refactor-store-decoder
KEP-3926: refactor: extract etcd3 store decode functions into an interface

Kubernetes-commit: d32e9b0b6996a439674a998d63ccebb5b1b4cbde
2024-10-15 10:12:42 +00:00
Kubernetes Publisher 44ff1c1665 Merge pull request #128038 from seans3/resource-quota-config-validation
Enable strict validation for ResourceQuotaConfiguration

Kubernetes-commit: 510a7e76018189b49e8d7c1ba4ac639bdf4dba37
2024-10-15 02:13:06 +00:00
Kubernetes Publisher 6ad2d93d2e Merge pull request #128011 from seans3/egress-selector-configuration-strict
EgressSelectorConfiguration now uses strict validation

Kubernetes-commit: 8b7b768ff78d73267e52c0828e1e0eb3f5328f28
2024-10-15 02:13:05 +00:00
Sean Sullivan e6b796812a TracingConfiguration is now validated strictly
Kubernetes-commit: a9c8061b1e4c2ac38c223fa6bf9c5c1bf8b01606
2024-10-14 18:09:16 -07:00
Davanum Srinivas 3ee34eb4ce Update to last versions of some very infrequently updated repos
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 7f8210e33f8cd626d1fb86cd7d12d077ec3047d5
2024-10-14 17:23:16 -04:00
Sean Sullivan 15f8ad59da Enable strict validation for ResourceQuotaConfiguration
Kubernetes-commit: a95c0c5499c54c16d561d0ad4537d4b43d500184
2024-10-13 17:50:18 -07:00
Sean Sullivan 285603b2d8 EgressSelectorConfiguration now uses strict validation
Kubernetes-commit: 32b2eea50d6457b4b1abf50e8e03be52e81d30c2
2024-10-11 16:17:09 -07:00
Kubernetes Publisher ebe463fe32 Merge pull request #127998 from skitt/golang-x-oct-2024
October 2024 golang.org/x bump

Kubernetes-commit: 8cbb11519c54c120e2dc120a4799e53abbfea4a4
2024-10-11 14:27:04 +00:00
Kubernetes Publisher b9c35c9189 Merge pull request #127985 from dims/update-moby-runc-dependencies-oct-10
Update moby/runc dependencies

Kubernetes-commit: 6e5e8f374e834fa8dab341bde5c522704ed55ba6
2024-10-11 14:27:02 +00:00
Stephen Kitt 042b9763e0 October 2024 golang.org/x bump
Nothing major here, but nothing liable to cause pain to downstreams
either.

* https://github.com/golang/crypto/compare/v0.26.0...v0.28.0 (there’s
  a SHA3 fix there but it’s only relevant for 32-bit platforms)
* https://github.com/golang/net/compare/v0.28.0...v0.30.0 (mostly
  http2; route address parsing fix on Darwin)
* https://github.com/golang/oauth2/compare/v0.21.0...v0.23.0 (Google
  license fix)
* https://github.com/golang/sys/compare/v0.23.0...v0.26.0 (faster
  getrandom() on Linux through the vDSO; improved RISC-V support)
* https://github.com/golang/term/compare/v0.23.0...v0.25.0
* https://github.com/golang/time/compare/v0.3.0...v0.7.0 (0-limit
  handling fix in x/time/rate; Google license fix)
* https://github.com/golang/tools/compare/v0.24.0...v0.26.0

This doesn’t include golang.org/x/exp; that doesn’t have any relevant
changes. There’s an apidiff fix but we always pull in the latest
apidiff anyway.

Signed-off-by: Stephen Kitt <skitt@redhat.com>

Kubernetes-commit: 6c5a528727c30803d4426b29c06ae5d350619877
2024-10-11 10:22:13 +02:00
Davanum Srinivas 33a5883440 Update moby/runc dependencies
Signed-off-by: Davanum Srinivas <davanum@gmail.com>

Kubernetes-commit: 521f2d106b9c5744ce57a8ec03124bcdadbca986
2024-10-10 11:58:22 -04:00
Kubernetes Publisher 23e33fe7dc Merge pull request #127984 from tkashem/refactor-storage-internal-error
KEP-4795: storage.InternalError should retain the inner error

Kubernetes-commit: 1c45ca8059a29f86802a995ebcdb2635fba9c129
2024-10-11 02:11:52 +00:00
Kubernetes Publisher 26cc27763e Merge pull request #127942 from liggitt/json123
sigs.k8s.io/json go 1.23 bump

Kubernetes-commit: fc318e3ba4cb1db822d7f802993059846f64179d
2024-10-10 22:15:15 +00:00
Kubernetes Publisher f14df2d4fd Merge pull request #127777 from tkashem/refactor-delete-option
KEP-4795: refactor: add delete options for Delete method in storage interface

Kubernetes-commit: d88b4e3b6e34a85f58778b7ef96e64edffff6823
2024-10-10 18:08:27 +00:00
Jordan Liggitt 810761bf3d Update sigs.k8s.io/json to go1.23
Kubernetes-commit: 8eff759b6ac7c3bb0c6a8823c751f5a578d6f721
2024-10-10 11:00:11 -04:00
Kubernetes Publisher 664cde1f32 Merge pull request #127778 from tkashem/refactor-conditional-delete
KEP-4795: refactor: etcd store conditional delete

Kubernetes-commit: daf76e6ead71524c86d4b35cee935206078acf32
2024-10-10 14:46:22 +01:00
Omer Aplatony 965243019f Use PollUntilContextTimeout in flowcontrol
Signed-off-by: Omer Aplatony <omerap12@gmail.com>

Kubernetes-commit: 9fe3f368b1813b491f9b1265547235f309ff87e9
2024-10-08 10:25:15 +03:00
Vinayak Goyal 9da53c5ba3 KEP-2862: Fine-grained Kubelet API Authorization
Signed-off-by: Vinayak Goyal <vinaygo@google.com>

Kubernetes-commit: b1f290d444f75b351f77f4d3292bbf557ae1cc45
2024-09-29 04:02:55 +00:00
Abu Kashem 33cfd964ef refactor: storage.InternalError should retain the inner error
This is so we can do proper error handling, at the same time
we want to maintain backward compatibility

Kubernetes-commit: f24ec7e00dfd133780b3e6519a19f646c0dbde63
2024-09-26 09:44:09 -04:00
Dr. Stefan Schimanski bf20cce32f apiserver/validation: fix some sets.NewString deprecations
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@gmail.com>

Kubernetes-commit: c44fc280871f1c4de177ed70367afcc9a9b06175
2024-09-25 11:39:53 +02:00
Abu Kashem 14881364b3 refactor: extract decode functions into an interface for etcd3 store
Kubernetes-commit: 1d1a656d8de1cdb99deaa6ec771aa354616eaa16
2024-09-24 07:19:19 -04:00
Dr. Stefan Schimanski 4b46916a7b apiserver/authconfig: wire CEL compiler through lower layers to allow sharing
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@gmail.com>

Kubernetes-commit: 4024390d8c8a19056ab7ced95eef5cce43c8096d
2024-09-20 12:34:08 +02:00
Abu Kashem f28acc6161 refactor etcd store conditional delete
Kubernetes-commit: fecab0713b96bb0d528aea58900942ae0cb52260
2024-09-19 13:14:52 -04:00
Kubernetes Publisher bd937b2b8b Merge pull request #122923 from tkashem/timeout-documenting-test
document behavior of per handler read/write timeout with test(s)

Kubernetes-commit: a6ea7b8218d01b504fda2ef4988627fe4194fcaa
2024-10-10 10:08:03 +00:00
Kubernetes Publisher f45e42a091 Merge pull request #127302 from cici37/costFG
Promote cost related feature gate to default true

Kubernetes-commit: 78d64904128add672b9f5d48d676c0bdc6a1c2de
2024-10-10 02:09:07 +00:00
Kubernetes Publisher 8602a60374 Merge pull request #127909 from richabanker/mvp-cleanup
Reduce IdentityLeaseRenewIntervalPeriod in peer_proxy test

Kubernetes-commit: d9c46d8ecb1ede9be30545c9803e17682fcc4b50
2024-10-09 14:53:00 +00:00
Richa Banker 50dafb8926 reset err in resolveServingLocation() when the req can be served by local apiserver
Kubernetes-commit: 6e66a359822a988f9acd7062c3731559535abcd5
2024-09-27 16:54:08 -07:00
Kubernetes Publisher c2070002ce Merge pull request #127905 from cici37/revert-127400
Revert pr 127400 due to regression

Kubernetes-commit: ea13c5e6a28c6c233f8158c0739e03bc35fb9eee
2024-10-07 22:31:17 +00:00
Kubernetes Publisher 7202dc017c Merge pull request #127902 from p0lyn0mial/upstream-system-authenticated-for-system-apiserver
server/config: assing system:apiserver user to system:authenticated group

Kubernetes-commit: 815532749c551413f8b9d9be0ae1f24cb3a1e871
2024-10-07 18:31:28 +00:00
Cici Huang f79926bf43 Revert pr 127400
Kubernetes-commit: 95ec1637253c2458d0ed41f257df932907602ca4
2024-10-07 16:11:30 +00:00
Lukasz Szaszkiewicz 3257d91a56 server/config: assing system:apiserver user to system:authenticated group
Kubernetes-commit: dfeb560d4a2294c7a9a6233e2acb6b27039ed37a
2024-10-07 17:39:10 +02:00
Kubernetes Publisher 5bdd80ce60 Merge pull request #127816 from cici37/updateCEL
Bump dependency: cel-go to v0.21.0

Kubernetes-commit: 9ffefe3da28a09e250b6517b0f2d5f3437b18265
2024-10-04 02:25:42 +00:00
Kubernetes Publisher d30adf3e28 Merge pull request #122957 from richabanker/uvip-bugfix
MVP cleanup #2

Kubernetes-commit: 3660a34d21492a2bdbb992c435554af0fa457e2d
2024-10-03 02:10:25 +00:00
Cici Huang be240164d1 Update cel-go to v0.21.0
Kubernetes-commit: 80c0c2c32eae71c1bf948266da413c4eb24f12c5
2024-10-02 17:15:58 +00:00
Cici Huang 0381d1eed6 Promote cost enforcement feature gates to GA
Kubernetes-commit: 4c64aa7a4eda6c379a3dec061dfef5beb311d66a
2024-09-26 16:39:18 +00:00
Abu Kashem 3a0975b22c refactor: add delete options for Delete method in storage interface
Kubernetes-commit: bc0ea34bc380e073a7278216fe6690a1b9aee48c
2024-09-19 10:31:08 -04:00
Mangirdas Judeikis 3e52ced45d fully remove StopCh
Signed-off-by: Mangirdas Judeikis <mangirdas@judeikis.lt>

Kubernetes-commit: fd76176811654c409cd6799de4a174bcb051a90d
2024-09-13 12:08:57 +03:00
Abu Kashem 7b1424d930 fixup! add test to document behavior of net/http read/write deadline
Kubernetes-commit: f91cdf768dcd893e2d18a5705071b899f979374d
2024-08-30 11:05:23 -04:00
Devesh Goyal b2a5ee84cf Updated width.go
Kubernetes-commit: ed0c2b4f3d33fe7f165ff2c39034ca062c75f964
2024-08-25 02:12:29 +05:30
Abu Kashem 85ab93cd5f add test to document behavior of net/http read/write deadline
Kubernetes-commit: 2abe3a5dfab04d22362aafd8a8e9f0da80c419b0
2024-06-18 16:33:52 -04:00
Abhijit Hoskeri b4437b251e Remove unused field from APIGroupVersion
RootScopedKinds is only present in unit tests, and
is not referenced anywhere else.

Kubernetes-commit: 90c49ab2eda2f786d143117f6f2133f1fa90af34
2024-05-18 12:47:42 -07:00
carlory 21560784da Add quota support for PVC with VolumeAttributesClass
Signed-off-by: carlory <baofa.fan@daocloud.io>

Kubernetes-commit: 27706a0b54cadf1ec2f1f0850ac8ab8f2f1d1c5b
2024-04-18 15:31:12 +08:00
Richa Banker 10dc325a69 Add flagz implementation and enablement in apiserver
Kubernetes-commit: da8dc433e9b5fb29f3cc91f79ed15f366addefd1
2024-03-28 19:41:00 -07:00
Krzysztof Dąbrowski 2146712740 apiserver: decrease timeout for TestKMSHealthzEndpoint
Kubernetes-commit: ee3e9a7bd448b26f49e07a89247276687b8a535d
2024-03-27 20:58:35 +01:00
Richa Banker df45eb6de5 refactor peerproxy_handler and add unit test
Kubernetes-commit: 9c65b79ea3b81859698f0f2613c82de99154755d
2024-01-24 19:48:51 -08:00
Kubernetes Publisher 08c52b99b8 Merge pull request #127764 from Jefftree/responsewriter-refactor
Move InMemoryResponseWriter into own package

Kubernetes-commit: 1209a7777f034e43104f0329b23e2c96f7496e79
2024-10-01 22:12:15 +00:00
Kubernetes Publisher 2861287f95 Merge pull request #127718 from richabanker/mvp-cleanup
MVP cleanup

Kubernetes-commit: cf870bb7fc41a8d1b38cb9748f9b3ca92ba51e28
2024-10-01 22:12:14 +00:00
Lukasz Szaszkiewicz 36e57697d1 apiserver/handlers/watch: encode initialEventsListBlueprint with watchEncoder (#127587)
* apiserver/handlers/get: construct versionedList

* storage/cacher: document caching the serialization of bookmark events

* endpoints/handlers/response: add watchListTransformer

* endpoints/handlers/watch: wire watchListTransformer

Kubernetes-commit: fbf1a0dc181ccbeb9925ad9c284d913a25c16562
2024-10-01 11:55:50 +00:00
Kubernetes Publisher d7cbe7a5f2 Merge pull request #127670 from Jefftree/port-kcm-feature
Port all remaining features to versioned

Kubernetes-commit: 2fb37e2489133edbbe8f318f920ef3515b0c8d58
2024-10-01 07:37:33 +00:00
Kubernetes Publisher b16c96d150 Merge pull request #127029 from tkashem/apf-fix-watch-panic-handling
apf: request handler must wait for watch init goroutine to return

Kubernetes-commit: 8bc073a5fe7415099f936f22ff92c96391b9c0c8
2024-10-01 07:37:32 +00:00
Jefftree f900b71720 Move inmemoryresponsewriter into own package
Kubernetes-commit: f67e15a26f5958b1ebf07a2590e98889a987677d
2024-09-30 21:11:53 +00:00
Kubernetes Publisher 60d1ca6725 Merge pull request #127690 from mmorel-35/testifylint/expected-actual@k8s.io/apiserver
fix: enable expected-actual rule from testifylint in module `k8s.io/apiserver`

Kubernetes-commit: 5006caadc8860e542339f4cd3851b6beb3504059
2024-09-29 04:37:57 +00:00
Kubernetes Publisher efdbeb541a Merge pull request #127683 from mmorel-35/testifylint/nil-compare@k8s.io/apiserver
fix: enable error-nil and nil-compare rules from testifylint in module `k8s.io/apiserver`

Kubernetes-commit: dad39bf4357dc89e105b80e16b8da5906406eb53
2024-09-28 20:40:07 +00:00
Kubernetes Publisher ad232ba084 Merge pull request #127671 from mmorel-35/testify/error-contains
fix: use `ErrorContains(t, err` instead of `Contains(t, err.Error()`

Kubernetes-commit: e34f7f4d80e6e635dfd125fb266eaafa02a213c9
2024-09-28 20:40:01 +00:00
Kubernetes Publisher 42502d245d Merge pull request #126764 from liggitt/mergo
reimplement merge to drop mergo dependency

Kubernetes-commit: ee74baec6e05afde972f1a8705d4f8efe066f120
2024-09-28 08:41:33 +00:00
Kubernetes Publisher 163594e62d Merge pull request #127536 from mmorel-35/testifylint/contains@k8s.io/apiserver
fix: enable contains rule from testifylint in module `k8s.io/apiserver`

Kubernetes-commit: 65ee79ee2c0eb538e545dd31fa4ee1bea39f1933
2024-09-28 04:47:10 +00:00
Richa Banker 8c1054e51e use structured logging wherever possible
Kubernetes-commit: 45a47d634569acad9022733ba35131088e9575e6
2024-09-27 16:54:08 -07:00
Richa Banker 40680bab4c Do not mark errorFetchingAddressFromLease, in case when the there's no address being fetched from the lease object
Kubernetes-commit: ac3fd5c634d2d9fd2e4011294f152e64f72d205b
2024-09-27 16:37:46 -07:00
Jefftree 38adb499b0 Port the rest of unversioned features
Kubernetes-commit: a8390dcddda14274e5ce24dd517a19587118a198
2024-09-27 19:04:47 +00:00
Kubernetes Publisher 326460d62e Merge pull request #127551 from carlory/fix-trace-context
introduce a proper trace context

Kubernetes-commit: 3d6c5b2e98afaaae1d17107e2d3d709c726be49d
2024-09-27 12:38:49 +00:00
Matthieu MOREL e32a42cf11 fix: enable expected-actual rule from testifylint in module `k8s.io/apiserver`
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: fbd773ecb82aa0afef3c02274db901afe1788220
2024-09-27 07:49:07 +02:00
Matthieu MOREL aaeee2a449 fix: enable error-nil and nil-compare rules from testifylint in module `k8s.io/apiserver`
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: 09274182e225fcf4df9e4a3d54fd96fc03de3bdf
2024-09-27 07:29:09 +02:00
Matthieu MOREL 17d29da911 fix: use `ErrorContains(t, err` instead of `Contains(t, err.Error()`
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: f777addb05aed8c2861c423fb29946e85151ecf6
2024-09-26 22:22:20 +02:00
Kubernetes Publisher d9db30ee93 Merge pull request #127647 from mmorel-35/testifylint/formatter@k8s.io/apiserver
fix: enable formatter rule from testifylint in module `k8s.io/apiserver`

Kubernetes-commit: c89205f7d62791230ba2d3e5f7610f515b277b69
2024-09-26 16:37:34 +00:00
Kubernetes Publisher afd0abad01 Merge pull request #127012 from Chaunceyctx/new-send-bookmark
send bookmark right now after sending all items in watchCache store

Kubernetes-commit: 81ebfb3d0c6194c80aa8656c06ab6fcc64535e99
2024-09-26 08:36:47 +00:00
Kubernetes Publisher 18cfc4e4d4 Merge pull request #127200 from omerap12/version_fg_apiserver
chore: moving apiserver featuregates to versioned.

Kubernetes-commit: b62d364195fefbe2604d07b4fa6c220fe3df5e4d
2024-09-26 04:36:40 +00:00
Lukasz Szaszkiewicz 8cb411e993 adds watchListEndpointRestrictions for watchlist requests (#126996)
* endpoints/handlers/get: intro watchListEndpointRestrictions

* consistencydetector/list_data_consistency_detector: expose IsDataConsistencyDetectionForListEnabled

* e2e/watchlist: extract common function for adding unstructured secrets

* e2e/watchlist: new e2e scenarios for convering watchListEndpointRestrict

Kubernetes-commit: ae35048cb0b9b177891aab41346b6d6cc504582f
2024-09-25 12:48:33 +00:00
Omer Aplatony 6dc6d8d7fa chore: moving apiserver featuregates to versioned
Signed-off-by: Omer Aplatony <omerap12@gmail.com>

Kubernetes-commit: ade730594005f93ac18e102ba54d61dbf23b616f
2024-09-24 23:36:30 +03:00
Kubernetes Publisher 11af6bbc46 Merge pull request #127093 from jpbetz/retry-generate-name-ga
Promote RetryGenerateName to GA

Kubernetes-commit: 1137a6a0cc98c49d2076461ce3d741a619a129e8
2024-09-24 00:38:00 +00:00
Kubernetes Publisher 763c8220a6 Merge pull request #127500 from p0lyn0mial/upstream-assign-rv-to-watchCacheInterval
cacher: prevents sending events with ResourceVersion < RequiredResourceVersion

Kubernetes-commit: c9d6fd9ff77f43363898362ec71796dafeb89929
2024-09-23 12:36:42 +00:00
Kubernetes Publisher c34f5e1bba Merge pull request #127323 from vrutkovs/tracing-cacher-get
tracing: add span for get cacher

Kubernetes-commit: 15d08bf7c8813b0533dc147a03d9f42aae735ecd
2024-09-23 12:36:41 +00:00
Kubernetes Publisher bcc457994e Merge pull request #127540 from mmorel-35/testifylint/error-is-as@k8s.io/apiserver
fix: enable error-is-as rule from testifylint in module `k8s.io/apiserver`

Kubernetes-commit: df5787a57fe391b75767028ea2a0255c82185680
2024-09-23 08:34:40 +00:00
Kubernetes Publisher 1e85cd3163 Merge pull request #127524 from mjudeikis/mjudeikis/extend.group.manager
Add GroupLister interface to discovery GroupManager

Kubernetes-commit: 19500e8551c478b7a30d822f2f8a668d361069cb
2024-09-23 08:34:38 +00:00
Kubernetes Publisher de96004cf6 Merge pull request #126799 from kiashok/update-cadvisor-hcsshim
Update cadvisor and hcsshim versions

Kubernetes-commit: 4c2e23904794a289be22b2c9bce3303edfa0f622
2024-09-23 04:35:47 +00:00
Kubernetes Publisher 8a3f4fc910 Merge pull request #127529 from mmorel-35/testifylint/compares@k8s.io/apiserver
fix: enable compares rule from testifylint in module k8s.io/apiserver

Kubernetes-commit: f7085634de23cafc7010b9404ec2e0d2c44a35c2
2024-09-23 00:37:04 +00:00
Matthieu MOREL b489c4ea4e fix: enable error-is-as rule from testifylint in module `k8s.io/apiserver`
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: 0dfc6e2843f913b26c9382144fd2381f4b536bf0
2024-09-22 12:21:19 +02:00
Matthieu MOREL c6ba9681f7 fix: enable contains rule from testifylint in module `k8s.io/apiserver`
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: 3b92b9f84d62550a62890d8d3f6a57d96fc70ccc
2024-09-22 11:57:24 +02:00
Matthieu MOREL 9996ba35fd fix: enable compares rule from testifylint in module k8s.io/apiserver
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: 6e82050ef0e8eae2c31e43b402646d7591e5eca4
2024-09-21 22:18:56 +02:00
Mangirdas Judeikis b09ab6e398 Add GroupLister interface to discovery GroupManager
Signed-off-by: Mangirdas Judeikis <mangirdas@judeikis.lt>

Kubernetes-commit: ee55200440c8236248f47cbe2dd783ba1a717614
2024-09-21 18:43:56 +03:00
Kubernetes Publisher 4509919d9b Merge pull request #126760 from ncdc/ncdc/emeritus
Move ncdc to emeritus

Kubernetes-commit: f9a57ba82d05a8b9f442092f0b391ae8bd74287c
2024-09-20 20:44:41 +00:00
Kubernetes Publisher 3f8a6ad61c Merge pull request #127422 from srivastav-abhishek/go-vet-fix
Go vet fixes for gotip

Kubernetes-commit: f2700895a4315014c56bfe1db7fb1562a61a8b50
2024-09-20 16:44:25 +00:00
Kubernetes Publisher 7d77d3c2dd Merge pull request #127493 from p0lyn0mial/upstream-deflake-cacher-dont-accept-requests
storage/cacher/cacher_whitebox_test:deflake TestCacherDontAcceptRequestsStopped when ResilientWatchCacheInitialization is off

Kubernetes-commit: 21f54c3ca1ac87611e101f46d8b05682f51efbee
2024-09-20 12:39:23 +00:00
Kubernetes Publisher 1a9bf77ab1 Merge pull request #127492 from p0lyn0mial/upstream-deflake-consistent-read-fallback
pkg/storage/cacher/cacher_whitebox_test: deflake TestConsistentReadFallback when ResilientWatchCacheInitialization is off

Kubernetes-commit: 65c77b7e3bc3f3c206e1b4a82feeb1a2c6678b45
2024-09-20 09:37:42 +01:00
Lukasz Szaszkiewicz cdd40a3d24 pkg/storage/testing/watcher_tests: RunWatchSemantics checks if the storage has been primed with init data
Kubernetes-commit: e7e2123feb2f1957be490a21aa606cb2dc718432
2024-09-20 09:37:53 +02:00
Lukasz Szaszkiewicz eb3a20ad8b storage/cacher/cache_watcher: processInterval sets RV from the snapshot
Kubernetes-commit: de735be512767dd2eced78530693d2e3ae997e6e
2024-09-20 08:48:31 +02:00
Lukasz Szaszkiewicz 743b5776f9 storage/cacher/cache_watcher: add RV to watchCacheInterval
Kubernetes-commit: f87e4a19c88fa908eb176ee7925f211bafba9b45
2024-09-20 08:47:49 +02:00
Lukasz Szaszkiewicz 87a5cdebbc storage/cacher/cacher_whitebox_test:deflake TestCacherDontAcceptRequestsStopped when ResilientWatchCacheInitialization is off
Kubernetes-commit: 5b7faca6b66dc0664f36c611867f5799377b0514
2024-09-20 06:40:47 +02:00
Lukasz Szaszkiewicz bafce892d3 pkg/storage/cacher/cacher_whitebox_test: deflake TestConsistentReadFallback when ResilientWatchCacheInitialization is off
Kubernetes-commit: 077c35ee82e40fa808fa4b45b86d843cded3cfa3
2024-09-20 06:15:22 +02:00
Kubernetes Publisher f5a73ba0a4 Merge pull request #127244 from Adarsh-verma-14/fix-comment
Fix inconsistency between the comment and the actual feature gate definition

Kubernetes-commit: be8ea98a5c9c1c4a942ccd09102e3e263385fc60
2024-09-19 20:37:13 +00:00
Kubernetes Publisher e70087b968 Merge pull request #127161 from Jefftree/duplicate-feature-inplacepod
remove duplicate unused feature InPlacePodVerticalScaling

Kubernetes-commit: cf1468dfeb47fa91530ae0dc29b98b7e79e9062c
2024-09-19 20:37:11 +00:00
Joe Betz a84f81807f Preserve testing of RetryGenerateName feature disablement
Kubernetes-commit: 0d14bac9a606cd49e6c346c1220ebdbf1b18fa09
2024-09-19 11:34:59 -04:00
Kubernetes Publisher 7e60e39b7b Merge pull request #124792 from mjudeikis/mjudeikis/ctx.wiring
Wire in ctx into rbac plugins

Kubernetes-commit: 6f1583990a6ad0acec1d8a418eab977fee586076
2024-09-18 08:37:39 +00:00
Kubernetes Publisher 87a949c64a Merge pull request #126977 from aaron-prindle/compat-version-132
chore: bump DefaultKubeBinaryVersion to 1.32, make 1.32 CEL changes, fix int tests to handle 1 version off API deprecation, and fix prerelease-lifecycle-gen for # of APIs

Kubernetes-commit: 920e9e34b07d0930a28b192b5cdc912e79d8562b
2024-09-18 00:47:09 +00:00
Kubernetes Publisher 71b1c7e730 Merge pull request #127400 from sttts/sttts-webhook-cel-compiler
apiserver/admission/webhook: construct static CEL compiler only once

Kubernetes-commit: 3baaac6286d6dab50704a2c94fe92dce804d28da
2024-09-17 12:36:37 +00:00
Kubernetes Publisher b0e4675775 Merge pull request #127279 from serathius/etcd-v3.5.16
Upgrade etcd client to v3.5.16

Kubernetes-commit: 7448e676aa0fc96eac53f159c03ca633823e7ff6
2024-09-16 23:44:11 +00:00
Dr. Stefan Schimanski 9a5bc0a561 apiserver/admission/webhook: construct static CEL compiler only once
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@gmail.com>

Kubernetes-commit: 26aeda3cc21db86b1440515f63cd09e2321d568e
2024-09-16 18:23:01 +02:00
Kubernetes Publisher ac5c364aba Merge pull request #125186 from liyuerich/newserializer
drop deprecated json/yaml newSerializers, use json.NewSerializerWithO…

Kubernetes-commit: e5dd48efd07e8a052604b3073e0fafe7361ca689
2024-09-13 15:50:00 +00:00
Mangirdas Judeikis 07be2984cd wire in ctx to rbac plugins
Kubernetes-commit: 4e4eb8c5c95652b4cbe672a02e4077a93d0bfe2d
2024-09-13 12:03:47 +03:00
Kubernetes Publisher d6db44ddd4 Merge pull request #127314 from xuzhenglun/cohabitating-ut
chore:  add unit test to cover cohabitating resources in StorageFactory

Kubernetes-commit: e35bd86edd66a956e4de409f207bced2c66efb7a
2024-09-12 19:43:53 +00:00
Abhishek Kr Srivastav 17ab6c21d5 Fix Go vet errors for master golang
Co-authored-by: Rajalakshmi-Girish <rajalakshmi.girish1@ibm.com>
Co-authored-by: Abhishek Kr Srivastav <Abhishek.kr.srivastav@ibm.com>

Kubernetes-commit: 95860cff1c418ea6f5494e4a6168e7acd1c390ec
2024-09-12 18:15:22 +05:30
Vadim Rutkovsky 7da1f042b3 tracing: add span for cacher.Get
Also updates tracing integration tests for cacher.GetList

Kubernetes-commit: dff0075e7cd687f77fd38337e9ba487bb2437ecc
2024-09-12 14:18:40 +02:00
xuzhenglun 3abc312093 add test coverage for cohabitating resources in StroageFactory
Kubernetes-commit: d994895dc4580101f17661eb573fc282d4b8517d
2024-09-12 16:59:08 +08:00
Kubernetes Publisher 6ab385d467 Merge pull request #127271 from liggitt/go1.23
Update go.mod for go 1.23

Kubernetes-commit: c775fb2238e1ed48f62f02898bbb3ecee993e044
2024-09-12 04:18:51 +00:00
Kubernetes Publisher b95faaac20 Merge pull request #127239 from xuzhenglun/cohabitating-resources
API emulation versioning honors cohabitating resources

Kubernetes-commit: 6309127d696828d79cc8745e22f3f82f0d9f65e2
2024-09-11 23:43:48 +00:00
Kubernetes Publisher 438d2ea372 Merge pull request #126368 from jpbetz/organize-cel-libraries
Improve structure of CEL libraries to ensure cost tests kept accurate with introduction of new types

Kubernetes-commit: e3a81ab0005973f25d498308702f826795c6d8c2
2024-09-11 23:43:47 +00:00
Kubernetes Publisher 14b3ab9aa0 Merge pull request #127186 from Adarsh-verma-14/remove-duplication
remove the feature flag duplicate definition from pkg/features/kube_features.go

Kubernetes-commit: 0aea469b96a1c0fed48dd2d4a0af96d36e24968c
2024-09-10 23:48:24 +00:00
Joe Betz 206e39c6f1 Test library and type names
Kubernetes-commit: 430b1de921b85611b409887fe94988f81ec4d39f
2024-09-10 17:07:29 -04:00
Joe Betz c90ac4722a Move CEL semver library into common libs, fix cost tests to use registered types
Kubernetes-commit: e085f3818a3a1d04d895532cbdd233d797e0913b
2024-09-10 16:55:57 -04:00
Marek Siarkowicz 0b2ba575d2 Upgrade etcd client to v3.5.16
Kubernetes-commit: 27e0ece976d38833ceec5dea9fb3e46f8dc083d4
2024-09-10 22:48:11 +02:00
Kubernetes Publisher d3a58ddf5a Merge pull request #127254 from liggitt/test-filter
Fix unit tests for filtering

Kubernetes-commit: 139cc3c659dd1624f7a4bbcc3b07fda79539677a
2024-09-10 19:45:32 +00:00
Joe Betz 91a4bf232d Promote RetryGenerateName to GA
Kubernetes-commit: e3cae09e63d72edef9cf841979418291acc31b17
2024-09-10 12:34:36 -04:00
Jordan Liggitt d3f16fa83a Pin godebug default to go1.23
Kubernetes-commit: 102a9dbab1764e8793d0237b25143fa49cd96831
2024-09-10 12:22:40 -04:00
Jordan Liggitt 9f2124ae21 Update go.mod to go 1.23
Kubernetes-commit: 65ef53139012dee36c08f558604dea48af170e11
2024-09-10 12:07:06 -04:00
Kubernetes Publisher fbad808f69 Merge pull request #127265 from p0lyn0mial/upstream-watchlist-matchsingle-recursive
cacher: apply key for initial events only if the call is not recursive

Kubernetes-commit: 7b3589151285716cd7b0a002bab9f73c32c286df
2024-09-10 12:08:23 +01:00
Lukasz Szaszkiewicz 8a764cf9c3 cacher: apply key for initial events only if the call is not recursive
Kubernetes-commit: 7cb51b1c278f9eb57c43f929fcc80bfed8438e17
2024-09-10 10:58:26 +02:00
Jordan Liggitt f014f4a768 Fix unit tests for filtering
Kubernetes-commit: 6a4170607291288e9b01be8435b82537309c547d
2024-09-09 16:45:32 -04:00
Adarsh-verma-14 5db4826844 fixing inconsistency between the comment and the actual feature gate definition
Kubernetes-commit: 8f471803cb386c2a227fa61e922822aab168ec95
2024-09-09 18:33:00 +05:30
xuzhenglun a096b0dd8d API emulation versioning honors cohabitating resources
Kubernetes-commit: cd5b27304f58f6b634be800ec4ec9990d28550a3
2024-09-09 17:54:35 +08:00
Kubernetes Publisher 197cd431b1 Merge pull request #125960 from pohly/dep-logging
dependencies: logr v1.4.2, zap v1.27.0

Kubernetes-commit: a8d4eb60097863210b89063003e537253cf091a8
2024-09-06 19:44:52 +00:00
Adarsh-verma-14 8a5b9105e9 add missing comment
Kubernetes-commit: 8619996319a07d5c5f777b6e06f54ce3884a73b4
2024-09-07 00:07:31 +05:30
Aaron Prindle fc23f87960 chore: bump DefaultKubeBinaryVersion to 1.32, make 1.32 CEL changes, fix int tests to handle 1 version off API deprecation, and fix prerelease-lifecycle-gen for # of APIs
Kubernetes-commit: 701e6c7ab191cb75824378b4a2aceac67be35462
2024-09-05 21:37:38 +00:00
Jefftree b93ecaaa38 remove duplicate unused feature InPlacePodVerticalScaling
Kubernetes-commit: 14fe8e2a91ba5f6fc53617bea32223d79e1a060c
2024-09-05 16:27:07 +00:00
Abu Kashem 4483cc97cf apiserver: all bookkeeping must complete before apf handler returns
all bookkeeping must complete before the apf handler returns,
whether it panics or returns normally

Kubernetes-commit: 71d9307eaeda86d6a205548ecdeb7fbf226d7d82
2024-09-05 12:01:36 -04:00
Abu Kashem a785076ef7 apiserver: fix apf watch test
the assert to verify that 'atomicReadOnlyExecuting' is zero
should be executed if the apf handler panics, all apf
bookkeeping must be completed before the handler returns

Kubernetes-commit: 0c8632de57075191e6c4e34897fb7871034c7081
2024-09-05 11:57:47 -04:00
Kubernetes Publisher 1d107b22c5 Merge pull request #115834 from stlaz/remote-uid
RequestHeader authentication: add UID to recognized request headers

Kubernetes-commit: 9f01cd7b28fdbc8a1ceb9ec371fd817551659ee5
2024-09-05 15:51:36 +00:00
Kirtana Ashok d076be3784 Update cadvisor and hcsshim versions
Signed-off-by: Kirtana Ashok <kiashok@microsoft.com>

Kubernetes-commit: 3fba9930b72f78b53fb83b0d533a426080c0f92e
2024-09-05 08:35:53 -07:00
Chaunceyctx f7eddd4bda send bookmark right now after sending all items in watchCache store
Kubernetes-commit: 723920253349ee3c272c5b5a77e9d19548c1533c
2024-08-29 17:31:36 +08:00
Joe Betz 6999423628 Add equality cost checking
Kubernetes-commit: 0a2dfba067d7c75fafb9844f3cf4539153b582cf
2024-08-27 14:42:58 -04:00
Andy Goldstein 614f663b4a Use emeritus_*
Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>

Kubernetes-commit: 0e228be96f352359847d013ff889522524e1cb98
2024-08-22 17:48:27 -04:00
Jordan Liggitt 0298bf965f Update vendor
Kubernetes-commit: 745ae75a15cad2f1c5da5518c00f2eb366ffb786
2024-08-17 21:42:00 -04:00
Andy Goldstein 1ccea00339 Move ncdc to emeritus
I am moving myself to emeritus as I am now firmly on the end-user side
of things.

Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>

Kubernetes-commit: 3ab816dcabf37acda33c665ab5aa85f1f6163bc1
2024-08-17 13:00:07 -04:00
Joe Betz 7896cd7b57 add a type for each CEL library, register all types
Kubernetes-commit: d2affe304847aa0bef3f81fa622d0b9c70a7f975
2024-07-25 16:33:18 -04:00
Matthieu MOREL 33d4365d5b fix: enable formatter rule from testifylint in module `k8s.io/apiserver`
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

Kubernetes-commit: 58d5acd59870219f11721eb46103d1764884bef1
2024-07-14 07:58:03 +00:00
Patrick Ohly d7c9221467 dependencies: logr v1.4.2, zap v1.27.0
No particular reason for updating besides staying up-to-date.

Kubernetes-commit: 861ed2d2feeca187a32dcf793c9e3d5ab21bfdfc
2024-07-08 17:51:59 +02:00
liyuerich 535f09400e drop deprecated json/yaml newSerializers, use json.NewSerializerWithOptions instead
Signed-off-by: liyuerich <yue.li@daocloud.io>

Kubernetes-commit: 3c9309db463679c348934429d8487d190ed5e64a
2024-05-29 19:00:00 +08:00
Stanislav Láznička cf15325096 delegate authn: don't default the ReqHeaders UID header
Kubernetes-commit: 26902de531620d2df5ce1bb572d2ea6965a7b7e9
2024-05-20 13:43:22 +02:00
CasperLiu 1bb7b2e6ab introduce a proper trace context
Signed-off-by: carlory <baofa.fan@daocloud.io>
Co-authored-by: CasperLiu <qiuyuzhe521@gmail.com>

Kubernetes-commit: 5b2632f70763aeadfc334df1364946fe39fc10bb
2024-04-07 17:58:59 +08:00
sxllwx e6df86ea96 Fix API server crash on concurrent map iteration and write
Improve audit context handling by encapsulating event data and operations behind a structured API. Make
the Audit system more robust in concurrent environments by properly isolating mutable state. The cleaner
API simplifies interaction with audit events, improving maintainability. Encapsulation reduces bugs
by preventing direct manipulation of audit events.

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
Co-Authored-By: Jordan Liggitt <liggitt@google.com>
Co-Authored-By: sxllwx <scottwangsxll@gmail.com>

Kubernetes-commit: 75afa1e0acfb309d984be14937a06f796f220cd6
2023-10-23 20:59:40 +08:00
Jordan Liggitt 1bac1a3a1c prevent deletionTimestamp from moving into the past
Kubernetes-commit: 88090c49732ff53ce09d4872b6029e21d33361ba
2023-09-02 13:09:42 -04:00
Stanislav Láznička 60f20c32c7 client-go: add the UID to the auth-proxy roundtripper
Kubernetes-commit: 2cc0370169ea1fcf45429f9586e0ffd4ab32ed26
2023-02-16 14:01:53 +01:00
Stanislav Láznička b9e6a66c69 requestheaders: add a "requestheader-uid-headers" flag and wire it up
Kubernetes-commit: 7fabd06c2be41f4134f425fa967d79ac31dc5756
2023-02-16 11:28:50 +01:00
Kubernetes Publisher 3da25cf156 Merge pull request #127119 from Jefftree/compat-version/cleanup-apiserver-example
Remove example feature gate from pkg/apiserver/kube_features.go

Kubernetes-commit: 19421346446536b69753f775e188845617094e8e
2024-09-04 19:44:05 +00:00
Kubernetes Publisher 90f5efd77b Merge pull request #126500 from liangyuanpeng/bump_etcd_3515
Bump dependency: etcd to 3.5.15.

Kubernetes-commit: 7df5940bf920349a3c158bcd425e4e4cf97096da
2024-09-04 19:44:01 +00:00
Jefftree e778ced9b7 Remove example feature gate from pkg/apiserver/kube_features.go
Kubernetes-commit: 79deb21ac1d0837fbafdf9e1556019062590c1d8
2024-09-04 14:50:41 +00:00
Kubernetes Publisher 1b4b1fb1c0 Merge pull request #127009 from vinayakankugoyal/kep4633
KEP-4633: Graduate to BETA.

Kubernetes-commit: 8b664fd41431aed4c69e3d6e76a2f25b84cb9a32
2024-09-04 03:51:20 +00:00
Kubernetes Publisher ec5e79c690 Merge pull request #127058 from carlory/rm-fgs
Remove GAed feature gates ServerSideApply/ServerSideFieldValidation

Kubernetes-commit: 6b2a5b84e58413f1736b862bad972fd4acc02dc0
2024-09-03 19:43:27 +00:00
Kubernetes Publisher 6b4322243f Merge pull request #127089 from sanposhiho/revert-126574
Revert: Fix data race in APF tests

Kubernetes-commit: 49ffd6192f1608e0a2980548390b2c5431d6af8a
2024-09-03 15:44:18 +00:00
Kensei Nakada cfa44309dd Revert "apiserver: fix data race in apf tests in server/filters package"
This reverts commit dde23bb0b103a00ac9c8e568e81826149b42472c.

Kubernetes-commit: d26772120531617e897a745b1bfd1178648fb995
2024-09-03 19:39:38 +09:00
Kensei Nakada 6bd08c5dc4 Revert "apiserver: improve logging for apf tests in server/filters package"
This reverts commit 8fa3e61399b85d534566dca6566ddb287873839c.

Kubernetes-commit: 9fe3b8410726b0276d2d65a79ce7645660d491f9
2024-09-03 19:39:32 +09:00
carlory af2142bfe4 Remove GAed feature gates ServerSideApply/ServerSideFieldValidation
Kubernetes-commit: de7e4318d6b2ad0de4472dcaef7d97c34057d3d8
2024-09-02 13:52:48 +08:00
Kubernetes Publisher bbf28207e3 Merge pull request #127010 from aramase/aramase/f/kep_3331_jti
Set credential-id in userinfo.extra for jwt authenticators if jti claim present

Kubernetes-commit: c3cb89ebb08e8c28eb3a3dc4baeb3a5117dbbea0
2024-08-30 19:46:15 +00:00
Anish Ramasekar 13c06f3696 Set credential-id in userinfo.extra for jwt authenticators if jti claim present
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: c22a41e879e72ba4c925b06d8aa00e43160a0f86
2024-08-29 17:09:07 -07:00
Kubernetes Publisher bb8caf4ebf Merge pull request #126574 from tkashem/apf-data-race
Fix data race in APF tests

Kubernetes-commit: dc8c42752aee5db66baca47535b867a41d15d92b
2024-08-29 19:47:04 +00:00
Kubernetes Publisher bd063e0342 Merge pull request #126968 from serathius/watchache-refactor-store
Watch cache refactor watch cache store and add tests

Kubernetes-commit: 6de748d97bf1cff8e6e511e8152af41c617ec8f5
2024-08-29 11:54:47 +00:00
Marek Siarkowicz a8c26a18b8 Extract watch cache store to separate file and cover with tests
Kubernetes-commit: c93d2e8fb19da0082765cb3e5a6db952eca628ce
2024-08-28 13:31:02 +02:00
Marek Siarkowicz 94a5e3cd04 Refactor WaitUntilFreshAndList to split out filtering to separate function
Kubernetes-commit: 7400d57943cf7576925d54e7daa42e397e71dfe4
2024-08-28 12:59:38 +02:00
Kubernetes Publisher 784a83e3b4 Merge pull request #126957 from dashpole/fix_tracing_leak
Fix memory leak from global OpenTelemetry MeterProvider

Kubernetes-commit: 3d65369f346d359bcabdc7f04addff5fdb2f194a
2024-08-28 20:28:38 +00:00
Kubernetes Publisher d9009c5253 Merge pull request #126627 from fusida/fix-reduce-etcd-conn
fix short circuit if the compaction request from apiserver is disabled

Kubernetes-commit: 26f399921f639df8bda16d365e9c632561ccc6de
2024-08-28 20:28:37 +00:00
Kubernetes Publisher 8beb81657f Merge pull request #125634 from ahmedtd/x509credentialID
Define credential IDs for X.509 certificates

Kubernetes-commit: bbd83d86444d7b325a51f4daa0d65163b795b70e
2024-08-28 16:11:34 +00:00
David Ashpole f1fcc94e63 update-vendor
Kubernetes-commit: 4aa655c2d73f90ed621bda58746f1cde28710d7a
2024-08-28 14:27:36 +00:00
David Ashpole daa75f8bec fix memory leak from global MeterProvider
Kubernetes-commit: b86cab8c4c3421c6b195fc82990a63c859449072
2024-08-28 14:20:46 +00:00
Kubernetes Publisher 6c201a977a Merge pull request #126930 from Ruddickmg/patch-1
kmsv2: set authority to localhost

Kubernetes-commit: 95b3fe9f15cdcaf98098be398478e70365b12dd7
2024-08-28 07:54:31 +00:00
Kubernetes Publisher 7198d4fe04 Merge pull request #126787 from Jefftree/update-kube-openapi
Bump k8s.io/kube-openapi and k8s.io/gengo

Kubernetes-commit: f1a922c8e6f951381450ee3c2922ca018f14a82e
2024-08-27 23:50:08 +00:00
Kubernetes Publisher a78241fd58 Merge pull request #126359 from jpbetz/quantity-estimated-cost
Fix estimated cost for Kubernetes defined CEL types for equals

Kubernetes-commit: f88281768c52a5729d1dccee16164b472e794922
2024-08-27 23:50:06 +00:00
Kubernetes Publisher 12c4904eb5 Merge pull request #126295 from sohankunkerkar/denoise-watcher-events
dynamiccertificates: denoise Kubelet logs by skipping removal of non-existent file watchers

Kubernetes-commit: 09596a57de807a9b9fb699f3ef44f0d3312bbcb1
2024-08-27 19:43:22 +00:00
Kubernetes Publisher 932b2589d5 Merge pull request #125884 from serathius/benchmark-storage
Benchmark storage

Kubernetes-commit: 3a849069043142b2bec8f45654b235ba0b660aad
2024-08-27 11:52:18 +00:00
Jefftree a03dae5dab re-vendor k8s.io/kube-openapi
Kubernetes-commit: ea2bdb6334ec1a2821a96163d83480d5fdb1861b
2024-08-27 01:58:39 +00:00
Marcus Ruddick 9c8c6ccc4d kmsv2: fixed issue with an invalid authority header being sent by the KMSv2 service
Kubernetes-commit: 618ca85bc9482ea11cf792331688fdf0c7b54518
2024-08-26 14:43:02 -10:00
Lan Liang 158efa920d Bump dependency: etcd to 3.5.15.
Signed-off-by: Lan Liang <gcslyp@gmail.com>

Kubernetes-commit: 81e754e7ef5cac4cd7697968b5ab8dc89648eca6
2024-08-24 04:46:01 +00:00
Kubernetes Publisher 586ab588eb Merge pull request #126645 from cici37/cleanupFG
Remove feature gate ValiatingAdmissionPolicy after being stable for two releases

Kubernetes-commit: 7b80cdb66a390f225d23cd612950144e3a39d1ae
2024-08-23 19:46:20 +00:00
Kubernetes Publisher da8e2914b0 Merge pull request #126867 from piny940/master
fix ValidatingAdmissionPolicy's Validate func to return decision with valid Evaluation

Kubernetes-commit: 1e827f4b2a46981e4f3056b54b43363e787bbaaa
2024-08-22 23:45:21 +00:00
cici37 e9e24680dc Remove FG inspection from VAP plugin
Kubernetes-commit: 72ad9c5fdf782f824281ecf9e18ec36caaa9472b
2024-08-22 17:52:21 +00:00
Kubernetes Publisher 27f5f2543a Merge pull request #126854 from serathius/pagination-tests
Add paging tests

Kubernetes-commit: cee43048c72403ae0f56f04053cb7e3361de1415
2024-08-22 07:43:46 +00:00
piny940 2ed104b2e5 fix ValidatingAdmissionPolicy's Validate func to return decision with valid Evaluation
Kubernetes-commit: 9f7ea45ea0ffc76be7793dbcb4730a98fa03bcf5
2024-08-22 13:33:17 +09:00
Vinayak Goyal 491f6248d4 KEP-4633: Graduate to BETA.
Signed-off-by: Vinayak Goyal <vinaygo@google.com>

Kubernetes-commit: 8a4e23ea30bb0af50aa425cea8af926998872ee4
2024-08-22 01:28:57 +00:00
Kubernetes Publisher a5bbfcdce4 Merge pull request #126512 from kmala/metrics
add resource to the transformation metrics

Kubernetes-commit: 77737c3eb37afaadbf40ec499a58c1593c9c7382
2024-08-21 23:44:23 +00:00
Kubernetes Publisher 6830df5156 Merge pull request #126305 from richabanker/optimize-tests
Init common apiserver for all testcases in CEL tests

Kubernetes-commit: beb696c2c9467dbc44cbaf35c5a4a3daf0321db3
2024-08-21 03:44:11 +00:00
Kubernetes Publisher 66b4299b79 Merge pull request #126774 from aramase/aramase/c/sa_rm_unused_function
cleanup unused fn GetOrCreateServiceAccount in serviceaccount/util

Kubernetes-commit: cb7b4ea648a97bdbf8f4f1b8655a7a110c9f78d0
2024-08-19 06:38:09 +00:00
Anish Ramasekar 43a56206a7 cleanup unused fn GetOrCreateServiceAccount in serviceaccount/util
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 2f96a788e299ef3cea08d0cb03b13c584496891c
2024-08-18 21:04:51 -07:00
Kubernetes Publisher 5d131b7a78 Merge pull request #126523 from enj/enj/i/ssa_authz_create_err
SSA: improve create authz error message

Kubernetes-commit: 114900ab1f678a03e5c1bc63fe92b0892d2a9238
2024-08-16 18:23:10 +00:00
Kubernetes Publisher 25d7e88901 Merge pull request #126553 from aramase/aramase/c/kep_3331_disallow_k8s_io_prefix
Disallow `k8s.io` and `kubernetes.io` namespaced extra key in structured authn config

Kubernetes-commit: f26cf38a50bb38689b7674d228d004bff7a65899
2024-08-15 18:32:46 +00:00
Kubernetes Publisher 38586e5d94 Merge pull request #126685 from enj/enj/i/kms_resouce_logs
Ensure transformers have access to the resource via request info

Kubernetes-commit: 026c55e40de835464e769bad65c8a19940b61459
2024-08-15 10:52:00 +00:00
Kubernetes Publisher 11b0e0730d Merge pull request #126698 from enj/enj/i/del_kms_v2_gates
Remove KMSv2 and KMSv2KDF feature gates

Kubernetes-commit: cd5f2083155bed7006b218ade85b584d53dfaae8
2024-08-15 02:43:25 +00:00
Monis Khan 272e9eba82 Remove KMSv2 and KMSv2KDF feature gates
These have been GA since v1.29 and can be safely removed.

Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 6398b8a19fe0e113cf250c13b0639dea258a174f
2024-08-14 15:59:01 -04:00
Monis Khan cd5bba1780 Ensure transformers have access to the resource via request info
This guarantees that logs and metrics that rely on this information
work as expected.

Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 49d7b4c97e4f7ee5c664b068c207a39b8c3f759e
2024-08-14 10:33:36 -04:00
Kubernetes Publisher b157511c42 Merge pull request #126649 from 0x5457/fix-panic
apiserver: declare kubeClient and dynamicClient as interface types to avoid panic

Kubernetes-commit: 19175396280537af75d20c5ea22e877f16b40792
2024-08-14 10:22:41 +00:00
Kubernetes Publisher 13f78e0e7e Merge pull request #126665 from liggitt/version-build-id
Restore honoring --version build ID overrides

Kubernetes-commit: 69dbf2eee96f1c95c097370ddcb1d5c30f86bec8
2024-08-14 06:30:43 +00:00
Kubernetes Publisher be949676bf Merge pull request #126565 from Adarsh-verma-14/remove-duplicate-call
remove duplicate call for ServeMux

Kubernetes-commit: 54691fdc21a84a6ac3a8e052d92f81a43a19139c
2024-08-14 06:30:41 +00:00
Kubernetes Publisher cb239f8776 Merge pull request #126354 from liangyuanpeng/celtest_update
Using NewExpressions for CEL lazy test.

Kubernetes-commit: bc3d6fd491aec44138086e5ece4e706041761398
2024-08-14 06:30:38 +00:00
Kubernetes Publisher c84ae4a3d4 Merge pull request #126316 from aramase/aramase/f/kep_3331_tighter_validation
Validate structured authn feature is enabled for discovery url/multiple audiences

Kubernetes-commit: c06ea0fc81168cee6d8055182aa4b3d38bc5bb58
2024-08-14 06:30:36 +00:00
Jordan Liggitt 77331233f8 Restore honoring --version build ID overrides
Kubernetes-commit: c181912dc5d8559834857e69ea34ee1729c43c6b
2024-08-13 18:48:56 -04:00
0x5457 27c3ca736b apiserver: declare kubeClient and dynamicClient as interface types to avoid panic
Kubernetes-commit: 81824b7c2e673f64f70a6e99180bb6bfc6b738d9
2024-08-13 11:25:11 +08:00
Cici Huang fac4f5d2a0 Remove feature gate ValiatingAdmissionPolicy after stable.
Kubernetes-commit: 0f19faf9be562f3d18880ed2ae12d6b9d059476c
2024-08-12 12:11:02 -07:00
古九 e312f49d45 fix short circuit if the compaction request from apiserver is disabled
Kubernetes-commit: 9fef30117f89830cc8b17610c359141b663844f1
2024-08-12 10:21:49 +08:00
Abu Kashem 9d542feed9 apiserver: improve logging for apf tests in server/filters package
Kubernetes-commit: 8fa3e61399b85d534566dca6566ddb287873839c
2024-08-07 07:49:54 -04:00
Adarsh-verma-14 41e1af4df2 remove duplicate call for ServeMux
Kubernetes-commit: 838d7c9049439b5997f0947258e183d677788475
2024-08-07 02:56:49 +05:30
Anish Ramasekar fed75d52d6 Disallow k8s.io and kubernetes.io namespaced extra key in structured authn config
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: 89c619f4fe698bf5b208ce86bce5da6833ca77b6
2024-08-05 16:09:00 -07:00
Monis Khan cc8ff8f965 ForbiddenStatusError: make linter happy on error construction
Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: bff6ce4a38077c29cdf2e1ac2fce1a551082ebfe
2024-08-05 10:50:51 -04:00
Monis Khan 757565c389 SSA: improve create authz error message
Signed-off-by: Monis Khan <mok@microsoft.com>

Kubernetes-commit: 857127f7c44a029f6f8dd44b0b40364aa00aa13d
2024-08-02 17:20:53 -04:00
Keerthan Reddy Mala fcf807e7b4 add resource to the transformation metrics
Kubernetes-commit: 3a8df1efdd83015773be4afd409b0f4cb7eab654
2024-08-01 15:46:50 -07:00
Kubernetes Publisher fb0703a685 Merge pull request #126329 from serathius/concurrent-transformation-chan-of-chan
[chan of chan] Make object transformation concurrent to remove watch cache scalability issue for conversion webhook

Kubernetes-commit: c19d9edfdee7b4ff39041f0254c92ebf66af332f
2024-07-31 10:41:42 -07:00
Joe Betz 28d9c91abf Add basic panicOnUnknown support for kubernetes types
Kubernetes-commit: f6995740a6fe4b90103131516c3318f158209d21
2024-07-25 15:53:39 -04:00
Joe Betz 65a6ca8228 support opaque kinds
Kubernetes-commit: 953fbaca487c45e3e1fc655d212008a2be01ac53
2024-07-25 15:04:09 -04:00
Joe Betz cbc488649b Fix estimated cost for Kubernetes defined CEL types
Kubernetes-commit: 0a4e863373abc1b84372b0a93c8bcd32a24d07fb
2024-07-25 14:14:20 -04:00
Lan Liang 552e7d7170 Using NewExpressions for cel lazy test.
Signed-off-by: Lan Liang <gcslyp@gmail.com>

Kubernetes-commit: 9a8d6b72e4f1e33e6a30fd281fd0972fdce93f78
2024-07-25 10:08:15 +00:00
Richa Banker f434fbf0c7 init a common apiserver for TestAuthorizationDecisionCaching testcases
Kubernetes-commit: 4acedb5132b2c3a7d61bd9e088c964af3fcfee3d
2024-07-23 22:19:02 -07:00
Richa Banker 1d26753b4b split Test_ValidateNamespace_NoParams into successes and failures tests, init a common apiserver for all testcases
Kubernetes-commit: 9df04b7c782cccc5fb068554152b4dcd9baf408b
2024-07-23 21:41:32 -07:00
Anish Ramasekar febd487238 Validate structured authn feature is enabled for discovery url/multiple
audiences

Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>

Kubernetes-commit: f80c73248f872769d72b620e567747a1018f8a2b
2024-07-23 15:04:02 -07:00
Sohan Kunkerkar fed8dfe736 dynamiccertificates: denoise Kubelet logs by skipping removal of non-existent file watchers
This commit updates the DynamicFileCAContent controller to skip the removal
of non-existent file watchers. Previously, the controller attempted to remove
a file watch even if it didn't exist, which resulted in a flood of error messages
being logged in the Kubelet logs.

Signed-off-by: Sohan Kunkerkar <sohank2602@gmail.com>

Kubernetes-commit: 17ad4b39f8b6b299d20fb94f99083ea84083b6b2
2024-07-23 10:55:16 -04:00
Marek Siarkowicz 3adae5fd46 Make object transformation concurrent to remove watch cache scalability issue for conversion webhook
Test by enabling consistent list from cache in storage version migrator stress test that uses
conversion webhook that bottlenects events comming to watch cache.

Set concurrency to 10, based on maximum/average transform latency when
running stress test. In my testing max was about 60-100ms, while average
was 6-10ms.

Kubernetes-commit: bb686f203308481bcd7808f767171cdef27e12a0
2024-07-22 11:24:37 +02:00
Marek Siarkowicz 9aa7a6ac61 Introduce ConcurrentWatchObjectDecode feature gate disabled by default
Kubernetes-commit: 93a10a75698075e86344ee4fdb56701309468b95
2024-07-30 16:28:48 +02:00
Kubernetes Publisher bd44a99f50 Merge pull request #126469 from serathius/beta2
Move ConsistentListFromCache to Beta default again

Kubernetes-commit: eb729d1db72fc27f495ddf397289678b180926f1
2024-07-31 18:05:06 +00:00
Kubernetes Publisher 1b569bf504 Merge pull request #126470 from benluddy/apiservingwithroutine-alpha-disabled
Move APIServingWithRoutine to alpha and disabled by default.

Kubernetes-commit: f9d2297298909c9f3a2be2e88f3c84df43f3a376
2024-07-31 05:33:58 +00:00
Kubernetes Publisher bebf7ae9a1 Merge pull request #126467 from serathius/fallback
Implement fallback for consistent reads from cache

Kubernetes-commit: 974f3d3d8ff6bfb33a375f7207c34c69b3e8b932
2024-07-30 21:33:25 +00:00
Marek Siarkowicz c470f38c60 Move ConsistentListFromCache to Beta default again
This reverts commit aeb51a16e369d5b823a8ae6488d1d5e12c683516.

Kubernetes-commit: 2ca56aab87d0927e568f1d896d49692433d5d93a
2024-07-30 22:49:47 +02:00
Ben Luddy 788e7ee758 Move APIServingWithRoutine to alpha and disabled by default.
Kubernetes-commit: c8380040848fcbd0a0cc06600b9d4531b65098d2
2024-07-30 16:33:31 -04:00
Marek Siarkowicz 6c5ee08ccf Implement fallback for consistent reads from cache
Kubernetes-commit: 35962561e44425fe5e23f19aeccba9269fab3a56
2024-07-30 18:57:22 +02:00
Kubernetes Publisher c8097e3f30 Merge pull request #124012 from Jefftree/le-controller
Coordinated Leader Election

Kubernetes-commit: 5f5c02da51cd3146f30c6ee56013c983f4999d9c
2024-07-25 21:25:59 +00:00
Marek Siarkowicz 132d3e46d6 Add paging tests
Kubernetes-commit: 99e69569808cf746262b25a9d9d515c26256c7e5
2024-07-07 16:15:47 +02:00
Marek Siarkowicz de0559ec7b Benchmark storage
Kubernetes-commit: fa5008807add2776ff87f346a7b7d3c029d19efc
2024-07-02 22:50:57 +02:00
Taahir Ahmed 72a449fe98 Define credential IDs for X.509 certificates
This commit expands the existing credential ID concept to cover X.509
certificates.  We use the certificate's signature as the credential ID,
since this safe and unique.

Kubernetes-commit: 2ad2bd8907d979f709cd924af7986be71c31ce12
2024-06-21 16:21:35 -07:00
jonyhy96 644a2519ca apiserver: fix data race in apf tests in server/filters package
Signed-off-by: jonyhy96 <hy352144278@gmail.com>
Co-authored-by: chenwen  <wen.chen@daocloud.io>

Kubernetes-commit: dde23bb0b103a00ac9c8e568e81826149b42472c
2022-01-28 15:03:11 +08:00
494 changed files with 35458 additions and 9789 deletions

3
OWNERS
View File

@ -13,7 +13,6 @@ reviewers:
- caesarxuchao
- liggitt
- sttts
- ncdc
- enj
- hzxuzhonghu
- apelisse
@ -24,3 +23,5 @@ labels:
- area/apiserver
emeritus_approvers:
- lavalamp
emeritus_reviewers:
- ncdc

2
doc.go
View File

@ -14,4 +14,4 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package apiserver // import "k8s.io/apiserver"
package apiserver

161
go.mod
View File

@ -2,126 +2,125 @@
module k8s.io/apiserver
go 1.22.0
go 1.24.0
godebug default=go1.24
require (
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/blang/semver/v4 v4.0.0
github.com/coreos/go-oidc v2.3.0+incompatible
github.com/coreos/go-systemd/v22 v22.5.0
github.com/emicklei/go-restful/v3 v3.11.0
github.com/fsnotify/fsnotify v1.7.0
github.com/emicklei/go-restful/v3 v3.12.2
github.com/fsnotify/fsnotify v1.9.0
github.com/go-logr/logr v1.4.2
github.com/gogo/protobuf v1.3.2
github.com/google/cel-go v0.20.1
github.com/google/gnostic-models v0.6.8
github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0
github.com/google/btree v1.1.3
github.com/google/cel-go v0.25.0
github.com/google/gnostic-models v0.7.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.0
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
go.etcd.io/etcd/api/v3 v3.5.14
go.etcd.io/etcd/client/pkg/v3 v3.5.14
go.etcd.io/etcd/client/v3 v3.5.14
go.etcd.io/etcd/server/v3 v3.5.13
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0
go.opentelemetry.io/otel v1.28.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0
go.opentelemetry.io/otel/sdk v1.28.0
go.opentelemetry.io/otel/trace v1.28.0
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.21.0
golang.org/x/time v0.3.0
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
go.etcd.io/etcd/api/v3 v3.6.1
go.etcd.io/etcd/client/pkg/v3 v3.6.1
go.etcd.io/etcd/client/v3 v3.6.1
go.etcd.io/etcd/server/v3 v3.6.1
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0
go.opentelemetry.io/otel v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
go.opentelemetry.io/otel/metric v1.35.0
go.opentelemetry.io/otel/sdk v1.34.0
go.opentelemetry.io/otel/trace v1.35.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.36.0
golang.org/x/net v0.38.0
golang.org/x/sync v0.12.0
golang.org/x/sys v0.31.0
golang.org/x/time v0.9.0
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb
google.golang.org/grpc v1.72.1
google.golang.org/protobuf v1.36.5
gopkg.in/evanphx/json-patch.v4 v4.12.0
gopkg.in/go-jose/go-jose.v2 v2.6.3
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/square/go-jose.v2 v2.6.0
k8s.io/api v0.0.0-20240724010313-f04ea0bc861d
k8s.io/apimachinery v0.0.0-20240720202316-95b78024e3fe
k8s.io/client-go v0.0.0-20240724010704-ac9204c6195b
k8s.io/component-base v0.0.0-20240722183709-6cc953a9d440
k8s.io/api v0.0.0-20250705010445-839e6c7fb630
k8s.io/apimachinery v0.0.0-20250710005335-ed63805e81ef
k8s.io/client-go v0.0.0-20250709010832-5439ef7b0c5e
k8s.io/component-base v0.0.0-20250708051227-72837f691197
k8s.io/klog/v2 v2.130.1
k8s.io/kms v0.0.0-20240707024556-6e3528fa4c33
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd
sigs.k8s.io/structured-merge-diff/v4 v4.4.1
sigs.k8s.io/yaml v1.4.0
k8s.io/kms v0.0.0-20250701212550-c0cb85aa532f
k8s.io/kube-openapi v0.0.0-20250628140032-d90c4fd18f59
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v4 v4.7.0
sigs.k8s.io/yaml v1.5.0
)
require (
cel.dev/expr v0.23.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/spdystream v0.4.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // 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/sirupsen/logrus v1.9.3 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.etcd.io/etcd/client/v2 v2.305.13 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.13 // indirect
go.etcd.io/etcd/raft/v3 v3.5.13 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.etcd.io/etcd/pkg/v3 v3.6.1 // indirect
go.etcd.io/raft/v3 v3.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace k8s.io/api => k8s.io/api v0.0.0-20240724010313-a789efa287e8

401
go.sum
View File

@ -1,57 +1,42 @@
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=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
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/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
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-oidc v2.3.0+incompatible h1:+5vEsrgprdLjjQ9FzIKAzQz1wwPD+83hQRfUIPh7rO0=
github.com/coreos/go-oidc v2.3.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
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=
@ -59,74 +44,60 @@ 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
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.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
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.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -134,263 +105,223 @@ 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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
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/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/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=
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 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-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
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.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.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
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/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
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=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.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/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8=
go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg=
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M=
go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0=
go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA=
go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw=
go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok=
go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.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.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
go.opentelemetry.io/otel/metric v1.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.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.etcd.io/etcd/api/v3 v3.6.1 h1:yJ9WlDih9HT457QPuHt/TH/XtsdN2tubyxyQHSHPsEo=
go.etcd.io/etcd/api/v3 v3.6.1/go.mod h1:lnfuqoGsXMlZdTJlact3IB56o3bWp1DIlXPIGKRArto=
go.etcd.io/etcd/client/pkg/v3 v3.6.1 h1:CxDVv8ggphmamrXM4Of8aCC8QHzDM4tGcVr9p2BSoGk=
go.etcd.io/etcd/client/pkg/v3 v3.6.1/go.mod h1:aTkCp+6ixcVTZmrJGa7/Mc5nMNs59PEgBbq+HCmWyMc=
go.etcd.io/etcd/client/v3 v3.6.1 h1:KelkcizJGsskUXlsxjVrSmINvMMga0VWwFF0tSPGEP0=
go.etcd.io/etcd/client/v3 v3.6.1/go.mod h1:fCbPUdjWNLfx1A6ATo9syUmFVxqHH9bCnPLBZmnLmMY=
go.etcd.io/etcd/pkg/v3 v3.6.1 h1:Qpshk3/SLra217k7FxcFGaH2niFAxFf1Dug57f0IUiw=
go.etcd.io/etcd/pkg/v3 v3.6.1/go.mod h1:nS0ahQoZZ9qXjQAtYGDt80IEHKl9YOF7mv6J0lQmBoQ=
go.etcd.io/etcd/server/v3 v3.6.1 h1:Y/mh94EeImzXyTBIMVgR0v5H+ANtRFDY4g1s5sxOZGE=
go.etcd.io/etcd/server/v3 v3.6.1/go.mod h1:nCqJGTP9c2WlZluJB59j3bqxZEI/GYBfQxno0MguVjE=
go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
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/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-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=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
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-20180917221912-90fa682c2a6e/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
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.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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
google.golang.org/genproto/googleapis/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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
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.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
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 v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=
gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/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-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=
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=
k8s.io/api v0.0.0-20240724010313-a789efa287e8 h1:TISAHWnfAdn420WpN+fEHG6snbLbfaCAp3kHDoAkxIc=
k8s.io/api v0.0.0-20240724010313-a789efa287e8/go.mod h1:ytlEzqC2wOTwYET71W7+J+k7O2V7vrDuzmNLBSpgT+k=
k8s.io/apimachinery v0.0.0-20240720202316-95b78024e3fe h1:V9MwpYUwbKlfLKVrhpVuKWiat/LBIhm1pGB9/xdHm5Q=
k8s.io/apimachinery v0.0.0-20240720202316-95b78024e3fe/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.0.0-20240724010704-ac9204c6195b h1:NTLYx38CAu+VstHvPLosqB6uSQUtSM+3Mqz2D/C5JpE=
k8s.io/client-go v0.0.0-20240724010704-ac9204c6195b/go.mod h1:Y6CzOT21oLI4O66cjiV5oSSUgOL7gG/VCG9n8XI8OxU=
k8s.io/component-base v0.0.0-20240722183709-6cc953a9d440 h1:14X+5sRQRsul6tLxIKTP0/DotvWlMd9DFCgMqHP1hZY=
k8s.io/component-base v0.0.0-20240722183709-6cc953a9d440/go.mod h1:dj2Pl05aLcVMZi2NXcwv+M/WdUVPEkisFPjDze7rbSk=
k8s.io/api v0.0.0-20250705010445-839e6c7fb630 h1:pnI9Db0bmtO4qa+X6jGK8WslPvzLwW8wrAe5B2//yGU=
k8s.io/api v0.0.0-20250705010445-839e6c7fb630/go.mod h1:cQb0K/knyMnN0b7QfEoYB+YzMbFk6PMoa/XTGxEJ7iw=
k8s.io/apimachinery v0.0.0-20250710005335-ed63805e81ef h1:87QT4Qmn87cD8y09BKqHmXrBC+eIZd9YTG2tDwkGhCc=
k8s.io/apimachinery v0.0.0-20250710005335-ed63805e81ef/go.mod h1:Th679JJyaVRDNFk3vKPKY43ypziDeoGnbEiEgBCz8s4=
k8s.io/client-go v0.0.0-20250709010832-5439ef7b0c5e h1:NGKcA9W2nQjLWlZrHfTcMPkPpjFci2M+SnmkusqoIHo=
k8s.io/client-go v0.0.0-20250709010832-5439ef7b0c5e/go.mod h1:mSlS6FavM9KR26OMw/g4NsTn4BtRSGVKE0DlKfOoE2c=
k8s.io/component-base v0.0.0-20250708051227-72837f691197 h1:HPpnKAvWicINIJb5H0yd7sMnvXjAcCeoN2j8NkIK+s8=
k8s.io/component-base v0.0.0-20250708051227-72837f691197/go.mod h1:U4cTVU7iRxFIlHEFNsrq7AYzG4lxdaliabhy0qPB/Ww=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kms v0.0.0-20240707024556-6e3528fa4c33 h1:Wd/sRvKMgzuCdkZ/WQg2rg/j6NLW8eyw0RK8AhV9Hak=
k8s.io/kms v0.0.0-20240707024556-6e3528fa4c33/go.mod h1:x2EJv5lkGE18ijjE04e8W0fVyRPfAx5flo9WdY7a1Hw=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
k8s.io/kms v0.0.0-20250701212550-c0cb85aa532f h1:IiE5Db+Ha45v4GSmZtR4OLdkk8j7Nk/76SP1gvVAjnM=
k8s.io/kms v0.0.0-20250701212550-c0cb85aa532f/go.mod h1:qCbYSZ7AgfskxlzZlEYmu4XfrmaR8oXyYSAiwvh/fTk=
k8s.io/kube-openapi v0.0.0-20250628140032-d90c4fd18f59 h1:Jc4GiFTK2HHOpfQFoQEGXTBTs2pETwHukmoD4yoTqwo=
k8s.io/kube-openapi v0.0.0-20250628140032-d90c4fd18f59/go.mod h1:GLOk5B+hDbRROvt0X2+hqX64v/zO3vXN7J78OUmBSKw=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=

View File

@ -30,13 +30,13 @@ func TestAddAnnotation(t *testing.T) {
attr.AddAnnotation("foo.admission.k8s.io/key1", "value1")
attr.AddAnnotation("foo.admission.k8s.io/key2", "value2")
annotations := attr.getAnnotations(auditinternal.LevelMetadata)
assert.Equal(t, annotations["foo.admission.k8s.io/key1"], "value1")
assert.Equal(t, "value1", annotations["foo.admission.k8s.io/key1"])
// test overwrite
assert.Error(t, attr.AddAnnotation("foo.admission.k8s.io/key1", "value1-overwrite"),
"admission annotations should not be allowd to be overwritten")
annotations = attr.getAnnotations(auditinternal.LevelMetadata)
assert.Equal(t, annotations["foo.admission.k8s.io/key1"], "value1", "admission annotations should not be overwritten")
assert.Equal(t, "value1", annotations["foo.admission.k8s.io/key1"], "admission annotations should not be overwritten")
// test invalid plugin names
var testCases = map[string]string{
@ -49,17 +49,16 @@ func TestAddAnnotation(t *testing.T) {
err := attr.AddAnnotation(invalidKey, "value-foo")
assert.Error(t, err)
annotations = attr.getAnnotations(auditinternal.LevelMetadata)
assert.Equal(t, annotations[invalidKey], "", name+": invalid pluginName is not allowed ")
assert.Equal(t, "", annotations[invalidKey], name+": invalid pluginName is not allowed ")
}
// test all saved annotations
assert.Equal(
t,
annotations,
map[string]string{
"foo.admission.k8s.io/key1": "value1",
"foo.admission.k8s.io/key2": "value2",
},
}, annotations,
"unexpected final annotations",
)
}

View File

@ -83,7 +83,7 @@ func ensureAnnotationGetter(a Attributes) error {
}
func (handler *auditHandler) logAnnotations(ctx context.Context, a Attributes) {
ae := audit.AuditEventFrom(ctx)
ae := audit.AuditContextFrom(ctx)
if ae == nil {
return
}
@ -91,9 +91,9 @@ func (handler *auditHandler) logAnnotations(ctx context.Context, a Attributes) {
var annotations map[string]string
switch a := a.(type) {
case privateAnnotationsGetter:
annotations = a.getAnnotations(ae.Level)
annotations = a.getAnnotations(ae.GetEventLevel())
case AnnotationsGetter:
annotations = a.GetAnnotations(ae.Level)
annotations = a.GetAnnotations(ae.GetEventLevel())
default:
// this will never happen, because we have already checked it in ensureAnnotationGetter
}

View File

@ -144,8 +144,10 @@ func TestWithAudit(t *testing.T) {
var handler Interface = fakeHandler{tc.admit, tc.admitAnnotations, tc.validate, tc.validateAnnotations, tc.handles}
ctx := audit.WithAuditContext(context.Background())
ac := audit.AuditContextFrom(ctx)
ae := &ac.Event
ae.Level = auditinternal.LevelMetadata
if err := ac.Init(audit.RequestAuditConfig{Level: auditinternal.LevelMetadata}, nil); err != nil {
t.Fatal(err)
}
auditHandler := WithAudit(handler)
a := attributes()
@ -171,9 +173,9 @@ func TestWithAudit(t *testing.T) {
annotations[k] = v
}
if len(annotations) == 0 {
assert.Nil(t, ae.Annotations, tcName+": unexptected annotations set in audit event")
assert.Nil(t, ac.GetEventAnnotations(), tcName+": unexptected annotations set in audit event")
} else {
assert.Equal(t, annotations, ae.Annotations, tcName+": unexptected annotations set in audit event")
assert.Equal(t, annotations, ac.GetEventAnnotations(), tcName+": unexptected annotations set in audit event")
}
}
}
@ -187,8 +189,6 @@ func TestWithAuditConcurrency(t *testing.T) {
}
var handler Interface = fakeHandler{admitAnnotations: admitAnnotations, handles: true}
ctx := audit.WithAuditContext(context.Background())
ac := audit.AuditContextFrom(ctx)
ac.Event.Level = auditinternal.LevelMetadata
auditHandler := WithAudit(handler)
a := attributes()
@ -200,9 +200,15 @@ func TestWithAuditConcurrency(t *testing.T) {
go func() {
defer wg.Done()
mutator, ok := handler.(MutationInterface)
require.True(t, ok)
if !ok {
t.Error("handler is not an interface of type MutationInterface")
return
}
auditMutator, ok := auditHandler.(MutationInterface)
require.True(t, ok)
if !ok {
t.Error("handler is not an interface of type MutationInterface")
return
}
assert.Equal(t, mutator.Admit(ctx, a, nil), auditMutator.Admit(ctx, a, nil), "WithAudit decorator should not effect the return value")
}()
}

View File

@ -63,7 +63,7 @@ func ReadAdmissionConfiguration(pluginNames []string, configFilePath string, con
if err != nil {
return nil, fmt.Errorf("unable to read admission control configuration from %q [%v]", configFilePath, err)
}
codecs := serializer.NewCodecFactory(configScheme)
codecs := serializer.NewCodecFactory(configScheme, serializer.EnableStrict)
decoder := codecs.UniversalDecoder()
decodedObj, err := runtime.Decode(decoder, data)
// we were able to decode the file successfully

View File

@ -22,6 +22,7 @@ import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"
@ -52,7 +53,28 @@ func TestReadAdmissionConfiguration(t *testing.T) {
ConfigBody string
ExpectedAdmissionConfig *apiserver.AdmissionConfiguration
PluginNames []string
ExpectedError string
}{
"duplicate field configuration error": {
ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1",
"kind": "AdmissionConfiguration",
"plugins": [
{"name": "ImagePolicyWebhook-duplicate", "name": "ImagePolicyWebhook", "path": "image-policy-webhook.json"},
{"name": "ResourceQuota"}
]}`,
ExpectedError: "strict decoding error: duplicate field",
},
"unknown field configuration error": {
ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1",
"kind": "AdmissionConfiguration",
"plugins": [
{"foo": "bar", "name": "ImagePolicyWebhook", "path": "image-policy-webhook.json"},
{"name": "ResourceQuota"}
]}`,
ExpectedError: "strict decoding error: unknown field",
},
"v1alpha1 configuration - path fixup": {
ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1",
@ -192,12 +214,18 @@ func TestReadAdmissionConfiguration(t *testing.T) {
t.Fatalf("unexpected err writing temp file: %v", err)
}
config, err := ReadAdmissionConfiguration(testCase.PluginNames, configFileName, scheme)
if err != nil {
if testCase.ExpectedError != "" {
if err != nil {
assert.Contains(t, err.Error(), testCase.ExpectedError)
} else {
t.Fatalf("expected error %q but received none", testCase.ExpectedError)
}
} else if err != nil {
t.Fatalf("unexpected err: %v", err)
} else if !reflect.DeepEqual(config.(configProvider).config, testCase.ExpectedAdmissionConfig) {
t.Fatalf("%s: Expected:\n\t%#v\nGot:\n\t%#v", testName, testCase.ExpectedAdmissionConfig, config.(configProvider).config)
}
if !reflect.DeepEqual(config.(configProvider).config, testCase.ExpectedAdmissionConfig) {
t.Errorf("%s: Expected:\n\t%#v\nGot:\n\t%#v", testName, testCase.ExpectedAdmissionConfig, config.(configProvider).config)
}
}
}

View File

@ -207,7 +207,7 @@ func newAdmissionMetrics() *AdmissionMetrics {
Namespace: namespace,
Subsystem: subsystem,
Name: "webhook_fail_open_count",
Help: "Admission webhook fail open count, identified by name and broken out for each admission type (validating or mutating).",
Help: "Admission webhook fail open count, identified by name and broken out for each admission type (validating or admit).",
StabilityLevel: metrics.ALPHA,
},
[]string{"name", "type"})
@ -217,7 +217,7 @@ func newAdmissionMetrics() *AdmissionMetrics {
Namespace: namespace,
Subsystem: subsystem,
Name: "webhook_request_total",
Help: "Admission webhook request total, identified by name and broken out for each admission type (validating or mutating) and operation. Additional labels specify whether the request was rejected or not and an HTTP status code. Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.",
Help: "Admission webhook request total, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify whether the request was rejected or not and an HTTP status code. Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.",
StabilityLevel: metrics.ALPHA,
},
[]string{"name", "type", "operation", "code", "rejected"})
@ -305,6 +305,11 @@ func (m *AdmissionMetrics) ObserveWebhookRejection(ctx context.Context, name, st
m.webhookRejection.WithContext(ctx).WithLabelValues(name, stepType, operation, string(errorType), strconv.Itoa(rejectionCode)).Inc()
}
// WebhookRejectionGathererForTest exposes admission webhook rejection metric for access by unit test.
func (m *AdmissionMetrics) WebhookRejectionGathererForTest() *metrics.CounterVec {
return m.webhookRejection
}
// ObserveWebhookFailOpen records validating or mutating webhook that fail open.
func (m *AdmissionMetrics) ObserveWebhookFailOpen(ctx context.Context, name, stepType string) {
m.webhookFailOpen.WithContext(ctx).WithLabelValues(name, stepType).Inc()

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package validating
package authorizer
import (
"context"
@ -39,7 +39,10 @@ type cachingAuthorizer struct {
decisions map[string]authzResult
}
func newCachingAuthorizer(in authorizer.Authorizer) authorizer.Authorizer {
// NewCachingAuthorizer returns an authorizer that caches decisions for the duration
// of the authorizers use. Intended to be used for short-lived operations such as
// the handling of a request in the admission chain, and then discarded.
func NewCachingAuthorizer(in authorizer.Authorizer) authorizer.Authorizer {
return &cachingAuthorizer{
authorizer: in,
decisions: make(map[string]authzResult),

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package validating
package authorizer
import (
"context"
@ -491,7 +491,7 @@ func TestCachingAuthorizer(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
var misses int
frontend := newCachingAuthorizer(func() authorizer.Authorizer {
frontend := NewCachingAuthorizer(func() authorizer.Authorizer {
return authorizer.AuthorizerFunc(func(_ context.Context, attributes authorizer.Attributes) (authorizer.Decision, string, error) {
if misses >= len(tc.backend) {
t.Fatalf("got more than expected %d backend invocations", len(tc.backend))

View File

@ -0,0 +1,190 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
import (
"context"
"fmt"
"github.com/google/cel-go/interpreter"
"math"
"time"
admissionv1 "k8s.io/api/admission/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/library"
)
// newActivation creates an activation for CEL admission plugins from the given request, admission chain and
// variable binding information.
func newActivation(compositionCtx CompositionContext, versionedAttr *admission.VersionedAttributes, request *admissionv1.AdmissionRequest, inputs OptionalVariableBindings, namespace *v1.Namespace) (*evaluationActivation, error) {
oldObjectVal, err := objectToResolveVal(versionedAttr.VersionedOldObject)
if err != nil {
return nil, fmt.Errorf("failed to prepare oldObject variable for evaluation: %w", err)
}
objectVal, err := objectToResolveVal(versionedAttr.VersionedObject)
if err != nil {
return nil, fmt.Errorf("failed to prepare object variable for evaluation: %w", err)
}
var paramsVal, authorizerVal, requestResourceAuthorizerVal any
if inputs.VersionedParams != nil {
paramsVal, err = objectToResolveVal(inputs.VersionedParams)
if err != nil {
return nil, fmt.Errorf("failed to prepare params variable for evaluation: %w", err)
}
}
if inputs.Authorizer != nil {
authorizerVal = library.NewAuthorizerVal(versionedAttr.GetUserInfo(), inputs.Authorizer)
requestResourceAuthorizerVal = library.NewResourceAuthorizerVal(versionedAttr.GetUserInfo(), inputs.Authorizer, versionedAttr)
}
requestVal, err := convertObjectToUnstructured(request)
if err != nil {
return nil, fmt.Errorf("failed to prepare request variable for evaluation: %w", err)
}
namespaceVal, err := objectToResolveVal(namespace)
if err != nil {
return nil, fmt.Errorf("failed to prepare namespace variable for evaluation: %w", err)
}
va := &evaluationActivation{
object: objectVal,
oldObject: oldObjectVal,
params: paramsVal,
request: requestVal.Object,
namespace: namespaceVal,
authorizer: authorizerVal,
requestResourceAuthorizer: requestResourceAuthorizerVal,
}
// composition is an optional feature that only applies for ValidatingAdmissionPolicy and MutatingAdmissionPolicy.
if compositionCtx != nil {
va.variables = compositionCtx.Variables(va)
}
return va, nil
}
type evaluationActivation struct {
object, oldObject, params, request, namespace, authorizer, requestResourceAuthorizer, variables interface{}
}
// ResolveName returns a value from the activation by qualified name, or false if the name
// could not be found.
func (a *evaluationActivation) ResolveName(name string) (interface{}, bool) {
switch name {
case ObjectVarName:
return a.object, true
case OldObjectVarName:
return a.oldObject, true
case ParamsVarName:
return a.params, true // params may be null
case RequestVarName:
return a.request, true
case NamespaceVarName:
return a.namespace, true
case AuthorizerVarName:
return a.authorizer, a.authorizer != nil
case RequestResourceAuthorizerVarName:
return a.requestResourceAuthorizer, a.requestResourceAuthorizer != nil
case VariableVarName: // variables always present
return a.variables, true
default:
return nil, false
}
}
// Parent returns the parent of the current activation, may be nil.
// If non-nil, the parent will be searched during resolve calls.
func (a *evaluationActivation) Parent() interpreter.Activation {
return nil
}
// Evaluate runs a compiled CEL admission plugin expression using the provided activation and CEL
// runtime cost budget.
func (a *evaluationActivation) Evaluate(ctx context.Context, compositionCtx CompositionContext, compilationResult CompilationResult, remainingBudget int64) (EvaluationResult, int64, error) {
var evaluation = EvaluationResult{}
if compilationResult.ExpressionAccessor == nil { // in case of placeholder
return evaluation, remainingBudget, nil
}
evaluation.ExpressionAccessor = compilationResult.ExpressionAccessor
if compilationResult.Error != nil {
evaluation.Error = &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("compilation error: %v", compilationResult.Error),
Cause: compilationResult.Error,
}
return evaluation, remainingBudget, nil
}
if compilationResult.Program == nil {
evaluation.Error = &cel.Error{
Type: cel.ErrorTypeInternal,
Detail: "unexpected internal error compiling expression",
}
return evaluation, remainingBudget, nil
}
t1 := time.Now()
evalResult, evalDetails, err := compilationResult.Program.ContextEval(ctx, a)
// budget may be spent due to lazy evaluation of composited variables
if compositionCtx != nil {
compositionCost := compositionCtx.GetAndResetCost()
if compositionCost > remainingBudget {
return evaluation, -1, &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: "validation failed due to running out of cost budget, no further validation rules will be run",
Cause: cel.ErrOutOfBudget,
}
}
remainingBudget -= compositionCost
}
elapsed := time.Since(t1)
evaluation.Elapsed = elapsed
if evalDetails == nil {
return evaluation, -1, &cel.Error{
Type: cel.ErrorTypeInternal,
Detail: fmt.Sprintf("runtime cost could not be calculated for expression: %v, no further expression will be run", compilationResult.ExpressionAccessor.GetExpression()),
}
} else {
rtCost := evalDetails.ActualCost()
if rtCost == nil {
return evaluation, -1, &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("runtime cost could not be calculated for expression: %v, no further expression will be run", compilationResult.ExpressionAccessor.GetExpression()),
Cause: cel.ErrOutOfBudget,
}
} else {
if *rtCost > math.MaxInt64 || int64(*rtCost) > remainingBudget {
return evaluation, -1, &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: "validation failed due to running out of cost budget, no further validation rules will be run",
Cause: cel.ErrOutOfBudget,
}
}
remainingBudget -= int64(*rtCost)
}
}
if err != nil {
evaluation.Error = &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("expression '%v' resulted in error: %v", compilationResult.ExpressionAccessor.GetExpression(), err),
}
} else {
evaluation.EvalResult = evalResult
}
return evaluation, remainingBudget, nil
}

View File

@ -24,8 +24,10 @@ import (
"k8s.io/apimachinery/pkg/util/version"
celconfig "k8s.io/apiserver/pkg/apis/cel"
apiservercel "k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/common"
"k8s.io/apiserver/pkg/cel/environment"
"k8s.io/apiserver/pkg/cel/library"
"k8s.io/apiserver/pkg/cel/mutation"
)
const (
@ -186,7 +188,7 @@ func (c compiler) CompileCELExpression(expressionAccessor ExpressionAccessor, op
found := false
returnTypes := expressionAccessor.ReturnTypes()
for _, returnType := range returnTypes {
if ast.OutputType() == returnType || cel.AnyType == returnType {
if ast.OutputType().IsExactType(returnType) || cel.AnyType.IsExactType(returnType) {
found = true
break
}
@ -194,9 +196,9 @@ func (c compiler) CompileCELExpression(expressionAccessor ExpressionAccessor, op
if !found {
var reason string
if len(returnTypes) == 1 {
reason = fmt.Sprintf("must evaluate to %v", returnTypes[0].String())
reason = fmt.Sprintf("must evaluate to %v but got %v", returnTypes[0].String(), ast.OutputType().String())
} else {
reason = fmt.Sprintf("must evaluate to one of %v", returnTypes)
reason = fmt.Sprintf("must evaluate to one of %v but got %v", returnTypes, ast.OutputType().String())
}
return resultError(reason, apiservercel.ErrorTypeInvalid, nil)
@ -226,46 +228,78 @@ func mustBuildEnvs(baseEnv *environment.EnvSet) variableDeclEnvs {
envs := make(variableDeclEnvs, 8) // since the number of variable combinations is small, pre-build a environment for each
for _, hasParams := range []bool{false, true} {
for _, hasAuthorizer := range []bool{false, true} {
var err error
for _, strictCost := range []bool{false, true} {
var envOpts []cel.EnvOption
if hasParams {
envOpts = append(envOpts, cel.Variable(ParamsVarName, cel.DynType))
}
if hasAuthorizer {
envOpts = append(envOpts,
cel.Variable(AuthorizerVarName, library.AuthorizerType),
cel.Variable(RequestResourceAuthorizerVarName, library.ResourceCheckType))
}
envOpts = append(envOpts,
cel.Variable(ObjectVarName, cel.DynType),
cel.Variable(OldObjectVarName, cel.DynType),
cel.Variable(NamespaceVarName, namespaceType.CelType()),
cel.Variable(RequestVarName, requestType.CelType()))
extended, err := baseEnv.Extend(
environment.VersionedOptions{
// Feature epoch was actually 1.26, but we artificially set it to 1.0 because these
// options should always be present.
IntroducedVersion: version.MajorMinor(1, 0),
EnvOptions: envOpts,
DeclTypes: []*apiservercel.DeclType{
namespaceType,
requestType,
},
},
)
decl := OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: hasAuthorizer, StrictCost: strictCost}
envs[decl], err = createEnvForOpts(baseEnv, namespaceType, requestType, decl)
if err != nil {
panic(fmt.Sprintf("environment misconfigured: %v", err))
panic(err)
}
if strictCost {
extended, err = extended.Extend(environment.StrictCostOpt)
if err != nil {
panic(fmt.Sprintf("environment misconfigured: %v", err))
}
}
envs[OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: hasAuthorizer, StrictCost: strictCost}] = extended
}
// We only need this ObjectTypes where strict cost is true
decl := OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: hasAuthorizer, StrictCost: true, HasPatchTypes: true}
envs[decl], err = createEnvForOpts(baseEnv, namespaceType, requestType, decl)
if err != nil {
panic(err)
}
}
}
return envs
}
func createEnvForOpts(baseEnv *environment.EnvSet, namespaceType *apiservercel.DeclType, requestType *apiservercel.DeclType, opts OptionalVariableDeclarations) (*environment.EnvSet, error) {
var envOpts []cel.EnvOption
envOpts = append(envOpts,
cel.Variable(ObjectVarName, cel.DynType),
cel.Variable(OldObjectVarName, cel.DynType),
cel.Variable(NamespaceVarName, namespaceType.CelType()),
cel.Variable(RequestVarName, requestType.CelType()))
if opts.HasParams {
envOpts = append(envOpts, cel.Variable(ParamsVarName, cel.DynType))
}
if opts.HasAuthorizer {
envOpts = append(envOpts,
cel.Variable(AuthorizerVarName, library.AuthorizerType),
cel.Variable(RequestResourceAuthorizerVarName, library.ResourceCheckType))
}
extended, err := baseEnv.Extend(
environment.VersionedOptions{
// Feature epoch was actually 1.26, but we artificially set it to 1.0 because these
// options should always be present.
IntroducedVersion: version.MajorMinor(1, 0),
EnvOptions: envOpts,
DeclTypes: []*apiservercel.DeclType{
namespaceType,
requestType,
},
},
)
if err != nil {
return nil, fmt.Errorf("environment misconfigured: %w", err)
}
if opts.StrictCost {
extended, err = extended.Extend(environment.StrictCostOpt)
if err != nil {
return nil, fmt.Errorf("environment misconfigured: %w", err)
}
}
if opts.HasPatchTypes {
extended, err = extended.Extend(hasPatchTypes)
if err != nil {
return nil, fmt.Errorf("environment misconfigured: %w", err)
}
}
return extended, nil
}
var hasPatchTypes = environment.VersionedOptions{
// Feature epoch was actually 1.32, but we artificially set it to 1.0 because these
// options should always be present.
IntroducedVersion: version.MajorMinor(1, 0),
EnvOptions: []cel.EnvOption{
common.ResolverEnvOption(&mutation.DynamicTypeResolver{}),
environment.UnversionedLib(library.JSONPatch), // for jsonPatch.escape() function
},
}

View File

@ -36,15 +36,27 @@ import (
const VariablesTypeName = "kubernetes.variables"
// CompositedCompiler compiles expressions with variable composition.
type CompositedCompiler struct {
Compiler
FilterCompiler
ConditionCompiler
MutatingCompiler
CompositionEnv *CompositionEnv
}
type CompositedFilter struct {
Filter
// CompositedConditionEvaluator provides evaluation of a condition expression with variable composition.
// The expressions must return a boolean.
type CompositedConditionEvaluator struct {
ConditionEvaluator
compositionEnv *CompositionEnv
}
// CompositedEvaluator provides evaluation of a single expression with variable composition.
// The types that may returned by the expression is determined at compilation time.
type CompositedEvaluator struct {
MutatingEvaluator
compositionEnv *CompositionEnv
}
@ -64,11 +76,13 @@ func NewCompositedCompilerFromTemplate(context *CompositionEnv) *CompositedCompi
CompiledVariables: map[string]CompilationResult{},
}
compiler := NewCompiler(context.EnvSet)
filterCompiler := NewFilterCompiler(context.EnvSet)
conditionCompiler := &conditionCompiler{compiler}
mutation := &mutatingCompiler{compiler}
return &CompositedCompiler{
Compiler: compiler,
FilterCompiler: filterCompiler,
CompositionEnv: context,
Compiler: compiler,
ConditionCompiler: conditionCompiler,
MutatingCompiler: mutation,
CompositionEnv: context,
}
}
@ -85,11 +99,20 @@ func (c *CompositedCompiler) CompileAndStoreVariable(variable NamedExpressionAcc
return result
}
func (c *CompositedCompiler) Compile(expressions []ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) Filter {
filter := c.FilterCompiler.Compile(expressions, optionalDecls, envType)
return &CompositedFilter{
Filter: filter,
compositionEnv: c.CompositionEnv,
func (c *CompositedCompiler) CompileCondition(expressions []ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) ConditionEvaluator {
condition := c.ConditionCompiler.CompileCondition(expressions, optionalDecls, envType)
return &CompositedConditionEvaluator{
ConditionEvaluator: condition,
compositionEnv: c.CompositionEnv,
}
}
// CompileEvaluator compiles an mutatingEvaluator for the given expression, options and environment.
func (c *CompositedCompiler) CompileMutatingEvaluator(expression ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) MutatingEvaluator {
mutation := c.MutatingCompiler.CompileMutatingEvaluator(expression, optionalDecls, envType)
return &CompositedEvaluator{
MutatingEvaluator: mutation,
compositionEnv: c.CompositionEnv,
}
}
@ -160,9 +183,9 @@ func (c *compositionContext) Variables(activation any) ref.Val {
return lazyMap
}
func (f *CompositedFilter) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *v1.AdmissionRequest, optionalVars OptionalVariableBindings, namespace *corev1.Namespace, runtimeCELCostBudget int64) ([]EvaluationResult, int64, error) {
func (f *CompositedConditionEvaluator) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *v1.AdmissionRequest, optionalVars OptionalVariableBindings, namespace *corev1.Namespace, runtimeCELCostBudget int64) ([]EvaluationResult, int64, error) {
ctx = f.compositionEnv.CreateContext(ctx)
return f.Filter.ForInput(ctx, versionedAttr, request, optionalVars, namespace, runtimeCELCostBudget)
return f.ConditionEvaluator.ForInput(ctx, versionedAttr, request, optionalVars, namespace, runtimeCELCostBudget)
}
func (c *compositionContext) reportCost(cost int64) {

View File

@ -223,8 +223,8 @@ func TestCompositedPolicies(t *testing.T) {
t.Fatal(err)
}
compiler.CompileAndStoreVariables(tc.variables, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false, StrictCost: tc.strictCostEnforcement}, environment.NewExpressions)
validations := []ExpressionAccessor{&condition{Expression: tc.expression}}
f := compiler.Compile(validations, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false, StrictCost: tc.strictCostEnforcement}, environment.NewExpressions)
validations := []ExpressionAccessor{&testCondition{Expression: tc.expression}}
f := compiler.CompileCondition(validations, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false, StrictCost: tc.strictCostEnforcement}, environment.NewExpressions)
versionedAttr, err := admission.NewVersionedAttributes(tc.attributes, tc.attributes.GetKind(), newObjectInterfacesForTest())
if err != nil {
t.Fatal(err)

View File

@ -0,0 +1,216 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
import (
"context"
"reflect"
admissionv1 "k8s.io/api/admission/v1"
authenticationv1 "k8s.io/api/authentication/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/cel/environment"
)
// conditionCompiler implement the interface ConditionCompiler.
type conditionCompiler struct {
compiler Compiler
}
func NewConditionCompiler(env *environment.EnvSet) ConditionCompiler {
return &conditionCompiler{compiler: NewCompiler(env)}
}
// CompileCondition compiles the cel expressions defined in the ExpressionAccessors into a ConditionEvaluator
func (c *conditionCompiler) CompileCondition(expressionAccessors []ExpressionAccessor, options OptionalVariableDeclarations, mode environment.Type) ConditionEvaluator {
compilationResults := make([]CompilationResult, len(expressionAccessors))
for i, expressionAccessor := range expressionAccessors {
if expressionAccessor == nil {
continue
}
compilationResults[i] = c.compiler.CompileCELExpression(expressionAccessor, options, mode)
}
return NewCondition(compilationResults)
}
// condition implements the ConditionEvaluator interface
type condition struct {
compilationResults []CompilationResult
}
func NewCondition(compilationResults []CompilationResult) ConditionEvaluator {
return &condition{
compilationResults,
}
}
func convertObjectToUnstructured(obj interface{}) (*unstructured.Unstructured, error) {
if obj == nil || reflect.ValueOf(obj).IsNil() {
return &unstructured.Unstructured{Object: nil}, nil
}
ret, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return nil, err
}
return &unstructured.Unstructured{Object: ret}, nil
}
func objectToResolveVal(r runtime.Object) (interface{}, error) {
if r == nil || reflect.ValueOf(r).IsNil() {
return nil, nil
}
v, err := convertObjectToUnstructured(r)
if err != nil {
return nil, err
}
return v.Object, nil
}
// ForInput evaluates the compiled CEL expressions converting them into CELEvaluations
// errors per evaluation are returned on the Evaluation object
// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
func (c *condition) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *admissionv1.AdmissionRequest, inputs OptionalVariableBindings, namespace *v1.Namespace, runtimeCELCostBudget int64) ([]EvaluationResult, int64, error) {
// TODO: replace unstructured with ref.Val for CEL variables when native type support is available
evaluations := make([]EvaluationResult, len(c.compilationResults))
var err error
// if this activation supports composition, we will need the compositionCtx. It may be nil.
compositionCtx, _ := ctx.(CompositionContext)
activation, err := newActivation(compositionCtx, versionedAttr, request, inputs, namespace)
if err != nil {
return nil, -1, err
}
remainingBudget := runtimeCELCostBudget
for i, compilationResult := range c.compilationResults {
evaluations[i], remainingBudget, err = activation.Evaluate(ctx, compositionCtx, compilationResult, remainingBudget)
if err != nil {
return nil, -1, err
}
}
return evaluations, remainingBudget, nil
}
// TODO: to reuse https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go#L154
func CreateAdmissionRequest(attr admission.Attributes, equivalentGVR metav1.GroupVersionResource, equivalentKind metav1.GroupVersionKind) *admissionv1.AdmissionRequest {
// Attempting to use same logic as webhook for constructing resource
// GVK, GVR, subresource
// Use the GVK, GVR that the matcher decided was equivalent to that of the request
// https://github.com/kubernetes/kubernetes/blob/90c362b3430bcbbf8f245fadbcd521dab39f1d7c/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go#L182-L210
gvk := equivalentKind
gvr := equivalentGVR
subresource := attr.GetSubresource()
requestGVK := attr.GetKind()
requestGVR := attr.GetResource()
requestSubResource := attr.GetSubresource()
aUserInfo := attr.GetUserInfo()
var userInfo authenticationv1.UserInfo
if aUserInfo != nil {
userInfo = authenticationv1.UserInfo{
Extra: make(map[string]authenticationv1.ExtraValue),
Groups: aUserInfo.GetGroups(),
UID: aUserInfo.GetUID(),
Username: aUserInfo.GetName(),
}
// Convert the extra information in the user object
for key, val := range aUserInfo.GetExtra() {
userInfo.Extra[key] = authenticationv1.ExtraValue(val)
}
}
dryRun := attr.IsDryRun()
return &admissionv1.AdmissionRequest{
Kind: metav1.GroupVersionKind{
Group: gvk.Group,
Kind: gvk.Kind,
Version: gvk.Version,
},
Resource: metav1.GroupVersionResource{
Group: gvr.Group,
Resource: gvr.Resource,
Version: gvr.Version,
},
SubResource: subresource,
RequestKind: &metav1.GroupVersionKind{
Group: requestGVK.Group,
Kind: requestGVK.Kind,
Version: requestGVK.Version,
},
RequestResource: &metav1.GroupVersionResource{
Group: requestGVR.Group,
Resource: requestGVR.Resource,
Version: requestGVR.Version,
},
RequestSubResource: requestSubResource,
Name: attr.GetName(),
Namespace: attr.GetNamespace(),
Operation: admissionv1.Operation(attr.GetOperation()),
UserInfo: userInfo,
// Leave Object and OldObject unset since we don't provide access to them via request
DryRun: &dryRun,
Options: runtime.RawExtension{
Object: attr.GetOperationOptions(),
},
}
}
// CreateNamespaceObject creates a Namespace object that is suitable for the CEL evaluation.
// If the namespace is nil, CreateNamespaceObject returns nil
func CreateNamespaceObject(namespace *v1.Namespace) *v1.Namespace {
if namespace == nil {
return nil
}
return &v1.Namespace{
Status: namespace.Status,
Spec: namespace.Spec,
ObjectMeta: metav1.ObjectMeta{
Name: namespace.Name,
GenerateName: namespace.GenerateName,
Namespace: namespace.Namespace,
UID: namespace.UID,
ResourceVersion: namespace.ResourceVersion,
Generation: namespace.Generation,
CreationTimestamp: namespace.CreationTimestamp,
DeletionTimestamp: namespace.DeletionTimestamp,
DeletionGracePeriodSeconds: namespace.DeletionGracePeriodSeconds,
Labels: namespace.Labels,
Annotations: namespace.Annotations,
Finalizers: namespace.Finalizers,
},
}
}
// CompilationErrors returns a list of all the errors from the compilation of the mutatingEvaluator
func (c *condition) CompilationErrors() []error {
compilationErrors := []error{}
for _, result := range c.compilationResults {
if result.Error != nil {
compilationErrors = append(compilationErrors, result.Error)
}
}
return compilationErrors
}

View File

@ -28,8 +28,6 @@ import (
celtypes "github.com/google/cel-go/common/types"
"github.com/stretchr/testify/require"
pointer "k8s.io/utils/ptr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -48,17 +46,18 @@ import (
genericfeatures "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
pointer "k8s.io/utils/ptr"
)
type condition struct {
type testCondition struct {
Expression string
}
func (c *condition) GetExpression() string {
return c.Expression
func (tc *testCondition) GetExpression() string {
return tc.Expression
}
func (v *condition) ReturnTypes() []*celgo.Type {
func (tc *testCondition) ReturnTypes() []*celgo.Type {
return []*celgo.Type{celgo.BoolType}
}
@ -71,10 +70,10 @@ func TestCompile(t *testing.T) {
{
name: "invalid syntax",
validation: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "1 < 'asdf'",
},
&condition{
&testCondition{
Expression: "1 < 2",
},
},
@ -85,13 +84,13 @@ func TestCompile(t *testing.T) {
{
name: "valid syntax",
validation: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "1 < 2",
},
&condition{
&testCondition{
Expression: "object.spec.string.matches('[0-9]+')",
},
&condition{
&testCondition{
Expression: "request.kind.group == 'example.com' && request.kind.version == 'v1' && request.kind.kind == 'Fake'",
},
},
@ -100,13 +99,13 @@ func TestCompile(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
c := filterCompiler{compiler: NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))}
e := c.Compile(tc.validation, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false, StrictCost: true}, environment.NewExpressions)
c := conditionCompiler{compiler: NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))}
e := c.CompileCondition(tc.validation, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false, StrictCost: true}, environment.NewExpressions)
if e == nil {
t.Fatalf("unexpected nil validator")
}
validations := tc.validation
CompilationResults := e.(*filter).compilationResults
CompilationResults := e.(*condition).compilationResults
require.Equal(t, len(validations), len(CompilationResults))
meets := make([]bool, len(validations))
@ -131,7 +130,7 @@ func TestCompile(t *testing.T) {
}
}
func TestFilter(t *testing.T) {
func TestCondition(t *testing.T) {
simpleLabelSelector, err := labels.NewRequirement("apple", selection.Equals, []string{"banana"})
if err != nil {
panic(err)
@ -185,6 +184,7 @@ func TestFilter(t *testing.T) {
v130 := version.MajorMinor(1, 30)
v131 := version.MajorMinor(1, 31)
v127 := version.MajorMinor(1, 27)
var nilUnstructured *unstructured.Unstructured
cases := []struct {
@ -201,11 +201,12 @@ func TestFilter(t *testing.T) {
enableSelectors bool
compatibilityVersion *version.Version
envType environment.Type
}{
{
name: "valid syntax for object",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "has(object.subsets) && object.subsets.size() < 2",
},
},
@ -220,7 +221,7 @@ func TestFilter(t *testing.T) {
{
name: "valid syntax for metadata",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "object.metadata.name == 'endpoints1'",
},
},
@ -235,10 +236,10 @@ func TestFilter(t *testing.T) {
{
name: "valid syntax for oldObject",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject == null",
},
&condition{
&testCondition{
Expression: "object != null",
},
},
@ -256,7 +257,7 @@ func TestFilter(t *testing.T) {
{
name: "valid syntax for request",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "request.operation == 'CREATE'",
},
},
@ -271,7 +272,7 @@ func TestFilter(t *testing.T) {
{
name: "valid syntax for configMap",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "request.namespace != params.data.fakeString",
},
},
@ -287,7 +288,7 @@ func TestFilter(t *testing.T) {
{
name: "test failure",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
},
@ -310,10 +311,10 @@ func TestFilter(t *testing.T) {
{
name: "test failure with multiple validations",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "has(object.subsets)",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
},
@ -332,10 +333,10 @@ func TestFilter(t *testing.T) {
{
name: "test failure policy with multiple failed validations",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject != null",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
},
@ -354,10 +355,10 @@ func TestFilter(t *testing.T) {
{
name: "test Object null in delete",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject != null",
},
&condition{
&testCondition{
Expression: "object == null",
},
},
@ -376,7 +377,7 @@ func TestFilter(t *testing.T) {
{
name: "test runtime error",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject.x == 100",
},
},
@ -392,7 +393,7 @@ func TestFilter(t *testing.T) {
{
name: "test against crd param",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "object.subsets.size() < params.spec.testSize",
},
},
@ -408,10 +409,10 @@ func TestFilter(t *testing.T) {
{
name: "test compile failure",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "fail to compile test",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > params.spec.testSize",
},
},
@ -430,7 +431,7 @@ func TestFilter(t *testing.T) {
{
name: "test pod",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "object.spec.nodeName == 'testnode'",
},
},
@ -446,7 +447,7 @@ func TestFilter(t *testing.T) {
{
name: "test deny paramKind without paramRef",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "params != null",
},
},
@ -461,7 +462,7 @@ func TestFilter(t *testing.T) {
{
name: "test allow paramKind without paramRef",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "params == null",
},
},
@ -477,10 +478,10 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer allow resource check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('').resource('endpoints').check('create').allowed()",
},
&condition{
&testCondition{
Expression: "authorizer.group('').resource('endpoints').check('create').errored()",
},
},
@ -503,7 +504,7 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer error using fieldSelector with 1.30 compatibility",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('apps').resource('deployments').fieldSelector('foo=bar').labelSelector('apple=banana').subresource('status').namespace('test').name('backend').check('create').allowed()",
},
},
@ -529,13 +530,13 @@ func TestFilter(t *testing.T) {
*simpleLabelSelector,
},
}),
enableSelectors: true,
enableSelectors: false,
compatibilityVersion: v130,
},
{
name: "test authorizer allow resource check with all fields",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('apps').resource('deployments').fieldSelector('foo=bar').labelSelector('apple=banana').subresource('status').namespace('test').name('backend').check('create').allowed()",
},
},
@ -567,7 +568,7 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer allow resource check with parse failures",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('apps').resource('deployments').fieldSelector('foo badoperator bar').labelSelector('apple badoperator banana').subresource('status').namespace('test').name('backend').check('create').allowed()",
},
},
@ -595,14 +596,14 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer allow resource check with all fields, without gate",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('apps').resource('deployments').fieldSelector('foo=bar').labelSelector('apple=banana').subresource('status').namespace('test').name('backend').check('create').allowed()",
},
},
attributes: newValidAttribute(&podObject, false),
results: []EvaluationResult{
{
EvalResult: celtypes.True,
Error: fmt.Errorf("fieldSelector"),
},
},
authorizer: newAuthzAllowMatch(authorizer.AttributesRecord{
@ -615,12 +616,13 @@ func TestFilter(t *testing.T) {
Verb: "create",
APIVersion: "*",
}),
enableSelectors: false,
compatibilityVersion: v131,
},
{
name: "test authorizer not allowed resource check one incorrect field",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('test').name('backend').check('create').allowed()",
},
@ -645,7 +647,7 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer reason",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('').resource('endpoints').check('create').reason() == 'fake reason'",
},
},
@ -660,13 +662,13 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer error",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.group('').resource('endpoints').check('create').errored()",
},
&condition{
&testCondition{
Expression: "authorizer.group('').resource('endpoints').check('create').error() == 'fake authz error'",
},
&condition{
&testCondition{
Expression: "authorizer.group('').resource('endpoints').check('create').allowed()",
},
},
@ -687,7 +689,7 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer allow path check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.path('/healthz').check('get').allowed()",
},
},
@ -705,7 +707,7 @@ func TestFilter(t *testing.T) {
{
name: "test authorizer decision is denied path check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.path('/healthz').check('get').allowed() == false",
},
},
@ -720,7 +722,7 @@ func TestFilter(t *testing.T) {
{
name: "test request resource authorizer allow check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.requestResource.check('custom-verb').allowed()",
},
},
@ -744,7 +746,7 @@ func TestFilter(t *testing.T) {
{
name: "test subresource request resource authorizer allow check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.requestResource.check('custom-verb').allowed()",
},
},
@ -768,7 +770,7 @@ func TestFilter(t *testing.T) {
{
name: "test serviceAccount authorizer allow check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "authorizer.serviceAccount('default', 'test-serviceaccount').group('').resource('endpoints').namespace('default').name('endpoints1').check('custom-verb').allowed()",
},
},
@ -795,7 +797,7 @@ func TestFilter(t *testing.T) {
{
name: "test perCallLimit exceed",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "object.subsets.size() < params.spec.testSize",
},
},
@ -812,28 +814,28 @@ func TestFilter(t *testing.T) {
{
name: "test namespaceObject",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "namespaceObject.metadata.name == 'test'",
},
&condition{
&testCondition{
Expression: "'env' in namespaceObject.metadata.labels && namespaceObject.metadata.labels.env == 'test'",
},
&condition{
&testCondition{
Expression: "('fake' in namespaceObject.metadata.labels) && namespaceObject.metadata.labels.fake == 'test'",
},
&condition{
&testCondition{
Expression: "namespaceObject.spec.finalizers[0] == 'kubernetes'",
},
&condition{
&testCondition{
Expression: "namespaceObject.status.phase == 'Active'",
},
&condition{
&testCondition{
Expression: "size(namespaceObject.metadata.managedFields) == 1",
},
&condition{
&testCondition{
Expression: "size(namespaceObject.metadata.ownerReferences) == 1",
},
&condition{
&testCondition{
Expression: "'env' in namespaceObject.metadata.annotations",
},
},
@ -867,13 +869,58 @@ func TestFilter(t *testing.T) {
hasParamKind: false,
namespaceObject: nsObject,
},
{
name: "cel lib not recognized in version earlier than introduced version",
validations: []ExpressionAccessor{
&testCondition{
Expression: "isQuantity(\"20M\")",
},
},
attributes: newValidAttribute(&podObject, false),
results: []EvaluationResult{
{
Error: fmt.Errorf("isQuantity"),
},
},
compatibilityVersion: v127,
},
{
name: "cel lib recognized in version later than introduced version",
validations: []ExpressionAccessor{
&testCondition{
Expression: "isQuantity(\"20M\")",
},
},
results: []EvaluationResult{
{
EvalResult: celtypes.True,
},
},
attributes: newValidAttribute(&podObject, false),
compatibilityVersion: v130,
},
{
name: "cel lib always recognized in stored expression",
validations: []ExpressionAccessor{
&testCondition{
Expression: "isQuantity(\"20M\")",
},
},
attributes: newValidAttribute(&podObject, false),
results: []EvaluationResult{
{
EvalResult: celtypes.True,
},
},
envType: environment.StoredExpressions,
compatibilityVersion: version.MajorMinor(1, 2),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if tc.enableSelectors {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AuthorizeWithSelectors, true)
}
environment.DisableBaseEnvSetCachingForTests()
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AuthorizeWithSelectors, tc.enableSelectors)
if tc.testPerCallLimit == 0 {
tc.testPerCallLimit = celconfig.PerCallLimit
@ -891,13 +938,18 @@ func TestFilter(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c := NewFilterCompiler(env)
f := c.Compile(tc.validations, OptionalVariableDeclarations{HasParams: tc.hasParamKind, HasAuthorizer: tc.authorizer != nil, StrictCost: tc.strictCost}, environment.NewExpressions)
c := NewConditionCompiler(env)
envType := tc.envType
if envType == "" {
envType = environment.NewExpressions
}
f := c.CompileCondition(tc.validations, OptionalVariableDeclarations{HasParams: tc.hasParamKind, HasAuthorizer: tc.authorizer != nil, StrictCost: tc.strictCost}, envType)
if f == nil {
t.Fatalf("unexpected nil validator")
}
validations := tc.validations
CompilationResults := f.(*filter).compilationResults
CompilationResults := f.(*condition).compilationResults
require.Equal(t, len(validations), len(CompilationResults))
versionedAttr, err := admission.NewVersionedAttributes(tc.attributes, tc.attributes.GetKind(), newObjectInterfacesForTest())
@ -913,8 +965,13 @@ func TestFilter(t *testing.T) {
}
require.Equal(t, len(evalResults), len(tc.results))
for i, result := range tc.results {
if result.Error != nil && evalResults[i].Error == nil {
t.Errorf("Expected error result containing '%v' but got non-error", result.Error)
continue
}
if result.Error != nil && !strings.Contains(evalResults[i].Error.Error(), result.Error.Error()) {
t.Errorf("Expected result '%v' but got '%v'", result.Error, evalResults[i].Error)
continue
}
if result.Error == nil && evalResults[i].Error != nil {
t.Errorf("Expected result '%v' but got error '%v'", result.EvalResult, evalResults[i].Error)
@ -954,10 +1011,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "expression exceed RuntimeCELCostBudget at fist expression",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "has(object.subsets) && object.subsets.size() < 2",
},
&condition{
&testCondition{
Expression: "has(object.subsets)",
},
},
@ -969,10 +1026,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "expression exceed RuntimeCELCostBudget at last expression",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "has(object.subsets) && object.subsets.size() < 2",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
},
@ -985,10 +1042,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "test RuntimeCELCostBudge is not exceed",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject != null",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
},
@ -1002,10 +1059,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "test RuntimeCELCostBudge exactly covers",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject != null",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
},
@ -1019,13 +1076,13 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "test RuntimeCELCostBudge exactly covers then constant",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "oldObject != null",
},
&condition{
&testCondition{
Expression: "object.subsets.size() > 2",
},
&condition{
&testCondition{
Expression: "true", // zero cost
},
},
@ -1039,7 +1096,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: authz check",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
},
@ -1053,7 +1110,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: isSorted()",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "[1,2,3,4].isSorted()",
},
},
@ -1066,7 +1123,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: url",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "url('https:://kubernetes.io/').getHostname() == 'kubernetes.io'",
},
},
@ -1079,7 +1136,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: split",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "size('abc 123 def 123'.split(' ')) > 0",
},
},
@ -1092,7 +1149,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: join",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "size(['aa', 'bb', 'cc', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].join(' ')) > 0",
},
},
@ -1105,7 +1162,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: find",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "size('abc 123 def 123'.find('123')) > 0",
},
},
@ -1118,7 +1175,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "Extended library cost: quantity",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "quantity(\"200M\") == quantity(\"0.2G\") && quantity(\"0.2G\") == quantity(\"200M\")",
},
},
@ -1131,10 +1188,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: expression exceed RuntimeCELCostBudget at fist expression",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
&condition{
&testCondition{
Expression: "has(object.subsets)",
},
},
@ -1148,10 +1205,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: expression exceed RuntimeCELCostBudget at last expression",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
},
@ -1165,10 +1222,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: test RuntimeCELCostBudge is not exceed",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
},
@ -1184,10 +1241,10 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: test RuntimeCELCostBudge exactly covers",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed()",
},
},
@ -1203,7 +1260,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: per call limit exceeds",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "!authorizer.group('').resource('endpoints').check('create').allowed() && !authorizer.group('').resource('endpoints').check('create').allowed() && !authorizer.group('').resource('endpoints').check('create').allowed()",
},
},
@ -1218,7 +1275,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: Extended library cost: isSorted()",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "[1,2,3,4].isSorted()",
},
},
@ -1232,7 +1289,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: Extended library cost: url",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "url('https:://kubernetes.io/').getHostname() == 'kubernetes.io'",
},
},
@ -1246,7 +1303,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: Extended library cost: split",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "size('abc 123 def 123'.split(' ')) > 0",
},
},
@ -1260,7 +1317,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: Extended library cost: join",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "size(['aa', 'bb', 'cc', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].join(' ')) > 0",
},
},
@ -1274,7 +1331,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: Extended library cost: find",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "size('abc 123 def 123'.find('123')) > 0",
},
},
@ -1288,7 +1345,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
{
name: "With StrictCostEnforcementForVAP enabled: Extended library cost: quantity",
validations: []ExpressionAccessor{
&condition{
&testCondition{
Expression: "quantity(\"200M\") == quantity(\"0.2G\") && quantity(\"0.2G\") == quantity(\"200M\")",
},
},
@ -1303,13 +1360,13 @@ func TestRuntimeCELCostBudget(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
c := filterCompiler{compiler: NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), tc.enableStrictCostEnforcement))}
f := c.Compile(tc.validations, OptionalVariableDeclarations{HasParams: tc.hasParamKind, HasAuthorizer: true, StrictCost: tc.enableStrictCostEnforcement}, environment.NewExpressions)
c := conditionCompiler{compiler: NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), tc.enableStrictCostEnforcement))}
f := c.CompileCondition(tc.validations, OptionalVariableDeclarations{HasParams: tc.hasParamKind, HasAuthorizer: true, StrictCost: tc.enableStrictCostEnforcement}, environment.NewExpressions)
if f == nil {
t.Fatalf("unexpected nil validator")
}
validations := tc.validations
CompilationResults := f.(*filter).compilationResults
CompilationResults := f.(*condition).compilationResults
require.Equal(t, len(validations), len(CompilationResults))
versionedAttr, err := admission.NewVersionedAttributes(tc.attributes, tc.attributes.GetKind(), newObjectInterfacesForTest())
@ -1470,7 +1527,7 @@ func TestCompilationErrors(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
e := filter{
e := condition{
compilationResults: tc.results,
}
compilationErrors := e.CompilationErrors()

View File

@ -1,361 +0,0 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
import (
"context"
"fmt"
"math"
"reflect"
"time"
"github.com/google/cel-go/interpreter"
admissionv1 "k8s.io/api/admission/v1"
authenticationv1 "k8s.io/api/authentication/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/environment"
"k8s.io/apiserver/pkg/cel/library"
)
// filterCompiler implement the interface FilterCompiler.
type filterCompiler struct {
compiler Compiler
}
func NewFilterCompiler(env *environment.EnvSet) FilterCompiler {
return &filterCompiler{compiler: NewCompiler(env)}
}
type evaluationActivation struct {
object, oldObject, params, request, namespace, authorizer, requestResourceAuthorizer, variables interface{}
}
// ResolveName returns a value from the activation by qualified name, or false if the name
// could not be found.
func (a *evaluationActivation) ResolveName(name string) (interface{}, bool) {
switch name {
case ObjectVarName:
return a.object, true
case OldObjectVarName:
return a.oldObject, true
case ParamsVarName:
return a.params, true // params may be null
case RequestVarName:
return a.request, true
case NamespaceVarName:
return a.namespace, true
case AuthorizerVarName:
return a.authorizer, a.authorizer != nil
case RequestResourceAuthorizerVarName:
return a.requestResourceAuthorizer, a.requestResourceAuthorizer != nil
case VariableVarName: // variables always present
return a.variables, true
default:
return nil, false
}
}
// Parent returns the parent of the current activation, may be nil.
// If non-nil, the parent will be searched during resolve calls.
func (a *evaluationActivation) Parent() interpreter.Activation {
return nil
}
// Compile compiles the cel expressions defined in the ExpressionAccessors into a Filter
func (c *filterCompiler) Compile(expressionAccessors []ExpressionAccessor, options OptionalVariableDeclarations, mode environment.Type) Filter {
compilationResults := make([]CompilationResult, len(expressionAccessors))
for i, expressionAccessor := range expressionAccessors {
if expressionAccessor == nil {
continue
}
compilationResults[i] = c.compiler.CompileCELExpression(expressionAccessor, options, mode)
}
return NewFilter(compilationResults)
}
// filter implements the Filter interface
type filter struct {
compilationResults []CompilationResult
}
func NewFilter(compilationResults []CompilationResult) Filter {
return &filter{
compilationResults,
}
}
func convertObjectToUnstructured(obj interface{}) (*unstructured.Unstructured, error) {
if obj == nil || reflect.ValueOf(obj).IsNil() {
return &unstructured.Unstructured{Object: nil}, nil
}
ret, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return nil, err
}
return &unstructured.Unstructured{Object: ret}, nil
}
func objectToResolveVal(r runtime.Object) (interface{}, error) {
if r == nil || reflect.ValueOf(r).IsNil() {
return nil, nil
}
v, err := convertObjectToUnstructured(r)
if err != nil {
return nil, err
}
return v.Object, nil
}
// ForInput evaluates the compiled CEL expressions converting them into CELEvaluations
// errors per evaluation are returned on the Evaluation object
// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *admissionv1.AdmissionRequest, inputs OptionalVariableBindings, namespace *v1.Namespace, runtimeCELCostBudget int64) ([]EvaluationResult, int64, error) {
// TODO: replace unstructured with ref.Val for CEL variables when native type support is available
evaluations := make([]EvaluationResult, len(f.compilationResults))
var err error
oldObjectVal, err := objectToResolveVal(versionedAttr.VersionedOldObject)
if err != nil {
return nil, -1, err
}
objectVal, err := objectToResolveVal(versionedAttr.VersionedObject)
if err != nil {
return nil, -1, err
}
var paramsVal, authorizerVal, requestResourceAuthorizerVal any
if inputs.VersionedParams != nil {
paramsVal, err = objectToResolveVal(inputs.VersionedParams)
if err != nil {
return nil, -1, err
}
}
if inputs.Authorizer != nil {
authorizerVal = library.NewAuthorizerVal(versionedAttr.GetUserInfo(), inputs.Authorizer)
requestResourceAuthorizerVal = library.NewResourceAuthorizerVal(versionedAttr.GetUserInfo(), inputs.Authorizer, versionedAttr)
}
requestVal, err := convertObjectToUnstructured(request)
if err != nil {
return nil, -1, err
}
namespaceVal, err := objectToResolveVal(namespace)
if err != nil {
return nil, -1, err
}
va := &evaluationActivation{
object: objectVal,
oldObject: oldObjectVal,
params: paramsVal,
request: requestVal.Object,
namespace: namespaceVal,
authorizer: authorizerVal,
requestResourceAuthorizer: requestResourceAuthorizerVal,
}
// composition is an optional feature that only applies for ValidatingAdmissionPolicy.
// check if the context allows composition
var compositionCtx CompositionContext
var ok bool
if compositionCtx, ok = ctx.(CompositionContext); ok {
va.variables = compositionCtx.Variables(va)
}
remainingBudget := runtimeCELCostBudget
for i, compilationResult := range f.compilationResults {
var evaluation = &evaluations[i]
if compilationResult.ExpressionAccessor == nil { // in case of placeholder
continue
}
evaluation.ExpressionAccessor = compilationResult.ExpressionAccessor
if compilationResult.Error != nil {
evaluation.Error = &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("compilation error: %v", compilationResult.Error),
Cause: compilationResult.Error,
}
continue
}
if compilationResult.Program == nil {
evaluation.Error = &cel.Error{
Type: cel.ErrorTypeInternal,
Detail: fmt.Sprintf("unexpected internal error compiling expression"),
}
continue
}
t1 := time.Now()
evalResult, evalDetails, err := compilationResult.Program.ContextEval(ctx, va)
// budget may be spent due to lazy evaluation of composited variables
if compositionCtx != nil {
compositionCost := compositionCtx.GetAndResetCost()
if compositionCost > remainingBudget {
return nil, -1, &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
Cause: cel.ErrOutOfBudget,
}
}
remainingBudget -= compositionCost
}
elapsed := time.Since(t1)
evaluation.Elapsed = elapsed
if evalDetails == nil {
return nil, -1, &cel.Error{
Type: cel.ErrorTypeInternal,
Detail: fmt.Sprintf("runtime cost could not be calculated for expression: %v, no further expression will be run", compilationResult.ExpressionAccessor.GetExpression()),
}
} else {
rtCost := evalDetails.ActualCost()
if rtCost == nil {
return nil, -1, &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("runtime cost could not be calculated for expression: %v, no further expression will be run", compilationResult.ExpressionAccessor.GetExpression()),
Cause: cel.ErrOutOfBudget,
}
} else {
if *rtCost > math.MaxInt64 || int64(*rtCost) > remainingBudget {
return nil, -1, &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
Cause: cel.ErrOutOfBudget,
}
}
remainingBudget -= int64(*rtCost)
}
}
if err != nil {
evaluation.Error = &cel.Error{
Type: cel.ErrorTypeInvalid,
Detail: fmt.Sprintf("expression '%v' resulted in error: %v", compilationResult.ExpressionAccessor.GetExpression(), err),
}
} else {
evaluation.EvalResult = evalResult
}
}
return evaluations, remainingBudget, nil
}
// TODO: to reuse https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go#L154
func CreateAdmissionRequest(attr admission.Attributes, equivalentGVR metav1.GroupVersionResource, equivalentKind metav1.GroupVersionKind) *admissionv1.AdmissionRequest {
// Attempting to use same logic as webhook for constructing resource
// GVK, GVR, subresource
// Use the GVK, GVR that the matcher decided was equivalent to that of the request
// https://github.com/kubernetes/kubernetes/blob/90c362b3430bcbbf8f245fadbcd521dab39f1d7c/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go#L182-L210
gvk := equivalentKind
gvr := equivalentGVR
subresource := attr.GetSubresource()
requestGVK := attr.GetKind()
requestGVR := attr.GetResource()
requestSubResource := attr.GetSubresource()
aUserInfo := attr.GetUserInfo()
var userInfo authenticationv1.UserInfo
if aUserInfo != nil {
userInfo = authenticationv1.UserInfo{
Extra: make(map[string]authenticationv1.ExtraValue),
Groups: aUserInfo.GetGroups(),
UID: aUserInfo.GetUID(),
Username: aUserInfo.GetName(),
}
// Convert the extra information in the user object
for key, val := range aUserInfo.GetExtra() {
userInfo.Extra[key] = authenticationv1.ExtraValue(val)
}
}
dryRun := attr.IsDryRun()
return &admissionv1.AdmissionRequest{
Kind: metav1.GroupVersionKind{
Group: gvk.Group,
Kind: gvk.Kind,
Version: gvk.Version,
},
Resource: metav1.GroupVersionResource{
Group: gvr.Group,
Resource: gvr.Resource,
Version: gvr.Version,
},
SubResource: subresource,
RequestKind: &metav1.GroupVersionKind{
Group: requestGVK.Group,
Kind: requestGVK.Kind,
Version: requestGVK.Version,
},
RequestResource: &metav1.GroupVersionResource{
Group: requestGVR.Group,
Resource: requestGVR.Resource,
Version: requestGVR.Version,
},
RequestSubResource: requestSubResource,
Name: attr.GetName(),
Namespace: attr.GetNamespace(),
Operation: admissionv1.Operation(attr.GetOperation()),
UserInfo: userInfo,
// Leave Object and OldObject unset since we don't provide access to them via request
DryRun: &dryRun,
Options: runtime.RawExtension{
Object: attr.GetOperationOptions(),
},
}
}
// CreateNamespaceObject creates a Namespace object that is suitable for the CEL evaluation.
// If the namespace is nil, CreateNamespaceObject returns nil
func CreateNamespaceObject(namespace *v1.Namespace) *v1.Namespace {
if namespace == nil {
return nil
}
return &v1.Namespace{
Status: namespace.Status,
Spec: namespace.Spec,
ObjectMeta: metav1.ObjectMeta{
Name: namespace.Name,
GenerateName: namespace.GenerateName,
Namespace: namespace.Namespace,
UID: namespace.UID,
ResourceVersion: namespace.ResourceVersion,
Generation: namespace.Generation,
CreationTimestamp: namespace.CreationTimestamp,
DeletionTimestamp: namespace.DeletionTimestamp,
DeletionGracePeriodSeconds: namespace.DeletionGracePeriodSeconds,
Labels: namespace.Labels,
Annotations: namespace.Annotations,
Finalizers: namespace.Finalizers,
},
}
}
// CompilationErrors returns a list of all the errors from the compilation of the evaluator
func (e *filter) CompilationErrors() []error {
compilationErrors := []error{}
for _, result := range e.compilationResults {
if result.Error != nil {
compilationErrors = append(compilationErrors, result.Error)
}
}
return compilationErrors
}

View File

@ -63,12 +63,15 @@ type OptionalVariableDeclarations struct {
HasAuthorizer bool
// StrictCost specifies if the CEL cost limitation is strict for extended libraries as well as native libraries.
StrictCost bool
// HasPatchTypes specifies if JSONPatch, Object, Object.metadata and similar types are available in CEL. These can be used
// to initialize the typed objects in CEL required to create patches.
HasPatchTypes bool
}
// FilterCompiler contains a function to assist with converting types and values to/from CEL-typed values.
type FilterCompiler interface {
// Compile is used for the cel expression compilation
Compile(expressions []ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) Filter
// ConditionCompiler contains a function to assist with converting types and values to/from CEL-typed values.
type ConditionCompiler interface {
// CompileCondition is used for the cel expression compilation
CompileCondition(expressions []ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) ConditionEvaluator
}
// OptionalVariableBindings provides expression bindings for optional CEL variables.
@ -82,16 +85,38 @@ type OptionalVariableBindings struct {
Authorizer authorizer.Authorizer
}
// Filter contains a function to evaluate compiled CEL-typed values
// ConditionEvaluator contains the result of compiling a CEL expression
// that evaluates to a condition. This is used both for validation and pre-conditions.
// It expects the inbound object to already have been converted to the version expected
// by the underlying CEL code (which is indicated by the match criteria of a policy definition).
// versionedParams may be nil.
type Filter interface {
type ConditionEvaluator interface {
// ForInput converts compiled CEL-typed values into evaluated CEL-typed value.
// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
// If cost budget is calculated, the filter should return the remaining budget.
// If cost budget is calculated, the condition should return the remaining budget.
ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *v1.AdmissionRequest, optionalVars OptionalVariableBindings, namespace *corev1.Namespace, runtimeCELCostBudget int64) ([]EvaluationResult, int64, error)
// CompilationErrors returns a list of errors from the compilation of the evaluator
// CompilationErrors returns a list of errors from the compilation of the mutatingEvaluator
CompilationErrors() []error
}
// MutatingCompiler contains a function to assist with converting types and values to/from CEL-typed values.
type MutatingCompiler interface {
// CompileMutatingEvaluator is used for the cel expression compilation
CompileMutatingEvaluator(expression ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) MutatingEvaluator
}
// MutatingEvaluator contains the result of compiling a CEL expression
// that evaluates to a mutation.
// It expects the inbound object to already have been converted to the version expected
// by the underlying CEL code (which is indicated by the match criteria of a policy definition).
// versionedParams may be nil.
type MutatingEvaluator interface {
// ForInput converts compiled CEL-typed values into a CEL-typed value representing a mutation.
// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
// If cost budget is calculated, the condition should return the remaining budget.
ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *v1.AdmissionRequest, optionalVars OptionalVariableBindings, namespace *corev1.Namespace, runtimeCELCostBudget int64) (EvaluationResult, int64, error)
// CompilationErrors returns a list of errors from the compilation of the mutatingEvaluator
CompilationErrors() []error
}

View File

@ -0,0 +1,73 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
import (
"context"
admissionv1 "k8s.io/api/admission/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/cel/environment"
)
// mutatingCompiler provides a MutatingCompiler implementation.
type mutatingCompiler struct {
compiler Compiler
}
// CompileMutatingEvaluator compiles a CEL expression for admission plugins and returns an MutatingEvaluator for executing the
// compiled CEL expression.
func (p *mutatingCompiler) CompileMutatingEvaluator(expressionAccessor ExpressionAccessor, options OptionalVariableDeclarations, mode environment.Type) MutatingEvaluator {
compilationResult := p.compiler.CompileCELExpression(expressionAccessor, options, mode)
return NewMutatingEvaluator(compilationResult)
}
type mutatingEvaluator struct {
compilationResult CompilationResult
}
func NewMutatingEvaluator(compilationResult CompilationResult) MutatingEvaluator {
return &mutatingEvaluator{compilationResult}
}
// ForInput evaluates the compiled CEL expression and returns an evaluation result
// errors per evaluation are returned in the evaluation result
// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
func (p *mutatingEvaluator) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *admissionv1.AdmissionRequest, inputs OptionalVariableBindings, namespace *v1.Namespace, runtimeCELCostBudget int64) (EvaluationResult, int64, error) {
// if this activation supports composition, we will need the compositionCtx. It may be nil.
compositionCtx, _ := ctx.(CompositionContext)
activation, err := newActivation(compositionCtx, versionedAttr, request, inputs, namespace)
if err != nil {
return EvaluationResult{}, -1, err
}
evaluation, remainingBudget, err := activation.Evaluate(ctx, compositionCtx, p.compilationResult, runtimeCELCostBudget)
if err != nil {
return evaluation, -1, err
}
return evaluation, remainingBudget, nil
}
// CompilationErrors returns a list of all the errors from the compilation of the mutatingEvaluator
func (p *mutatingEvaluator) CompilationErrors() (compilationErrors []error) {
if p.compilationResult.Error != nil {
return []error{p.compilationResult.Error}
}
return nil
}

View File

@ -26,6 +26,7 @@ type PolicyAccessor interface {
GetNamespace() string
GetParamKind() *v1.ParamKind
GetMatchConstraints() *v1.MatchResources
GetFailurePolicy() *v1.FailurePolicyType
}
type BindingAccessor interface {

View File

@ -49,6 +49,9 @@ type Source[H Hook] interface {
// Dispatcher dispatches evaluates an admission request against the currently
// active hooks returned by the source.
type Dispatcher[H Hook] interface {
// Start the dispatcher. This method should be called only once at startup.
Start(ctx context.Context) error
// Dispatch a request to the policies. Dispatcher may choose not to
// call a hook, either because the rules of the hook does not match, or
// the namespaceSelector or the objectSelector of the hook does not

View File

@ -36,8 +36,9 @@ import (
)
// H is the Hook type generated by the source and consumed by the dispatcher.
// !TODO: Just pass in a Plugin[H] with accessors to all this information
type sourceFactory[H any] func(informers.SharedInformerFactory, kubernetes.Interface, dynamic.Interface, meta.RESTMapper) Source[H]
type dispatcherFactory[H any] func(authorizer.Authorizer, *matching.Matcher) Dispatcher[H]
type dispatcherFactory[H any] func(authorizer.Authorizer, *matching.Matcher, kubernetes.Interface) Dispatcher[H]
// admissionResources is the list of resources related to CEL-based admission
// features.
@ -170,7 +171,7 @@ func (c *Plugin[H]) ValidateInitialization() error {
}
c.source = c.sourceFactory(c.informerFactory, c.client, c.dynamicClient, c.restMapper)
c.dispatcher = c.dispatcherFactory(c.authorizer, c.matcher)
c.dispatcher = c.dispatcherFactory(c.authorizer, c.matcher, c.client)
pluginContext, pluginContextCancel := context.WithCancel(context.Background())
go func() {
@ -181,10 +182,15 @@ func (c *Plugin[H]) ValidateInitialization() error {
go func() {
err := c.source.Run(pluginContext)
if err != nil && !errors.Is(err, context.Canceled) {
utilruntime.HandleError(fmt.Errorf("policy source context unexpectedly closed: %v", err))
utilruntime.HandleError(fmt.Errorf("policy source context unexpectedly closed: %w", err))
}
}()
err := c.dispatcher.Start(pluginContext)
if err != nil && !errors.Is(err, context.Canceled) {
utilruntime.HandleError(fmt.Errorf("policy dispatcher context unexpectedly closed: %w", err))
}
c.SetReadyFunc(func() bool {
return namespaceInformer.Informer().HasSynced() && c.source.HasSynced()
})

View File

@ -36,7 +36,7 @@ import (
"k8s.io/client-go/tools/cache"
)
// A policy invocation is a single policy-binding-param tuple from a Policy Hook
// PolicyInvocation is a single policy-binding-param tuple from a Policy Hook
// in the context of a specific request. The params have already been resolved
// and any error in configuration or setting up the invocation is stored in
// the Error field.
@ -62,10 +62,6 @@ type PolicyInvocation[P runtime.Object, B runtime.Object, E Evaluator] struct {
// Params fetched by the binding to use to evaluate the policy
Param runtime.Object
// Error is set if there was an error with the policy or binding or its
// params, etc
Error error
}
// dispatcherDelegate is called during a request with a pre-filtered list
@ -76,7 +72,7 @@ type PolicyInvocation[P runtime.Object, B runtime.Object, E Evaluator] struct {
//
// The delegate provides the "validation" or "mutation" aspect of dispatcher functionality
// (in contrast to generic.PolicyDispatcher which only selects active policies and params)
type dispatcherDelegate[P, B runtime.Object, E Evaluator] func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, versionedAttributes webhookgeneric.VersionedAttributeAccessor, invocations []PolicyInvocation[P, B, E]) error
type dispatcherDelegate[P, B runtime.Object, E Evaluator] func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, versionedAttributes webhookgeneric.VersionedAttributeAccessor, invocations []PolicyInvocation[P, B, E]) ([]PolicyError, *apierrors.StatusError)
type policyDispatcher[P runtime.Object, B runtime.Object, E Evaluator] struct {
newPolicyAccessor func(P) PolicyAccessor
@ -104,7 +100,10 @@ func NewPolicyDispatcher[P runtime.Object, B runtime.Object, E Evaluator](
// request. It then resolves all params and creates an Invocation for each
// matching policy-binding-param tuple. The delegate is then called with the
// list of tuples.
//
func (d *policyDispatcher[P, B, E]) Start(ctx context.Context) error {
return nil
}
// Note: MatchConditions expressions are not evaluated here. The dispatcher delegate
// is expected to ignore the result of any policies whose match conditions dont pass.
// This may be possible to refactor so matchconditions are checked here instead.
@ -117,29 +116,33 @@ func (d *policyDispatcher[P, B, E]) Dispatch(ctx context.Context, a admission.At
objectInterfaces: o,
}
var policyErrors []PolicyError
addConfigError := func(err error, definition PolicyAccessor, binding BindingAccessor) {
var message error
if binding == nil {
message = fmt.Errorf("failed to configure policy: %w", err)
} else {
message = fmt.Errorf("failed to configure binding: %w", err)
}
policyErrors = append(policyErrors, PolicyError{
Policy: definition,
Binding: binding,
Message: message,
})
}
for _, hook := range hooks {
policyAccessor := d.newPolicyAccessor(hook.Policy)
matches, matchGVR, matchGVK, err := d.matcher.DefinitionMatches(a, o, policyAccessor)
if err != nil {
// There was an error evaluating if this policy matches anything.
utilruntime.HandleError(err)
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
Policy: hook.Policy,
Error: err,
})
addConfigError(err, policyAccessor, nil)
continue
} else if !matches {
continue
} else if hook.ConfigurationError != nil {
// The policy matches but there is a configuration error with the
// policy itself
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
Policy: hook.Policy,
Error: hook.ConfigurationError,
Resource: matchGVR,
Kind: matchGVK,
})
utilruntime.HandleError(hook.ConfigurationError)
addConfigError(hook.ConfigurationError, policyAccessor, nil)
continue
}
@ -148,19 +151,22 @@ func (d *policyDispatcher[P, B, E]) Dispatch(ctx context.Context, a admission.At
matches, err = d.matcher.BindingMatches(a, o, bindingAccessor)
if err != nil {
// There was an error evaluating if this binding matches anything.
utilruntime.HandleError(err)
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
Policy: hook.Policy,
Binding: binding,
Error: err,
Resource: matchGVR,
Kind: matchGVK,
})
addConfigError(err, policyAccessor, bindingAccessor)
continue
} else if !matches {
continue
}
// here the binding matches.
// VersionedAttr result will be cached and reused later during parallel
// hook calls.
if _, err = versionedAttrAccessor.VersionedAttribute(matchGVK); err != nil {
// VersionedAttr result will be cached and reused later during parallel
// hook calls.
addConfigError(err, policyAccessor, nil)
continue
}
// Collect params for this binding
params, err := CollectParams(
policyAccessor.GetParamKind(),
@ -171,14 +177,7 @@ func (d *policyDispatcher[P, B, E]) Dispatch(ctx context.Context, a admission.At
)
if err != nil {
// There was an error collecting params for this binding.
utilruntime.HandleError(err)
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
Policy: hook.Policy,
Binding: binding,
Error: err,
Resource: matchGVR,
Kind: matchGVK,
})
addConfigError(err, policyAccessor, bindingAccessor)
continue
}
@ -194,23 +193,72 @@ func (d *policyDispatcher[P, B, E]) Dispatch(ctx context.Context, a admission.At
Evaluator: hook.Evaluator,
})
}
}
}
// VersionedAttr result will be cached and reused later during parallel
// hook calls
_, err = versionedAttrAccessor.VersionedAttribute(matchGVK)
if err != nil {
return apierrors.NewInternalError(err)
}
if len(relevantHooks) > 0 {
extraPolicyErrors, statusError := d.delegate(ctx, a, o, versionedAttrAccessor, relevantHooks)
if statusError != nil {
return statusError
}
policyErrors = append(policyErrors, extraPolicyErrors...)
}
var filteredErrors []PolicyError
for _, e := range policyErrors {
// we always default the FailurePolicy if it is unset and validate it in API level
var policy v1.FailurePolicyType
if fp := e.Policy.GetFailurePolicy(); fp == nil {
policy = v1.Fail
} else {
policy = *fp
}
switch policy {
case v1.Ignore:
// TODO: add metrics for ignored error here
continue
case v1.Fail:
filteredErrors = append(filteredErrors, e)
default:
filteredErrors = append(filteredErrors, e)
}
}
if len(relevantHooks) == 0 {
// no matching hooks
return nil
if len(filteredErrors) > 0 {
forbiddenErr := admission.NewForbidden(a, fmt.Errorf("admission request denied by policy"))
// The forbiddenErr is always a StatusError.
var err *apierrors.StatusError
if !errors.As(forbiddenErr, &err) {
// Should never happen.
return apierrors.NewInternalError(fmt.Errorf("failed to create status error"))
}
err.ErrStatus.Message = ""
for _, policyError := range filteredErrors {
message := policyError.Error()
// If this is the first denied decision, use its message and reason
// for the status error message.
if err.ErrStatus.Message == "" {
err.ErrStatus.Message = message
if policyError.Reason != "" {
err.ErrStatus.Reason = policyError.Reason
}
}
// Add the denied decision's message to the status error's details
err.ErrStatus.Details.Causes = append(
err.ErrStatus.Details.Causes,
metav1.StatusCause{Message: message})
}
return err
}
return d.delegate(ctx, a, o, versionedAttrAccessor, relevantHooks)
return nil
}
// Returns params to use to evaluate a policy-binding with given param
@ -352,3 +400,18 @@ func (v *versionedAttributeAccessor) VersionedAttribute(gvk schema.GroupVersionK
v.versionedAttrs[gvk] = versionedAttr
return versionedAttr, nil
}
type PolicyError struct {
Policy PolicyAccessor
Binding BindingAccessor
Message error
Reason metav1.StatusReason
}
func (c PolicyError) Error() string {
if c.Binding != nil {
return fmt.Sprintf("policy '%s' with binding '%s' denied request: %s", c.Policy.GetName(), c.Binding.GetName(), c.Message.Error())
}
return fmt.Sprintf("policy %q denied request: %s", c.Policy.GetName(), c.Message.Error())
}

View File

@ -41,6 +41,13 @@ import (
"k8s.io/klog/v2"
)
// Interval for refreshing policies.
// TODO: Consider reducing this to a shorter duration or replacing this entirely
// with checks that detect when a policy change took effect.
const policyRefreshIntervalDefault = 1 * time.Second
var policyRefreshInterval = policyRefreshIntervalDefault
type policySource[P runtime.Object, B runtime.Object, E Evaluator] struct {
ctx context.Context
policyInformer generic.Informer[P]
@ -122,6 +129,15 @@ func NewPolicySource[P runtime.Object, B runtime.Object, E Evaluator](
return res
}
// SetPolicyRefreshIntervalForTests allows the refresh interval to be overridden during tests.
// This should only be called from tests.
func SetPolicyRefreshIntervalForTests(interval time.Duration) func() {
policyRefreshInterval = interval
return func() {
policyRefreshInterval = policyRefreshIntervalDefault
}
}
func (s *policySource[P, B, E]) Run(ctx context.Context) error {
if s.ctx != nil {
return fmt.Errorf("policy source already running")
@ -178,7 +194,7 @@ func (s *policySource[P, B, E]) Run(ctx context.Context) error {
// and needs to be recompiled
go func() {
// Loop every 1 second until context is cancelled, refreshing policies
wait.Until(s.refreshPolicies, 1*time.Second, ctx.Done())
wait.Until(s.refreshPolicies, policyRefreshInterval, ctx.Done())
}()
<-ctx.Done()

View File

@ -17,22 +17,35 @@ limitations under the License.
package generic_test
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
)
func makeTestDispatcher(authorizer.Authorizer, *matching.Matcher) generic.Dispatcher[generic.PolicyHook[*FakePolicy, *FakeBinding, generic.Evaluator]] {
type fakeDispatcher struct{}
func (fd *fakeDispatcher) Dispatch(context.Context, admission.Attributes, admission.ObjectInterfaces, []generic.PolicyHook[*FakePolicy, *FakeBinding, generic.Evaluator]) error {
return nil
}
func (fd *fakeDispatcher) Start(context.Context) error {
return nil
}
func makeTestDispatcher(authorizer.Authorizer, *matching.Matcher, kubernetes.Interface) generic.Dispatcher[generic.PolicyHook[*FakePolicy, *FakeBinding, generic.Evaluator]] {
return &fakeDispatcher{}
}
func TestPolicySourceHasSyncedEmpty(t *testing.T) {
testContext, testCancel, err := generic.NewPolicyTestContext(
@ -207,6 +220,10 @@ func (fb *FakePolicy) GetMatchConstraints() *v1.MatchResources {
return nil
}
func (fb *FakePolicy) GetFailurePolicy() *v1.FailurePolicyType {
return nil
}
func (fb *FakeBinding) GetName() string {
return fb.Name
}

View File

@ -45,7 +45,6 @@ import (
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/features"
)
// PolicyTestContext is everything you need to unit test a policy plugin
@ -196,18 +195,6 @@ func NewPolicyTestContext[P, B runtime.Object, E Evaluator](
plugin.SetEnabled(true)
featureGate := featuregate.NewFeatureGate()
err = featureGate.Add(map[featuregate.Feature]featuregate.FeatureSpec{
//!TODO: move this to validating specific tests
features.ValidatingAdmissionPolicy: {
Default: true, PreRelease: featuregate.Beta}})
if err != nil {
return nil, nil, err
}
err = featureGate.SetFromMap(map[string]bool{string(features.ValidatingAdmissionPolicy): true})
if err != nil {
return nil, nil, err
}
testContext, testCancel := context.WithCancel(context.Background())
genericInitializer := initializer.New(
nativeClient,

View File

@ -199,7 +199,9 @@ func TestReconcile(t *testing.T) {
go func() {
defer wg.Done()
stopReason := myController.Run(testContext)
require.ErrorIs(t, stopReason, context.Canceled)
if !errors.Is(stopReason, context.Canceled) {
t.Errorf("expected error to be context.Canceled, but got: %v", stopReason)
}
}()
// The controller is blocked because the reconcile function sends on an
@ -255,7 +257,9 @@ func TestShutdown(t *testing.T) {
go func() {
defer wg.Done()
stopReason := myController.Run(testContext)
require.ErrorIs(t, stopReason, context.Canceled)
if !errors.Is(stopReason, context.Canceled) {
t.Errorf("expected error to be context.Canceled, but got: %v", stopReason)
}
}()
// Wait for controller and informer to start up
@ -287,7 +291,9 @@ func TestInformerNeverStarts(t *testing.T) {
go func() {
defer wg.Done()
stopReason := myController.Run(testContext)
require.ErrorIs(t, stopReason, context.DeadlineExceeded)
if !errors.Is(stopReason, context.DeadlineExceeded) {
t.Errorf("expected error to be context.Canceled, but got: %v", stopReason)
}
}()
// Wait for deadline to pass without syncing the cache
@ -335,7 +341,9 @@ func TestIgnoredUpdate(t *testing.T) {
go func() {
defer wg.Done()
stopReason := myController.Run(testContext)
require.ErrorIs(t, stopReason, context.Canceled)
if !errors.Is(stopReason, context.Canceled) {
t.Errorf("expected error to be context.Canceled, but got: %v", stopReason)
}
}()
// The controller is blocked because the reconcile function sends on an
@ -392,7 +400,9 @@ func TestReconcileRetry(t *testing.T) {
go func() {
defer wg.Done()
stopReason := myController.Run(testContext)
require.ErrorIs(t, stopReason, context.Canceled)
if !errors.Is(stopReason, context.Canceled) {
t.Errorf("expected error to be context.Canceled, but got: %v", stopReason)
}
}()
// Add object to informer

View File

@ -0,0 +1,144 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
v1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
)
func NewMutatingAdmissionPolicyAccessor(obj *Policy) generic.PolicyAccessor {
return &mutatingAdmissionPolicyAccessor{
Policy: obj,
}
}
func NewMutatingAdmissionPolicyBindingAccessor(obj *PolicyBinding) generic.BindingAccessor {
return &mutatingAdmissionPolicyBindingAccessor{
PolicyBinding: obj,
}
}
type mutatingAdmissionPolicyAccessor struct {
*Policy
}
func (v *mutatingAdmissionPolicyAccessor) GetNamespace() string {
return v.Namespace
}
func (v *mutatingAdmissionPolicyAccessor) GetName() string {
return v.Name
}
func (v *mutatingAdmissionPolicyAccessor) GetParamKind() *v1.ParamKind {
pk := v.Spec.ParamKind
if pk == nil {
return nil
}
return &v1.ParamKind{
APIVersion: pk.APIVersion,
Kind: pk.Kind,
}
}
func (v *mutatingAdmissionPolicyAccessor) GetMatchConstraints() *v1.MatchResources {
return convertV1alpha1ResourceRulesToV1(v.Spec.MatchConstraints)
}
func (v *mutatingAdmissionPolicyAccessor) GetFailurePolicy() *v1.FailurePolicyType {
return toV1FailurePolicy(v.Spec.FailurePolicy)
}
func toV1FailurePolicy(failurePolicy *v1alpha1.FailurePolicyType) *v1.FailurePolicyType {
if failurePolicy == nil {
return nil
}
fp := v1.FailurePolicyType(*failurePolicy)
return &fp
}
type mutatingAdmissionPolicyBindingAccessor struct {
*PolicyBinding
}
func (v *mutatingAdmissionPolicyBindingAccessor) GetNamespace() string {
return v.Namespace
}
func (v *mutatingAdmissionPolicyBindingAccessor) GetName() string {
return v.Name
}
func (v *mutatingAdmissionPolicyBindingAccessor) GetPolicyName() types.NamespacedName {
return types.NamespacedName{
Namespace: "",
Name: v.Spec.PolicyName,
}
}
func (v *mutatingAdmissionPolicyBindingAccessor) GetMatchResources() *v1.MatchResources {
return convertV1alpha1ResourceRulesToV1(v.Spec.MatchResources)
}
func (v *mutatingAdmissionPolicyBindingAccessor) GetParamRef() *v1.ParamRef {
if v.Spec.ParamRef == nil {
return nil
}
var nfa *v1.ParameterNotFoundActionType
if v.Spec.ParamRef.ParameterNotFoundAction != nil {
nfa = new(v1.ParameterNotFoundActionType)
*nfa = v1.ParameterNotFoundActionType(*v.Spec.ParamRef.ParameterNotFoundAction)
}
return &v1.ParamRef{
Name: v.Spec.ParamRef.Name,
Namespace: v.Spec.ParamRef.Namespace,
Selector: v.Spec.ParamRef.Selector,
ParameterNotFoundAction: nfa,
}
}
func convertV1alpha1ResourceRulesToV1(mc *v1alpha1.MatchResources) *v1.MatchResources {
if mc == nil {
return nil
}
var res v1.MatchResources
res.NamespaceSelector = mc.NamespaceSelector
res.ObjectSelector = mc.ObjectSelector
for _, ex := range mc.ExcludeResourceRules {
res.ExcludeResourceRules = append(res.ExcludeResourceRules, v1.NamedRuleWithOperations{
ResourceNames: ex.ResourceNames,
RuleWithOperations: ex.RuleWithOperations,
})
}
for _, ex := range mc.ResourceRules {
res.ResourceRules = append(res.ResourceRules, v1.NamedRuleWithOperations{
ResourceNames: ex.ResourceNames,
RuleWithOperations: ex.RuleWithOperations,
})
}
if mc.MatchPolicy != nil {
mp := v1.MatchPolicyType(*mc.MatchPolicy)
res.MatchPolicy = &mp
}
return &res
}

View File

@ -0,0 +1,81 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
"fmt"
"k8s.io/api/admissionregistration/v1alpha1"
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
apiservercel "k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/environment"
)
// compilePolicy compiles the policy into a PolicyEvaluator
// any error is stored and delayed until invocation.
//
// Each individual mutation is compiled into MutationEvaluationFunc and
// returned is a PolicyEvaluator in the same order as the mutations appeared in the policy.
func compilePolicy(policy *Policy) PolicyEvaluator {
opts := plugincel.OptionalVariableDeclarations{HasParams: policy.Spec.ParamKind != nil, StrictCost: true, HasAuthorizer: true}
compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
if err != nil {
return PolicyEvaluator{Error: &apiservercel.Error{
Type: apiservercel.ErrorTypeInternal,
Detail: fmt.Sprintf("failed to initialize CEL compiler: %v", err),
}}
}
// Compile and store variables
compiler.CompileAndStoreVariables(convertv1alpha1Variables(policy.Spec.Variables), opts, environment.StoredExpressions)
// Compile matchers
var matcher matchconditions.Matcher = nil
matchConditions := policy.Spec.MatchConditions
if len(matchConditions) > 0 {
matchExpressionAccessors := make([]plugincel.ExpressionAccessor, len(matchConditions))
for i := range matchConditions {
matchExpressionAccessors[i] = (*matchconditions.MatchCondition)(&matchConditions[i])
}
matcher = matchconditions.NewMatcher(compiler.CompileCondition(matchExpressionAccessors, opts, environment.StoredExpressions), toV1FailurePolicy(policy.Spec.FailurePolicy), "policy", "validate", policy.Name)
}
// Compiler patchers
var patchers []patch.Patcher
patchOptions := opts
patchOptions.HasPatchTypes = true
for _, m := range policy.Spec.Mutations {
switch m.PatchType {
case v1alpha1.PatchTypeJSONPatch:
if m.JSONPatch != nil {
accessor := &patch.JSONPatchCondition{Expression: m.JSONPatch.Expression}
compileResult := compiler.CompileMutatingEvaluator(accessor, patchOptions, environment.StoredExpressions)
patchers = append(patchers, patch.NewJSONPatcher(compileResult))
}
case v1alpha1.PatchTypeApplyConfiguration:
if m.ApplyConfiguration != nil {
accessor := &patch.ApplyConfigurationCondition{Expression: m.ApplyConfiguration.Expression}
compileResult := compiler.CompileMutatingEvaluator(accessor, patchOptions, environment.StoredExpressions)
patchers = append(patchers, patch.NewApplyConfigurationPatcher(compileResult))
}
}
}
return PolicyEvaluator{Matcher: matcher, Mutators: patchers, CompositionEnv: compiler.CompositionEnv}
}

View File

@ -0,0 +1,457 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
"context"
"github.com/google/go-cmp/cmp"
"strings"
"testing"
"time"
"k8s.io/api/admissionregistration/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/openapi/openapitest"
"k8s.io/utils/ptr"
)
// TestCompilation is an open-box test of mutatingEvaluator.compile
// However, the result is a set of CEL programs, manually invoke them to assert
// on the results.
func TestCompilation(t *testing.T) {
deploymentGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
deploymentGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
testCases := []struct {
name string
policy *Policy
gvr schema.GroupVersionResource
object runtime.Object
oldObject runtime.Object
params runtime.Object
namespace *corev1.Namespace
expectedErr string
expectedResult runtime.Object
}{
{
name: "applyConfiguration then jsonPatch",
policy: mutations(policy("d1"), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + 100
}
}`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeJSONPatch,
JSONPatch: &v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas", value: object.spec.replicas + 10}
]`,
},
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](111)}},
},
{
name: "jsonPatch then applyConfiguration",
policy: mutations(policy("d1"),
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeJSONPatch,
JSONPatch: &v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas", value: object.spec.replicas + 10}
]`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + 100
}
}`,
},
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](111)}},
},
{
name: "jsonPatch with variable",
policy: jsonPatches(variables(policy("d1"), v1alpha1.Variable{Name: "desired", Expression: "10"}), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas", value: variables.desired + 1},
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](11)}},
},
{
name: "apply configuration with variable",
policy: applyConfigurations(variables(policy("d1"), v1alpha1.Variable{Name: "desired", Expression: "10"}),
`Object{
spec: Object.spec{
replicas: variables.desired + 1
}
}`),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](11)}},
},
{
name: "apply configuration with params",
policy: paramKind(applyConfigurations(policy("d1"),
`Object{
spec: Object.spec{
replicas: int(params.data['k1'])
}
}`), &v1alpha1.ParamKind{Kind: "ConfigMap", APIVersion: "v1"}),
params: &corev1.ConfigMap{Data: map[string]string{"k1": "100"}},
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](100)}},
},
{
name: "jsonPatch with excessive cost",
policy: jsonPatches(variables(policy("d1"), v1alpha1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: variables.list.all(x1, variables.list.all(x2, variables.list.all(x3, variables.list.all(x4, variables.list.all(x5, variables.list.all(x5, "0123456789" == "0123456789"))))))? 1 : 0
}
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "operation cancelled: actual cost limit exceeded",
},
{
name: "applyConfiguration with excessive cost",
policy: variables(applyConfigurations(policy("d1"),
`Object{
spec: Object.spec{
replicas: variables.list.all(x1, variables.list.all(x2, variables.list.all(x3, variables.list.all(x4, variables.list.all(x5, variables.list.all(x5, "0123456789" == "0123456789"))))))? 1 : 0
}
}`), v1alpha1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "operation cancelled: actual cost limit exceeded",
},
{
name: "request variable",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: request.kind.group == 'apps' && request.kind.version == 'v1' && request.kind.kind == 'Deployment' ? 10 : 0
}
]`}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](10)}},
},
{
name: "namespace request variable",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: namespaceObject.metadata.name == 'ns1' ? 10 : 0
}
]`}),
namespace: &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}},
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](10)}},
},
{
name: "authorizer check",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: authorizer.group('').resource('endpoints').check('create').allowed() ? 10 : 0
}
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](10)}},
},
{
name: "object type has field access",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
value: {
"value": Object{field: "fieldValue"}.field,
}
}
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"value": "fieldValue",
}}},
},
{
name: "object type has field testing",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
value: {
"field": string(has(Object{field: "fieldValue"}.field)),
"field-unset": string(has(Object{}.field)),
}
}
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"field": "true",
"field-unset": "false",
}}},
},
{
name: "object type equality",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
value: {
"empty": string(Object{} == Object{}),
"same": string(Object{field: "x"} == Object{field: "x"}),
"different": string(Object{field: "x"} == Object{field: "y"}),
}
}
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"empty": "true",
"same": "true",
"different": "false",
}}},
},
{
// TODO: This test documents existing behavior that we should be fixed before
// MutatingAdmissionPolicy graduates to beta.
// It is possible to initialize invalid Object types because we do not yet perform
// a full compilation pass with the types fully bound. Before beta, we should
// recompile all expressions with fully bound types before evaluation and report
// errors if invalid Object types like this are initialized.
name: "object types are not fully type checked",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/spec",
value: Object.invalid{replicas: 1}
}
]`,
}),
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}},
Spec: appsv1.DeploymentSpec{
Replicas: ptr.To[int32](1),
},
},
},
}
scheme := runtime.NewScheme()
err := appsv1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
tcManager := patch.NewTypeConverterManager(nil, openapitest.NewEmbeddedFileClient())
go tcManager.Run(ctx)
err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Second, true, func(context.Context) (done bool, err error) {
converter := tcManager.GetTypeConverter(deploymentGVK)
return converter != nil, nil
})
if err != nil {
t.Fatal(err)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var gvk schema.GroupVersionKind
gvks, _, err := scheme.ObjectKinds(tc.object)
if err != nil {
t.Fatal(err)
}
if len(gvks) == 1 {
gvk = gvks[0]
} else {
t.Fatalf("Failed to find gvk for type: %T", tc.object)
}
policyEvaluator := compilePolicy(tc.policy)
if policyEvaluator.CompositionEnv != nil {
ctx = policyEvaluator.CompositionEnv.CreateContext(ctx)
}
obj := tc.object
typeAccessor, err := meta.TypeAccessor(obj)
if err != nil {
t.Fatal(err)
}
typeAccessor.SetKind(gvk.Kind)
typeAccessor.SetAPIVersion(gvk.GroupVersion().String())
typeConverter := tcManager.GetTypeConverter(gvk)
metaAccessor, err := meta.Accessor(obj)
if err != nil {
t.Fatal(err)
}
for _, patcher := range policyEvaluator.Mutators {
attrs := admission.NewAttributesRecord(obj, tc.oldObject, gvk,
metaAccessor.GetNamespace(), metaAccessor.GetName(), tc.gvr,
"", admission.Create, &metav1.CreateOptions{}, false, nil)
vAttrs := &admission.VersionedAttributes{
Attributes: attrs,
VersionedKind: gvk,
VersionedObject: obj,
VersionedOldObject: tc.oldObject,
}
r := patch.Request{
MatchedResource: tc.gvr,
VersionedAttributes: vAttrs,
ObjectInterfaces: admission.NewObjectInterfacesFromScheme(scheme),
OptionalVariables: cel.OptionalVariableBindings{VersionedParams: tc.params, Authorizer: fakeAuthorizer{}},
Namespace: tc.namespace,
TypeConverter: typeConverter,
}
obj, err = patcher.Patch(ctx, r, celconfig.RuntimeCELCostBudget)
if len(tc.expectedErr) > 0 {
if err == nil {
t.Fatalf("expected error: %s", tc.expectedErr)
} else {
if !strings.Contains(err.Error(), tc.expectedErr) {
t.Fatalf("expected error: %s, got: %s", tc.expectedErr, err.Error())
}
return
}
}
if err != nil && len(tc.expectedErr) == 0 {
t.Fatalf("unexpected error: %v", err)
}
}
got, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
t.Fatal(err)
}
wantTypeAccessor, err := meta.TypeAccessor(tc.expectedResult)
if err != nil {
t.Fatal(err)
}
wantTypeAccessor.SetKind(gvk.Kind)
wantTypeAccessor.SetAPIVersion(gvk.GroupVersion().String())
want, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.expectedResult)
if err != nil {
t.Fatal(err)
}
if !equality.Semantic.DeepEqual(want, got) {
t.Errorf("unexpected result, got diff:\n%s\n", cmp.Diff(want, got))
}
})
}
}
func policy(name string) *v1alpha1.MutatingAdmissionPolicy {
return &v1alpha1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{},
}
}
func variables(policy *v1alpha1.MutatingAdmissionPolicy, variables ...v1alpha1.Variable) *v1alpha1.MutatingAdmissionPolicy {
policy.Spec.Variables = append(policy.Spec.Variables, variables...)
return policy
}
func jsonPatches(policy *v1alpha1.MutatingAdmissionPolicy, jsonPatches ...v1alpha1.JSONPatch) *v1alpha1.MutatingAdmissionPolicy {
for _, jsonPatch := range jsonPatches {
policy.Spec.Mutations = append(policy.Spec.Mutations, v1alpha1.Mutation{
JSONPatch: &jsonPatch,
PatchType: v1alpha1.PatchTypeJSONPatch,
})
}
return policy
}
func applyConfigurations(policy *v1alpha1.MutatingAdmissionPolicy, expressions ...string) *v1alpha1.MutatingAdmissionPolicy {
for _, expression := range expressions {
policy.Spec.Mutations = append(policy.Spec.Mutations, v1alpha1.Mutation{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{Expression: expression},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
})
}
return policy
}
func paramKind(policy *v1alpha1.MutatingAdmissionPolicy, paramKind *v1alpha1.ParamKind) *v1alpha1.MutatingAdmissionPolicy {
policy.Spec.ParamKind = paramKind
return policy
}
func mutations(policy *v1alpha1.MutatingAdmissionPolicy, mutations ...v1alpha1.Mutation) *v1alpha1.MutatingAdmissionPolicy {
policy.Spec.Mutations = append(policy.Spec.Mutations, mutations...)
return policy
}
func matchConstraints(policy *v1alpha1.MutatingAdmissionPolicy, matchConstraints *v1alpha1.MatchResources) *v1alpha1.MutatingAdmissionPolicy {
policy.Spec.MatchConstraints = matchConstraints
return policy
}
type fakeAuthorizer struct{}
func (f fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
return authorizer.DecisionAllow, "", nil
}

View File

@ -0,0 +1,295 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
"context"
"errors"
"fmt"
"k8s.io/api/admissionregistration/v1alpha1"
v1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission"
admissionauthorizer "k8s.io/apiserver/pkg/admission/plugin/authorizer"
"k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
webhookgeneric "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authorization/authorizer"
)
func NewDispatcher(a authorizer.Authorizer, m *matching.Matcher, tcm patch.TypeConverterManager) generic.Dispatcher[PolicyHook] {
res := &dispatcher{
matcher: m,
authz: a,
typeConverterManager: tcm,
}
res.Dispatcher = generic.NewPolicyDispatcher[*Policy, *PolicyBinding, PolicyEvaluator](
NewMutatingAdmissionPolicyAccessor,
NewMutatingAdmissionPolicyBindingAccessor,
m,
res.dispatchInvocations,
)
return res
}
type dispatcher struct {
matcher *matching.Matcher
authz authorizer.Authorizer
typeConverterManager patch.TypeConverterManager
generic.Dispatcher[PolicyHook]
}
func (d *dispatcher) Start(ctx context.Context) error {
go d.typeConverterManager.Run(ctx)
return d.Dispatcher.Start(ctx)
}
func (d *dispatcher) dispatchInvocations(
ctx context.Context,
a admission.Attributes,
o admission.ObjectInterfaces,
versionedAttributes webhookgeneric.VersionedAttributeAccessor,
invocations []generic.PolicyInvocation[*Policy, *PolicyBinding, PolicyEvaluator],
) ([]generic.PolicyError, *k8serrors.StatusError) {
var lastVersionedAttr *admission.VersionedAttributes
reinvokeCtx := a.GetReinvocationContext()
var policyReinvokeCtx *policyReinvokeContext
if v := reinvokeCtx.Value(PluginName); v != nil {
policyReinvokeCtx = v.(*policyReinvokeContext)
} else {
policyReinvokeCtx = &policyReinvokeContext{}
reinvokeCtx.SetValue(PluginName, policyReinvokeCtx)
}
if reinvokeCtx.IsReinvoke() && policyReinvokeCtx.IsOutputChangedSinceLastPolicyInvocation(a.GetObject()) {
// If the object has changed, we know the in-tree plugin re-invocations have mutated the object,
// and we need to reinvoke all eligible policies.
policyReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
}
defer func() {
policyReinvokeCtx.SetLastPolicyInvocationOutput(a.GetObject())
}()
var policyErrors []generic.PolicyError
addConfigError := func(err error, invocation generic.PolicyInvocation[*Policy, *PolicyBinding, PolicyEvaluator], reason metav1.StatusReason) {
policyErrors = append(policyErrors, generic.PolicyError{
Message: err,
Policy: NewMutatingAdmissionPolicyAccessor(invocation.Policy),
Binding: NewMutatingAdmissionPolicyBindingAccessor(invocation.Binding),
Reason: reason,
})
}
// There is at least one invocation to invoke. Make sure we have a namespace
// object if the incoming object is not cluster scoped to pass into the evaluator.
var namespace *v1.Namespace
var err error
namespaceName := a.GetNamespace()
// Special case, the namespace object has the namespace of itself (maybe a bug).
// unset it if the incoming object is a namespace
if gvk := a.GetKind(); gvk.Kind == "Namespace" && gvk.Version == "v1" && gvk.Group == "" {
namespaceName = ""
}
// if it is cluster scoped, namespaceName will be empty
// Otherwise, get the Namespace resource.
if namespaceName != "" {
namespace, err = d.matcher.GetNamespace(namespaceName)
if err != nil {
return nil, k8serrors.NewNotFound(schema.GroupResource{Group: "", Resource: "namespaces"}, namespaceName)
}
}
authz := admissionauthorizer.NewCachingAuthorizer(d.authz)
// Should loop through invocations, handling possible error and invoking
// evaluator to apply patch, also should handle re-invocations
for _, invocation := range invocations {
if invocation.Evaluator.CompositionEnv != nil {
ctx = invocation.Evaluator.CompositionEnv.CreateContext(ctx)
}
if len(invocation.Evaluator.Mutators) != len(invocation.Policy.Spec.Mutations) {
// This would be a bug. The compiler should always return exactly as
// many evaluators as there are mutations
return nil, k8serrors.NewInternalError(fmt.Errorf("expected %v compiled evaluators for policy %v, got %v",
len(invocation.Policy.Spec.Mutations), invocation.Policy.Name, len(invocation.Evaluator.Mutators)))
}
versionedAttr, err := versionedAttributes.VersionedAttribute(invocation.Kind)
if err != nil {
// This should never happen, we pre-warm versoined attribute
// accessors before starting the dispatcher
return nil, k8serrors.NewInternalError(err)
}
if invocation.Evaluator.Matcher != nil {
matchResults := invocation.Evaluator.Matcher.Match(ctx, versionedAttr, invocation.Param, authz)
if matchResults.Error != nil {
addConfigError(matchResults.Error, invocation, metav1.StatusReasonInvalid)
continue
}
// if preconditions are not met, then skip mutations
if !matchResults.Matches {
continue
}
}
invocationKey, invocationKeyErr := keyFor(invocation)
if invocationKeyErr != nil {
// This should never happen. It occurs if there is a programming
// error causing the Param not to be a valid object.
return nil, k8serrors.NewInternalError(invocationKeyErr)
}
if reinvokeCtx.IsReinvoke() && !policyReinvokeCtx.ShouldReinvoke(invocationKey) {
continue
}
objectBeforeMutations := versionedAttr.VersionedObject
// Mutations for a single invocation of a MutatingAdmissionPolicy are evaluated
// in order.
for mutationIndex := range invocation.Policy.Spec.Mutations {
lastVersionedAttr = versionedAttr
if versionedAttr.VersionedObject == nil { // Do not call patchers if there is no object to patch.
continue
}
patcher := invocation.Evaluator.Mutators[mutationIndex]
optionalVariables := cel.OptionalVariableBindings{VersionedParams: invocation.Param, Authorizer: authz}
err = d.dispatchOne(ctx, patcher, o, versionedAttr, namespace, invocation.Resource, optionalVariables)
if err != nil {
var statusError *k8serrors.StatusError
if errors.As(err, &statusError) {
return nil, statusError
}
addConfigError(err, invocation, metav1.StatusReasonInvalid)
continue
}
}
if !apiequality.Semantic.DeepEqual(objectBeforeMutations, versionedAttr.VersionedObject) {
// The mutation has changed the object. Prepare to reinvoke all previous mutations that are eligible for re-invocation.
policyReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
reinvokeCtx.SetShouldReinvoke()
}
if invocation.Policy.Spec.ReinvocationPolicy == v1alpha1.IfNeededReinvocationPolicy {
policyReinvokeCtx.AddReinvocablePolicyToPreviouslyInvoked(invocationKey)
}
}
if lastVersionedAttr != nil && lastVersionedAttr.VersionedObject != nil && lastVersionedAttr.Dirty {
policyReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
reinvokeCtx.SetShouldReinvoke()
if err := o.GetObjectConvertor().Convert(lastVersionedAttr.VersionedObject, lastVersionedAttr.Attributes.GetObject(), nil); err != nil {
return nil, k8serrors.NewInternalError(fmt.Errorf("failed to convert object: %w", err))
}
}
return policyErrors, nil
}
func (d *dispatcher) dispatchOne(
ctx context.Context,
patcher patch.Patcher,
o admission.ObjectInterfaces,
versionedAttributes *admission.VersionedAttributes,
namespace *v1.Namespace,
resource schema.GroupVersionResource,
optionalVariables cel.OptionalVariableBindings,
) (err error) {
if patcher == nil {
// internal error. this should not happen
return k8serrors.NewInternalError(fmt.Errorf("policy evaluator is nil"))
}
// Find type converter for the invoked Group-Version.
typeConverter := d.typeConverterManager.GetTypeConverter(versionedAttributes.VersionedKind)
if typeConverter == nil {
// This can happen if the request is for a resource whose schema
// has not been registered with the type converter manager.
return k8serrors.NewServiceUnavailable(fmt.Sprintf("Resource kind %s not found. There can be a delay between when CustomResourceDefinitions are created and when they are available.", versionedAttributes.VersionedKind))
}
patchRequest := patch.Request{
MatchedResource: resource,
VersionedAttributes: versionedAttributes,
ObjectInterfaces: o,
OptionalVariables: optionalVariables,
Namespace: namespace,
TypeConverter: typeConverter,
}
newVersionedObject, err := patcher.Patch(ctx, patchRequest, celconfig.RuntimeCELCostBudget)
if err != nil {
return err
}
switch versionedAttributes.VersionedObject.(type) {
case *unstructured.Unstructured:
// No conversion needed before defaulting for the patch object if the admitted object is unstructured.
default:
// Before defaulting, if the admitted object is a typed object, convert unstructured patch result back to a typed object.
newVersionedObject, err = o.GetObjectConvertor().ConvertToVersion(newVersionedObject, versionedAttributes.GetKind().GroupVersion())
if err != nil {
return err
}
}
o.GetObjectDefaulter().Default(newVersionedObject)
versionedAttributes.Dirty = true
versionedAttributes.VersionedObject = newVersionedObject
return nil
}
func keyFor(invocation generic.PolicyInvocation[*Policy, *PolicyBinding, PolicyEvaluator]) (key, error) {
var paramUID types.NamespacedName
if invocation.Param != nil {
paramAccessor, err := meta.Accessor(invocation.Param)
if err != nil {
// This should never happen, as the param should have been validated
// before being passed to the plugin.
return key{}, err
}
paramUID = types.NamespacedName{
Name: paramAccessor.GetName(),
Namespace: paramAccessor.GetNamespace(),
}
}
return key{
PolicyUID: types.NamespacedName{
Name: invocation.Policy.GetName(),
Namespace: invocation.Policy.GetNamespace(),
},
BindingUID: types.NamespacedName{
Name: invocation.Binding.GetName(),
Namespace: invocation.Binding.GetNamespace(),
},
ParamUID: paramUID,
}, nil
}

View File

@ -0,0 +1,715 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
"context"
"github.com/google/go-cmp/cmp"
"testing"
"time"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/openapi/openapitest"
"k8s.io/utils/ptr"
)
func TestDispatcher(t *testing.T) {
deploymentGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
deploymentGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
testCases := []struct {
name string
object, oldObject runtime.Object
gvk schema.GroupVersionKind
gvr schema.GroupVersionResource
params []runtime.Object // All params are expected to be ConfigMap for this test.
policyHooks []PolicyHook
expect runtime.Object
}{
{
name: "simple patch",
gvk: deploymentGVK,
gvr: deploymentGVR,
object: &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Replicas: ptr.To[int32](1),
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: mutations(matchConstraints(policy("policy1"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
}), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + 100
}
}`,
}}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
},
expect: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Replicas: ptr.To[int32](101),
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
},
{
name: "with param",
gvk: deploymentGVK,
gvr: deploymentGVR,
object: &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Replicas: ptr.To[int32](1),
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
params: []runtime.Object{
&corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: "cm1",
Namespace: "default",
},
Data: map[string]string{
"key": "10",
},
},
},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: paramKind(mutations(matchConstraints(policy("policy1"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
}}),
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + int(params.data['key'])
}
}`,
}}),
&v1alpha1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
ParamRef: &v1alpha1.ParamRef{Name: "cm1", Namespace: "default"},
},
}},
},
},
expect: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Replicas: ptr.To[int32](11),
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
},
{
name: "both policies reinvoked",
gvk: deploymentGVK,
gvr: deploymentGVR,
object: &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: mutations(matchConstraints(policy("policy1"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
}), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy1": string(int(object.?metadata.labels["count"].orValue("1")) + 1)}
}
}`,
}}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
{
Policy: mutations(matchConstraints(policy("policy2"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
}), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy2": string(int(object.?metadata.labels["count"].orValue("1")) + 1)}
}
}`,
}}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy2",
},
}},
},
},
expect: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
Labels: map[string]string{
"policy1": "2",
"policy2": "2",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
},
{
name: "1st policy sets match condition that 2nd policy matches",
gvk: deploymentGVK,
gvr: deploymentGVR,
object: &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy1",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"environment": "production"}
}
}`}},
},
},
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy2",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
},
MatchConditions: []v1alpha1.MatchCondition{
{
Name: "prodonly",
Expression: `object.?metadata.labels["environment"].orValue("") == "production"`,
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy1invoked": "true"}
}
}`}},
},
},
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy2",
},
}},
},
},
expect: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
Labels: map[string]string{
"environment": "production",
"policy1invoked": "true",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
},
{
// TODO: This behavior pre-exists with webhook match conditions but should be reconsidered
name: "1st policy still does not match after 2nd policy sets match condition",
gvk: deploymentGVK,
gvr: deploymentGVR,
object: &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy1",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
},
MatchConditions: []v1alpha1.MatchCondition{
{
Name: "prodonly",
Expression: `object.?metadata.labels["environment"].orValue("") == "production"`,
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy1invoked": "true"}
}
}`}},
},
},
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy2",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
Operations: []admissionregistrationv1.OperationType{"*"},
},
},
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"environment": "production"}
}
}`}},
},
},
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy2",
},
}},
},
},
expect: &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "d1",
Namespace: "default",
Labels: map[string]string{
"environment": "production",
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
},
}},
},
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tcManager := patch.NewTypeConverterManager(nil, openapitest.NewEmbeddedFileClient())
go tcManager.Run(ctx)
err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Second, true, func(context.Context) (done bool, err error) {
converter := tcManager.GetTypeConverter(deploymentGVK)
return converter != nil, nil
})
if err != nil {
t.Fatal(err)
}
scheme := runtime.NewScheme()
err = appsv1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}
err = corev1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}
// Register a fake defaulter since registering the full defaulter adds noise
// and creates dep cycles.
scheme.AddTypeDefaultingFunc(&appsv1.Deployment{},
func(obj interface{}) { fakeSetDefaultForDeployment(obj.(*appsv1.Deployment)) })
objectInterfaces := admission.NewObjectInterfacesFromScheme(scheme)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
client := fake.NewClientset(tc.params...)
// always include default namespace
err := client.Tracker().Add(&corev1.Namespace{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Namespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: "default",
},
Spec: corev1.NamespaceSpec{},
})
if err != nil {
t.Fatal(err)
}
informerFactory := informers.NewSharedInformerFactory(client, 0)
matcher := matching.NewMatcher(informerFactory.Core().V1().Namespaces().Lister(), client)
paramInformer, err := informerFactory.ForResource(schema.GroupVersionResource{Version: "v1", Resource: "configmaps"})
if err != nil {
t.Fatal(err)
}
informerFactory.Start(ctx.Done())
informerFactory.WaitForCacheSync(ctx.Done())
for i, h := range tc.policyHooks {
tc.policyHooks[i].ParamInformer = paramInformer
tc.policyHooks[i].ParamScope = testParamScope{}
tc.policyHooks[i].Evaluator = compilePolicy(h.Policy)
}
dispatcher := NewDispatcher(fakeAuthorizer{}, matcher, tcManager)
err = dispatcher.Start(ctx)
if err != nil {
t.Fatalf("error starting dispatcher: %v", err)
}
metaAccessor, err := meta.Accessor(tc.object)
if err != nil {
t.Fatal(err)
}
attrs := admission.NewAttributesRecord(tc.object, tc.oldObject, tc.gvk,
metaAccessor.GetNamespace(), metaAccessor.GetName(), tc.gvr,
"", admission.Create, &metav1.CreateOptions{}, false, nil)
vAttrs := &admission.VersionedAttributes{
Attributes: attrs,
VersionedKind: tc.gvk,
VersionedObject: tc.object,
VersionedOldObject: tc.oldObject,
}
err = dispatcher.Dispatch(ctx, vAttrs, objectInterfaces, tc.policyHooks)
if err != nil {
t.Fatalf("error dispatching policy hooks: %v", err)
}
obj := vAttrs.VersionedObject
if !equality.Semantic.DeepEqual(obj, tc.expect) {
t.Errorf("unexpected result, got diff:\n%s\n", cmp.Diff(tc.expect, obj))
}
})
}
}
type testParamScope struct{}
func (t testParamScope) Name() meta.RESTScopeName {
return meta.RESTScopeNameNamespace
}
var _ meta.RESTScope = testParamScope{}
func fakeSetDefaultForDeployment(obj *appsv1.Deployment) {
// Just default strategy type so the tests have a defaulted field to observe
strategy := &obj.Spec.Strategy
if strategy.Type == "" {
strategy.Type = appsv1.RollingUpdateDeploymentStrategyType
}
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/managedfields"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
)
// Patcher provides a patch function to perform a mutation to an object in the admission chain.
type Patcher interface {
// Patch returns a copy of the object in the request, modified to change specified by the patch.
// The original object in the request MUST NOT be modified in-place.
Patch(ctx context.Context, request Request, runtimeCELCostBudget int64) (runtime.Object, error)
}
// Request defines the arguments required by a patcher.
type Request struct {
MatchedResource schema.GroupVersionResource
VersionedAttributes *admission.VersionedAttributes
ObjectInterfaces admission.ObjectInterfaces
OptionalVariables cel.OptionalVariableBindings
Namespace *v1.Namespace
TypeConverter managedfields.TypeConverter
}

View File

@ -0,0 +1,192 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
gojson "encoding/json"
"errors"
"fmt"
celgo "github.com/google/cel-go/cel"
"reflect"
"strconv"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/traits"
"google.golang.org/protobuf/types/known/structpb"
jsonpatch "gopkg.in/evanphx/json-patch.v4"
admissionv1 "k8s.io/api/admission/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/cel/mutation"
"k8s.io/apiserver/pkg/cel/mutation/dynamic"
pointer "k8s.io/utils/ptr"
)
// JSONPatchCondition contains the inputs needed to compile and evaluate a cel expression
// that returns a JSON patch value.
type JSONPatchCondition struct {
Expression string
}
var _ plugincel.ExpressionAccessor = &JSONPatchCondition{}
func (v *JSONPatchCondition) GetExpression() string {
return v.Expression
}
func (v *JSONPatchCondition) ReturnTypes() []*celgo.Type {
return []*celgo.Type{celgo.ListType(jsonPatchType)}
}
var jsonPatchType = types.NewObjectType("JSONPatch")
// NewJSONPatcher creates a patcher that performs a JSON Patch mutation.
func NewJSONPatcher(patchEvaluator plugincel.MutatingEvaluator) Patcher {
return &jsonPatcher{patchEvaluator}
}
type jsonPatcher struct {
PatchEvaluator plugincel.MutatingEvaluator
}
func (e *jsonPatcher) Patch(ctx context.Context, r Request, runtimeCELCostBudget int64) (runtime.Object, error) {
admissionRequest := plugincel.CreateAdmissionRequest(
r.VersionedAttributes.Attributes,
metav1.GroupVersionResource(r.MatchedResource),
metav1.GroupVersionKind(r.VersionedAttributes.VersionedKind))
compileErrors := e.PatchEvaluator.CompilationErrors()
if len(compileErrors) > 0 {
return nil, errors.Join(compileErrors...)
}
patchObj, _, err := e.evaluatePatchExpression(ctx, e.PatchEvaluator, runtimeCELCostBudget, r, admissionRequest)
if err != nil {
return nil, err
}
o := r.ObjectInterfaces
jsonSerializer := json.NewSerializerWithOptions(json.DefaultMetaFactory, o.GetObjectCreater(), o.GetObjectTyper(), json.SerializerOptions{Pretty: false, Strict: true})
objJS, err := runtime.Encode(jsonSerializer, r.VersionedAttributes.VersionedObject)
if err != nil {
return nil, fmt.Errorf("failed to create JSON patch: %w", err)
}
patchedJS, err := patchObj.Apply(objJS)
if err != nil {
if errors.Is(err, jsonpatch.ErrTestFailed) {
// If a json patch fails a test operation, the patch must not be applied
return r.VersionedAttributes.VersionedObject, nil
}
return nil, fmt.Errorf("JSON Patch: %w", err)
}
var newVersionedObject runtime.Object
if _, ok := r.VersionedAttributes.VersionedObject.(*unstructured.Unstructured); ok {
newVersionedObject = &unstructured.Unstructured{}
} else {
newVersionedObject, err = o.GetObjectCreater().New(r.VersionedAttributes.VersionedKind)
if err != nil {
return nil, apierrors.NewInternalError(err)
}
}
if newVersionedObject, _, err = jsonSerializer.Decode(patchedJS, nil, newVersionedObject); err != nil {
return nil, apierrors.NewInternalError(err)
}
return newVersionedObject, nil
}
func (e *jsonPatcher) evaluatePatchExpression(ctx context.Context, patchEvaluator plugincel.MutatingEvaluator, remainingBudget int64, r Request, admissionRequest *admissionv1.AdmissionRequest) (jsonpatch.Patch, int64, error) {
var err error
var eval plugincel.EvaluationResult
eval, remainingBudget, err = patchEvaluator.ForInput(ctx, r.VersionedAttributes, admissionRequest, r.OptionalVariables, r.Namespace, remainingBudget)
if err != nil {
return nil, -1, err
}
if eval.Error != nil {
return nil, -1, eval.Error
}
refVal := eval.EvalResult
// the return type can be any valid CEL value.
// Scalars, maps and lists are used to set the value when the path points to a field of that type.
// ObjectVal is used when the path points to a struct. A map like "{"field1": 1, "fieldX": bool}" is not
// possible in Kubernetes CEL because maps and lists may not have mixed types.
iter, ok := refVal.(traits.Lister)
if !ok {
// Should never happen since compiler checks return type.
return nil, -1, fmt.Errorf("type mismatch: JSONPatchType.expression should evaluate to array")
}
result := jsonpatch.Patch{}
for it := iter.Iterator(); it.HasNext() == types.True; {
v := it.Next()
patchObj, err := v.ConvertToNative(reflect.TypeOf(&mutation.JSONPatchVal{}))
if err != nil {
// Should never happen since return type is checked by compiler.
return nil, -1, fmt.Errorf("type mismatch: JSONPatchType.expression should evaluate to array of JSONPatch: %w", err)
}
op, ok := patchObj.(*mutation.JSONPatchVal)
if !ok {
// Should never happen since return type is checked by compiler.
return nil, -1, fmt.Errorf("type mismatch: JSONPatchType.expression should evaluate to array of JSONPatch, got element of %T", patchObj)
}
// Construct a JSON Patch from the evaluated CEL expression
resultOp := jsonpatch.Operation{}
resultOp["op"] = pointer.To(gojson.RawMessage(strconv.Quote(op.Op)))
resultOp["path"] = pointer.To(gojson.RawMessage(strconv.Quote(op.Path)))
if len(op.From) > 0 {
resultOp["from"] = pointer.To(gojson.RawMessage(strconv.Quote(op.From)))
}
if op.Val != nil {
if objVal, ok := op.Val.(*dynamic.ObjectVal); ok {
// TODO: Object initializers are insufficiently type checked.
// In the interim, we use this sanity check to detect type mismatches
// between field names and Object initializers. For example,
// "Object.spec{ selector: Object.spec.wrong{}}" is detected as a mismatch.
// Before beta, attaching full type information both to Object initializers and
// the "object" and "oldObject" variables is needed. This will allow CEL to
// perform comprehensive runtime type checking.
err := objVal.CheckTypeNamesMatchFieldPathNames()
if err != nil {
return nil, -1, fmt.Errorf("type mismatch: %w", err)
}
}
// CEL data literals representing arbitrary JSON values can be serialized to JSON for use in
// JSON Patch if first converted to pb.Value.
v, err := op.Val.ConvertToNative(reflect.TypeOf(&structpb.Value{}))
if err != nil {
return nil, -1, fmt.Errorf("JSONPath valueExpression evaluated to a type that could not marshal to JSON: %w", err)
}
b, err := gojson.Marshal(v)
if err != nil {
return nil, -1, fmt.Errorf("JSONPath valueExpression evaluated to a type that could not marshal to JSON: %w", err)
}
resultOp["value"] = pointer.To[gojson.RawMessage](b)
}
result = append(result, resultOp)
}
return result, remainingBudget, nil
}

View File

@ -0,0 +1,476 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
"github.com/google/go-cmp/cmp"
"strings"
"testing"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/cel/environment"
"k8s.io/utils/ptr"
)
func TestJSONPatch(t *testing.T) {
deploymentGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
tests := []struct {
name string
expression string
gvr schema.GroupVersionResource
object, oldObject runtime.Object
expectedResult runtime.Object
expectedErr string
}{
{
name: "jsonPatch with false test operation",
expression: `[
JSONPatch{op: "test", path: "/spec/replicas", value: 100},
JSONPatch{op: "replace", path: "/spec/replicas", value: 3},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
},
{
name: "jsonPatch with true test operation",
expression: `[
JSONPatch{op: "test", path: "/spec/replicas", value: 1},
JSONPatch{op: "replace", path: "/spec/replicas", value: 3},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](3)}},
},
{
name: "jsonPatch remove to unset field",
expression: `[
JSONPatch{op: "remove", path: "/spec/replicas"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch remove map entry by key",
expression: `[
JSONPatch{op: "remove", path: "/metadata/labels/y"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1", "y": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch remove element in list",
expression: `[
JSONPatch{op: "remove", path: "/spec/template/spec/containers/1"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "b"}, {Name: "c"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "c"}},
}}}},
},
{
name: "jsonPatch copy map entry by key",
expression: `[
JSONPatch{op: "copy", from: "/metadata/labels/x", path: "/metadata/labels/y"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1", "y": "1"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch copy first element to end of list",
expression: `[
JSONPatch{op: "copy", from: "/spec/template/spec/containers/0", path: "/spec/template/spec/containers/-"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "b"}, {Name: "c"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "b"}, {Name: "c"}, {Name: "a"}},
}}}},
},
{
name: "jsonPatch move map entry by key",
expression: `[
JSONPatch{op: "move", from: "/metadata/labels/x", path: "/metadata/labels/y"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "1"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch move first element to end of list",
expression: `[
JSONPatch{op: "move", from: "/spec/template/spec/containers/0", path: "/spec/template/spec/containers/-"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "b"}, {Name: "c"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "b"}, {Name: "c"}, {Name: "a"}},
}}}},
},
{
name: "jsonPatch add map entry by key and value",
expression: `[
JSONPatch{op: "add", path: "/metadata/labels/x", value: "2"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "1", "x": "2"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch add map value to field",
expression: `[
JSONPatch{op: "add", path: "/metadata/labels", value: {"y": "2"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "2"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch add map to existing map", // performs a replacement
expression: `[
JSONPatch{op: "add", path: "/metadata/labels", value: {"y": "2"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "2"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch add to start of list",
expression: `[
JSONPatch{op: "add", path: "/spec/template/spec/containers/0", value: {"name": "x"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "x"}, {Name: "a"}},
}}}},
},
{
name: "jsonPatch add to end of list",
expression: `[
JSONPatch{op: "add", path: "/spec/template/spec/containers/-", value: {"name": "x"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "x"}},
}}}},
},
{
name: "jsonPatch replace key in map",
expression: `[
JSONPatch{op: "replace", path: "/metadata/labels/x", value: "2"},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "1", "x": "2"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch replace map value of unset field", // adds the field value
expression: `[
JSONPatch{op: "replace", path: "/metadata/labels", value: {"y": "2"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "2"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch replace map value of set field",
expression: `[
JSONPatch{op: "replace", path: "/metadata/labels", value: {"y": "2"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"x": "1"}}, Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"y": "2"}}, Spec: appsv1.DeploymentSpec{}},
},
{
name: "jsonPatch replace first element in list",
expression: `[
JSONPatch{op: "replace", path: "/spec/template/spec/containers/0", value: {"name": "x"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "x"}},
}}}},
},
{
name: "jsonPatch add map entry by key and value",
expression: `[
JSONPatch{op: "add", path: "/spec", value: Object.spec{selector: Object.spec.selector{}, replicas: 10}}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Selector: &metav1.LabelSelector{}, Replicas: ptr.To[int32](10)}},
},
{
name: "JSONPatch patch type has field access",
expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
value: {
"op": JSONPatch{op: "opValue"}.op,
"path": JSONPatch{path: "pathValue"}.path,
"from": JSONPatch{from: "fromValue"}.from,
"value": string(JSONPatch{value: "valueValue"}.value),
}
}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"op": "opValue",
"path": "pathValue",
"from": "fromValue",
"value": "valueValue",
}}},
},
{
name: "JSONPatch patch type has field testing",
expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
value: {
"op": string(has(JSONPatch{op: "opValue"}.op)),
"path": string(has(JSONPatch{path: "pathValue"}.path)),
"from": string(has(JSONPatch{from: "fromValue"}.from)),
"value": string(has(JSONPatch{value: "valueValue"}.value)),
"op-unset": string(has(JSONPatch{}.op)),
"path-unset": string(has(JSONPatch{}.path)),
"from-unset": string(has(JSONPatch{}.from)),
"value-unset": string(has(JSONPatch{}.value)),
}
}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"op": "true",
"path": "true",
"from": "true",
"value": "true",
"op-unset": "false",
"path-unset": "false",
"from-unset": "false",
"value-unset": "false",
}}},
},
{
name: "JSONPatch patch type equality",
expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
value: {
"empty": string(JSONPatch{} == JSONPatch{}),
"partial": string(JSONPatch{op: "add"} == JSONPatch{op: "add"}),
"same-all": string(JSONPatch{op: "add", path: "path", from: "from", value: 1} == JSONPatch{op: "add", path: "path", from: "from", value: 1}),
"different-op": string(JSONPatch{op: "add"} == JSONPatch{op: "remove"}),
"different-path": string(JSONPatch{op: "add", path: "x", from: "from", value: 1} == JSONPatch{op: "add", path: "path", from: "from", value: 1}),
"different-from": string(JSONPatch{op: "add", path: "path", from: "x", value: 1} == JSONPatch{op: "add", path: "path", from: "from", value: 1}),
"different-value": string(JSONPatch{op: "add", path: "path", from: "from", value: "1"} == JSONPatch{op: "add", path: "path", from: "from", value: 1}),
}
}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"empty": "true",
"partial": "true",
"same-all": "true",
"different-op": "false",
"different-path": "false",
"different-from": "false",
"different-value": "false",
}}},
},
{
name: "JSONPatch key escaping",
expression: `[
JSONPatch{
op: "add", path: "/metadata/labels", value: {}
},
JSONPatch{
op: "add", path: "/metadata/labels/" + jsonpatch.escapeKey("k8s.io/x~y"), value: "true"
}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}},
expectedResult: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"k8s.io/x~y": "true",
}}},
},
{
name: "jsonPatch with CEL initializer",
expression: `[
JSONPatch{op: "add", path: "/spec/template/spec/containers/-", value: Object.spec.template.spec.containers{
name: "x",
ports: [Object.spec.template.spec.containers.ports{containerPort: 8080}],
}
},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "x", Ports: []corev1.ContainerPort{{ContainerPort: 8080}}}},
}}}},
},
{
name: "jsonPatch invalid CEL initializer field",
expression: `[
JSONPatch{
op: "add", path: "/spec/template/spec/containers/-",
value: Object.spec.template.spec.containers{
name: "x",
ports: [Object.spec.template.spec.containers.ports{containerPortZ: 8080}]
}
}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedErr: "strict decoding error: unknown field \"spec.template.spec.containers[1].ports[0].containerPortZ\"",
},
{
name: "jsonPatch invalid CEL initializer type",
expression: `[
JSONPatch{
op: "add", path: "/spec/template/spec/containers/-",
value: Object.spec.template.spec.containers{
name: "x",
ports: [Object.spec.template.spec.containers.portsZ{containerPort: 8080}]
}
}
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedErr: " mismatch: unexpected type name \"Object.spec.template.spec.containers.portsZ\", expected \"Object.spec.template.spec.containers.ports\", which matches field name path from root Object type",
},
{
name: "jsonPatch replace end of list with - not allowed",
expression: `[
JSONPatch{op: "replace", path: "/spec/template/spec/containers/-", value: {"name": "x"}},
]`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}},
}}}},
expectedErr: "JSON Patch: replace operation does not apply: doc is missing key: /spec/template/spec/containers/-: missing value",
},
}
compiler, err := cel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
if err != nil {
t.Fatal(err)
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
accessor := &JSONPatchCondition{Expression: tc.expression}
compileResult := compiler.CompileMutatingEvaluator(accessor, cel.OptionalVariableDeclarations{StrictCost: true, HasPatchTypes: true}, environment.StoredExpressions)
patcher := jsonPatcher{PatchEvaluator: compileResult}
scheme := runtime.NewScheme()
err := appsv1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}
var gvk schema.GroupVersionKind
gvks, _, err := scheme.ObjectKinds(tc.object)
if err != nil {
t.Fatal(err)
}
if len(gvks) == 1 {
gvk = gvks[0]
} else {
t.Fatalf("Failed to find gvk for type: %T", tc.object)
}
metaAccessor, err := meta.Accessor(tc.object)
if err != nil {
t.Fatal(err)
}
attrs := admission.NewAttributesRecord(tc.object, tc.oldObject, gvk,
metaAccessor.GetNamespace(), metaAccessor.GetName(), tc.gvr,
"", admission.Create, &metav1.CreateOptions{}, false, nil)
vAttrs := &admission.VersionedAttributes{
Attributes: attrs,
VersionedKind: gvk,
VersionedObject: tc.object,
VersionedOldObject: tc.oldObject,
}
r := Request{
MatchedResource: tc.gvr,
VersionedAttributes: vAttrs,
ObjectInterfaces: admission.NewObjectInterfacesFromScheme(scheme),
OptionalVariables: cel.OptionalVariableBindings{},
}
got, err := patcher.Patch(context.Background(), r, celconfig.RuntimeCELCostBudget)
if len(tc.expectedErr) > 0 {
if err == nil {
t.Fatalf("expected error: %s", tc.expectedErr)
} else {
if !strings.Contains(err.Error(), tc.expectedErr) {
t.Fatalf("expected error: %s, got: %s", tc.expectedErr, err.Error())
}
return
}
}
if err != nil && len(tc.expectedErr) == 0 {
t.Fatalf("unexpected error: %v", err)
}
if !equality.Semantic.DeepEqual(tc.expectedResult, got) {
t.Errorf("unexpected result, got diff:\n%s\n", cmp.Diff(tc.expectedResult, got))
}
})
}
}

View File

@ -0,0 +1,217 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
"errors"
"fmt"
celgo "github.com/google/cel-go/cel"
celtypes "github.com/google/cel-go/common/types"
"strings"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
"sigs.k8s.io/structured-merge-diff/v4/schema"
"sigs.k8s.io/structured-merge-diff/v4/typed"
"sigs.k8s.io/structured-merge-diff/v4/value"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/managedfields"
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/cel/mutation/dynamic"
)
// ApplyConfigurationCondition contains the inputs needed to compile and evaluate a cel expression
// that returns an apply configuration
type ApplyConfigurationCondition struct {
Expression string
}
var _ plugincel.ExpressionAccessor = &ApplyConfigurationCondition{}
func (v *ApplyConfigurationCondition) GetExpression() string {
return v.Expression
}
func (v *ApplyConfigurationCondition) ReturnTypes() []*celgo.Type {
return []*celgo.Type{applyConfigObjectType}
}
var applyConfigObjectType = celtypes.NewObjectType("Object")
// NewApplyConfigurationPatcher creates a patcher that performs an applyConfiguration mutation.
func NewApplyConfigurationPatcher(expressionEvaluator plugincel.MutatingEvaluator) Patcher {
return &applyConfigPatcher{expressionEvaluator: expressionEvaluator}
}
type applyConfigPatcher struct {
expressionEvaluator plugincel.MutatingEvaluator
}
func (e *applyConfigPatcher) Patch(ctx context.Context, r Request, runtimeCELCostBudget int64) (runtime.Object, error) {
admissionRequest := plugincel.CreateAdmissionRequest(
r.VersionedAttributes.Attributes,
metav1.GroupVersionResource(r.MatchedResource),
metav1.GroupVersionKind(r.VersionedAttributes.VersionedKind))
compileErrors := e.expressionEvaluator.CompilationErrors()
if len(compileErrors) > 0 {
return nil, errors.Join(compileErrors...)
}
eval, _, err := e.expressionEvaluator.ForInput(ctx, r.VersionedAttributes, admissionRequest, r.OptionalVariables, r.Namespace, runtimeCELCostBudget)
if err != nil {
return nil, err
}
if eval.Error != nil {
return nil, eval.Error
}
v := eval.EvalResult
// The compiler ensures that the return type is an ObjectVal with type name of "Object".
objVal, ok := v.(*dynamic.ObjectVal)
if !ok {
// Should not happen since the compiler type checks the return type.
return nil, fmt.Errorf("unsupported return type from ApplyConfiguration expression: %v", v.Type())
}
// TODO: Object initializers are insufficiently type checked.
// In the interim, we use this sanity check to detect type mismatches
// between field names and Object initializers. For example,
// "Object.spec{ selector: Object.spec.wrong{}}" is detected as a mismatch.
// Before beta, attaching full type information both to Object initializers and
// the "object" and "oldObject" variables is needed. This will allow CEL to
// perform comprehensive runtime type checking.
err = objVal.CheckTypeNamesMatchFieldPathNames()
if err != nil {
return nil, fmt.Errorf("type mismatch: %w", err)
}
value, ok := objVal.Value().(map[string]any)
if !ok {
return nil, fmt.Errorf("invalid return type: %T", v)
}
patchObject := unstructured.Unstructured{Object: value}
patchObject.SetGroupVersionKind(r.VersionedAttributes.VersionedObject.GetObjectKind().GroupVersionKind())
patched, err := ApplyStructuredMergeDiff(r.TypeConverter, r.VersionedAttributes.VersionedObject, &patchObject)
if err != nil {
return nil, fmt.Errorf("error applying patch: %w", err)
}
return patched, nil
}
// ApplyStructuredMergeDiff applies a structured merge diff to an object and returns a copy of the object
// with the patch applied.
func ApplyStructuredMergeDiff(
typeConverter managedfields.TypeConverter,
originalObject runtime.Object,
patch *unstructured.Unstructured,
) (runtime.Object, error) {
if patch.GroupVersionKind() != originalObject.GetObjectKind().GroupVersionKind() {
return nil, fmt.Errorf("patch (%v) and original object (%v) are not of the same gvk", patch.GroupVersionKind().String(), originalObject.GetObjectKind().GroupVersionKind().String())
} else if typeConverter == nil {
return nil, fmt.Errorf("type converter must not be nil")
}
patchObjTyped, err := typeConverter.ObjectToTyped(patch)
if err != nil {
return nil, fmt.Errorf("failed to convert patch object to typed object: %w", err)
}
err = validatePatch(patchObjTyped)
if err != nil {
return nil, fmt.Errorf("invalid ApplyConfiguration: %w", err)
}
liveObjTyped, err := typeConverter.ObjectToTyped(originalObject)
if err != nil {
return nil, fmt.Errorf("failed to convert original object to typed object: %w", err)
}
newObjTyped, err := liveObjTyped.Merge(patchObjTyped)
if err != nil {
return nil, fmt.Errorf("failed to merge patch: %w", err)
}
// Our mutating admission policy sets the fields but does not track ownership.
// Newly introduced fields in the patch won't be tracked by a field manager
// (so if the original object is updated again but the mutating policy is
// not active, the fields will be dropped).
//
// This necessarily means that changes to an object by a mutating policy
// are only preserved if the policy was active at the time of the change.
// (If the policy is not active, the changes may be dropped.)
newObj, err := typeConverter.TypedToObject(newObjTyped)
if err != nil {
return nil, fmt.Errorf("failed to convert typed object to object: %w", err)
}
return newObj, nil
}
// validatePatch searches an apply configuration for any arrays, maps or structs elements that are atomic and returns
// an error if any are found.
// This prevents accidental removal of fields that can occur when the user intends to modify some
// fields in an atomic type, not realizing that all fields not explicitly set in the new value
// of the atomic will be removed.
func validatePatch(v *typed.TypedValue) error {
atomics := findAtomics(nil, v.Schema(), v.TypeRef(), v.AsValue())
if len(atomics) > 0 {
return fmt.Errorf("may not mutate atomic arrays, maps or structs: %v", strings.Join(atomics, ", "))
}
return nil
}
// findAtomics returns field paths for any atomic arrays, maps or structs found when traversing the given value.
func findAtomics(path []fieldpath.PathElement, s *schema.Schema, tr schema.TypeRef, v value.Value) (atomics []string) {
if a, ok := s.Resolve(tr); ok { // Validation pass happens before this and checks that all schemas can be resolved
if v.IsMap() && a.Map != nil {
if a.Map.ElementRelationship == schema.Atomic {
atomics = append(atomics, pathString(path))
}
v.AsMap().Iterate(func(key string, val value.Value) bool {
pe := fieldpath.PathElement{FieldName: &key}
if sf, ok := a.Map.FindField(key); ok {
tr = sf.Type
atomics = append(atomics, findAtomics(append(path, pe), s, tr, val)...)
}
return true
})
}
if v.IsList() && a.List != nil {
if a.List.ElementRelationship == schema.Atomic {
atomics = append(atomics, pathString(path))
}
list := v.AsList()
for i := 0; i < list.Length(); i++ {
pe := fieldpath.PathElement{Index: &i}
atomics = append(atomics, findAtomics(append(path, pe), s, a.List.ElementType, list.At(i))...)
}
}
}
return atomics
}
func pathString(path []fieldpath.PathElement) string {
sb := strings.Builder{}
for _, p := range path {
sb.WriteString(p.String())
}
return sb.String()
}

View File

@ -0,0 +1,346 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
"github.com/google/go-cmp/cmp"
"strings"
"testing"
"time"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/cel/environment"
"k8s.io/client-go/openapi/openapitest"
"k8s.io/utils/ptr"
)
func TestApplyConfiguration(t *testing.T) {
deploymentGVR := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
deploymentGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
tests := []struct {
name string
expression string
gvr schema.GroupVersionResource
object, oldObject runtime.Object
expectedResult runtime.Object
expectedErr string
}{
{
name: "apply configuration add to listType=map",
expression: `Object{
spec: Object.spec{
template: Object.spec.template{
spec: Object.spec.template.spec{
volumes: [Object.spec.template.spec.volumes{
name: "y"
}]
}
}
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}},
},
},
}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}, {Name: "y"}},
},
},
}},
},
{
name: "apply configuration update listType=map entry",
expression: `Object{
spec: Object.spec{
template: Object.spec.template{
spec: Object.spec.template.spec{
volumes: [Object.spec.template.spec.volumes{
name: "y",
hostPath: Object.spec.template.spec.volumes.hostPath{
path: "a"
}
}]
}
}
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}, {Name: "y"}},
},
},
}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{{Name: "x"}, {Name: "y", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "a"}}}},
},
},
}},
},
{
name: "apply configuration with conditionals",
expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas % 2 == 0?object.spec.replicas + 1:object.spec.replicas
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](2)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](3)}},
},
{
name: "apply configuration with old object",
expression: `Object{
spec: Object.spec{
replicas: oldObject.spec.replicas % 2 == 0?oldObject.spec.replicas + 1:oldObject.spec.replicas
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
oldObject: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](2)}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](3)}},
},
{
name: "complex apply configuration initialization",
expression: `Object{
spec: Object.spec{
replicas: 1,
template: Object.spec.template{
metadata: Object.spec.template.metadata{
labels: {"app": "nginx"}
},
spec: Object.spec.template.spec{
containers: [Object.spec.template.spec.containers{
name: "nginx",
image: "nginx:1.14.2",
ports: [Object.spec.template.spec.containers.ports{
containerPort: 80
}],
resources: Object.spec.template.spec.containers.resources{
limits: {"cpu": "128M"},
}
}]
}
}
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{}},
expectedResult: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{
Replicas: ptr.To[int32](1),
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "nginx"},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Name: "nginx",
Image: "nginx:1.14.2",
Ports: []corev1.ContainerPort{
{ContainerPort: 80},
},
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{corev1.ResourceName("cpu"): resource.MustParse("128M")},
},
}},
},
},
}},
},
{
name: "apply configuration with change to atomic",
expression: `Object{
spec: Object.spec{
selector: Object.spec.selector{
matchLabels: {"l": "v"}
}
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "error applying patch: invalid ApplyConfiguration: may not mutate atomic arrays, maps or structs: .spec.selector",
},
{
name: "apply configuration with invalid type name",
expression: `Object{
spec: Object.specx{
replicas: 1
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "type mismatch: unexpected type name \"Object.specx\", expected \"Object.spec\", which matches field name path from root Object type",
},
{
name: "apply configuration with invalid field name",
expression: `Object{
spec: Object.spec{
replicasx: 1
}
}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "error applying patch: failed to convert patch object to typed object: .spec.replicasx: field not declared in schema",
},
{
name: "apply configuration with invalid return type",
expression: `"I'm a teapot!"`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "must evaluate to Object but got string",
},
{
name: "apply configuration with invalid initializer return type",
expression: `Object.spec.metadata{}`,
gvr: deploymentGVR,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}},
expectedErr: "must evaluate to Object but got Object.spec.metadata",
},
}
compiler, err := cel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tcManager := NewTypeConverterManager(nil, openapitest.NewEmbeddedFileClient())
go tcManager.Run(ctx)
err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Second, true, func(context.Context) (done bool, err error) {
converter := tcManager.GetTypeConverter(deploymentGVK)
return converter != nil, nil
})
if err != nil {
t.Fatal(err)
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
accessor := &ApplyConfigurationCondition{Expression: tc.expression}
compileResult := compiler.CompileMutatingEvaluator(accessor, cel.OptionalVariableDeclarations{StrictCost: true, HasPatchTypes: true}, environment.StoredExpressions)
patcher := applyConfigPatcher{expressionEvaluator: compileResult}
scheme := runtime.NewScheme()
err := appsv1.AddToScheme(scheme)
if err != nil {
t.Fatal(err)
}
var gvk schema.GroupVersionKind
gvks, _, err := scheme.ObjectKinds(tc.object)
if err != nil {
t.Fatal(err)
}
if len(gvks) == 1 {
gvk = gvks[0]
} else {
t.Fatalf("Failed to find gvk for type: %T", tc.object)
}
metaAccessor, err := meta.Accessor(tc.object)
if err != nil {
t.Fatal(err)
}
typeAccessor, err := meta.TypeAccessor(tc.object)
if err != nil {
t.Fatal(err)
}
typeAccessor.SetKind(gvk.Kind)
typeAccessor.SetAPIVersion(gvk.GroupVersion().String())
attrs := admission.NewAttributesRecord(tc.object, tc.oldObject, gvk,
metaAccessor.GetNamespace(), metaAccessor.GetName(), tc.gvr,
"", admission.Create, &metav1.CreateOptions{}, false, nil)
vAttrs := &admission.VersionedAttributes{
Attributes: attrs,
VersionedKind: gvk,
VersionedObject: tc.object,
VersionedOldObject: tc.oldObject,
}
r := Request{
MatchedResource: tc.gvr,
VersionedAttributes: vAttrs,
ObjectInterfaces: admission.NewObjectInterfacesFromScheme(scheme),
OptionalVariables: cel.OptionalVariableBindings{},
TypeConverter: tcManager.GetTypeConverter(gvk),
}
patched, err := patcher.Patch(ctx, r, celconfig.RuntimeCELCostBudget)
if len(tc.expectedErr) > 0 {
if err == nil {
t.Fatalf("expected error: %s", tc.expectedErr)
} else {
if !strings.Contains(err.Error(), tc.expectedErr) {
t.Fatalf("expected error: %s, got: %s", tc.expectedErr, err.Error())
}
return
}
}
if err != nil && len(tc.expectedErr) == 0 {
t.Fatalf("unexpected error: %v", err)
}
got, err := runtime.DefaultUnstructuredConverter.ToUnstructured(patched)
if err != nil {
t.Fatal(err)
}
wantTypeAccessor, err := meta.TypeAccessor(tc.expectedResult)
if err != nil {
t.Fatal(err)
}
wantTypeAccessor.SetKind(gvk.Kind)
wantTypeAccessor.SetAPIVersion(gvk.GroupVersion().String())
want, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.expectedResult)
if err != nil {
t.Fatal(err)
}
if !equality.Semantic.DeepEqual(want, got) {
t.Errorf("unexpected result, got diff:\n%s\n", cmp.Diff(want, got))
}
})
}
}

View File

@ -0,0 +1,187 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
"encoding/json"
"fmt"
"strings"
"sync"
"time"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/managedfields"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/openapi"
"k8s.io/kube-openapi/pkg/spec3"
)
type TypeConverterManager interface {
// GetTypeConverter returns a type converter for the given GVK
GetTypeConverter(gvk schema.GroupVersionKind) managedfields.TypeConverter
Run(ctx context.Context)
}
func NewTypeConverterManager(
staticTypeConverter managedfields.TypeConverter,
openapiClient openapi.Client,
) TypeConverterManager {
return &typeConverterManager{
staticTypeConverter: staticTypeConverter,
openapiClient: openapiClient,
typeConverterMap: make(map[schema.GroupVersion]typeConverterCacheEntry),
lastFetchedPaths: make(map[schema.GroupVersion]openapi.GroupVersion),
}
}
type typeConverterCacheEntry struct {
typeConverter managedfields.TypeConverter
entry openapi.GroupVersion
}
// typeConverterManager helps us make sure we have an up to date schema and
// type converter for our openapi models. It should be connfigured to use a
// static type converter for natively typed schemas, and fetches the schema
// for CRDs/other over the network on demand (trying to reduce network calls where necessary)
type typeConverterManager struct {
// schemaCache is used to cache the schema for a given GVK
staticTypeConverter managedfields.TypeConverter
// discoveryClient is used to fetch the schema for a given GVK
openapiClient openapi.Client
lock sync.RWMutex
typeConverterMap map[schema.GroupVersion]typeConverterCacheEntry
lastFetchedPaths map[schema.GroupVersion]openapi.GroupVersion
}
func (t *typeConverterManager) Run(ctx context.Context) {
// Loop every 5s refershing the OpenAPI schema list to know which
// schemas have been invalidated. This should use e-tags under the hood
_ = wait.PollUntilContextCancel(ctx, 5*time.Second, true, func(_ context.Context) (done bool, err error) {
paths, err := t.openapiClient.Paths()
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to fetch openapi paths: %w", err))
return false, nil
}
// The /openapi/v3 endpoint contains a list of paths whose ServerRelativeURL
// value changes every time the schema is updated. So we poll /openapi/v3
// to get the "version number" for each schema, and invalidate our cache
// if the version number has changed since we pulled it.
parsedPaths := make(map[schema.GroupVersion]openapi.GroupVersion, len(paths))
for path, entry := range paths {
if !strings.HasPrefix(path, "apis/") && !strings.HasPrefix(path, "api/") {
continue
}
path = strings.TrimPrefix(path, "apis/")
path = strings.TrimPrefix(path, "api/")
gv, err := schema.ParseGroupVersion(path)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to parse group version %q: %w", path, err))
return false, nil
}
parsedPaths[gv] = entry
}
t.lock.Lock()
defer t.lock.Unlock()
t.lastFetchedPaths = parsedPaths
return false, nil
})
}
func (t *typeConverterManager) GetTypeConverter(gvk schema.GroupVersionKind) managedfields.TypeConverter {
// Check to see if the static type converter handles this GVK
if t.staticTypeConverter != nil {
//!TODO: Add ability to check existence to type converter
// working around for now but seeing if getting a typed version of an
// empty object returns error
stub := &unstructured.Unstructured{}
stub.SetGroupVersionKind(gvk)
if _, err := t.staticTypeConverter.ObjectToTyped(stub); err == nil {
return t.staticTypeConverter
}
}
gv := gvk.GroupVersion()
existing, entry, err := func() (managedfields.TypeConverter, openapi.GroupVersion, error) {
t.lock.RLock()
defer t.lock.RUnlock()
// If schema is not supported by static type converter, ask discovery
// for the schema
entry, ok := t.lastFetchedPaths[gv]
if !ok {
// If we can't get the schema, we can't do anything
return nil, nil, fmt.Errorf("no schema for %v", gvk)
}
// If the entry schema has not changed, used the same type converter
if existing, ok := t.typeConverterMap[gv]; ok && existing.entry.ServerRelativeURL() == entry.ServerRelativeURL() {
// If we have a type converter for this GVK, return it
return existing.typeConverter, existing.entry, nil
}
return nil, entry, nil
}()
if err != nil {
utilruntime.HandleError(err)
return nil
} else if existing != nil {
return existing
}
schBytes, err := entry.Schema(runtime.ContentTypeJSON)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to get schema for %v: %w", gvk, err))
return nil
}
var sch spec3.OpenAPI
if err := json.Unmarshal(schBytes, &sch); err != nil {
utilruntime.HandleError(fmt.Errorf("failed to unmarshal schema for %v: %w", gvk, err))
return nil
}
// The schema has changed, or there is no entry for it, generate
// a new type converter for this GV
tc, err := managedfields.NewTypeConverter(sch.Components.Schemas, false)
if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to create type converter for %v: %w", gvk, err))
return nil
}
t.lock.Lock()
defer t.lock.Unlock()
t.typeConverterMap[gv] = typeConverterCacheEntry{
typeConverter: tc,
entry: entry,
}
return tc
}

View File

@ -0,0 +1,100 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package patch
import (
"context"
"github.com/google/go-cmp/cmp"
"testing"
"time"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/openapi/openapitest"
)
func TestTypeConverter(t *testing.T) {
deploymentGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
tests := []struct {
name string
gvk schema.GroupVersionKind
object runtime.Object
}{
{
name: "simple round trip",
gvk: deploymentGVK,
object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Template: corev1.PodTemplateSpec{Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "a"}, {Name: "x", Ports: []corev1.ContainerPort{{ContainerPort: 8080}}}},
}}}},
},
}
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
tcManager := NewTypeConverterManager(nil, openapitest.NewEmbeddedFileClient())
go tcManager.Run(ctx)
err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Second, true, func(context.Context) (done bool, err error) {
converter := tcManager.GetTypeConverter(deploymentGVK)
return converter != nil, nil
})
if err != nil {
t.Fatal(err)
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
typeAccessor, err := meta.TypeAccessor(tc.object)
if err != nil {
t.Fatal(err)
}
typeAccessor.SetKind(tc.gvk.Kind)
typeAccessor.SetAPIVersion(tc.gvk.GroupVersion().String())
converter := tcManager.GetTypeConverter(tc.gvk)
if converter == nil {
t.Errorf("nil TypeConverter")
}
typedObject, err := converter.ObjectToTyped(tc.object)
if err != nil {
t.Fatal(err)
}
roundTripped, err := converter.TypedToObject(typedObject)
if err != nil {
t.Fatal(err)
}
got, err := runtime.DefaultUnstructuredConverter.ToUnstructured(roundTripped)
if err != nil {
t.Fatal(err)
}
want, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.object)
if err != nil {
t.Fatal(err)
}
if !equality.Semantic.DeepEqual(want, got) {
t.Errorf("unexpected result, got diff:\n%s\n", cmp.Diff(want, got))
}
})
}
}

View File

@ -0,0 +1,151 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
"context"
celgo "github.com/google/cel-go/cel"
"io"
"k8s.io/api/admissionregistration/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/managedfields"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/features"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
)
const (
// PluginName indicates the name of admission plug-in
PluginName = "MutatingAdmissionPolicy"
)
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(configFile io.Reader) (admission.Interface, error) {
return NewPlugin(configFile), nil
})
}
type Policy = v1alpha1.MutatingAdmissionPolicy
type PolicyBinding = v1alpha1.MutatingAdmissionPolicyBinding
type PolicyMutation = v1alpha1.Mutation
type PolicyHook = generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]
type Mutator struct {
}
type MutationEvaluationFunc func(
ctx context.Context,
matchedResource schema.GroupVersionResource,
versionedAttr *admission.VersionedAttributes,
o admission.ObjectInterfaces,
versionedParams runtime.Object,
namespace *corev1.Namespace,
typeConverter managedfields.TypeConverter,
runtimeCELCostBudget int64,
authorizer authorizer.Authorizer,
) (runtime.Object, error)
type PolicyEvaluator struct {
Matcher matchconditions.Matcher
Mutators []patch.Patcher
CompositionEnv *cel.CompositionEnv
Error error
}
// Plugin is an implementation of admission.Interface.
type Plugin struct {
*generic.Plugin[PolicyHook]
}
var _ admission.Interface = &Plugin{}
var _ admission.MutationInterface = &Plugin{}
// NewPlugin returns a generic admission webhook plugin.
func NewPlugin(_ io.Reader) *Plugin {
// There is no request body to mutate for DELETE, so this plugin never handles that operation.
handler := admission.NewHandler(admission.Create, admission.Update, admission.Connect)
res := &Plugin{}
res.Plugin = generic.NewPlugin(
handler,
func(f informers.SharedInformerFactory, client kubernetes.Interface, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) generic.Source[PolicyHook] {
return generic.NewPolicySource(
f.Admissionregistration().V1alpha1().MutatingAdmissionPolicies().Informer(),
f.Admissionregistration().V1alpha1().MutatingAdmissionPolicyBindings().Informer(),
NewMutatingAdmissionPolicyAccessor,
NewMutatingAdmissionPolicyBindingAccessor,
compilePolicy,
//!TODO: Create a way to share param informers between
// mutating/validating plugins
f,
dynamicClient,
restMapper,
)
},
func(a authorizer.Authorizer, m *matching.Matcher, client kubernetes.Interface) generic.Dispatcher[PolicyHook] {
return NewDispatcher(a, m, patch.NewTypeConverterManager(nil, client.Discovery().OpenAPIV3()))
},
)
return res
}
// Admit makes an admission decision based on the request attributes.
func (a *Plugin) Admit(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
return a.Plugin.Dispatch(ctx, attr, o)
}
func (a *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
a.Plugin.SetEnabled(featureGates.Enabled(features.MutatingAdmissionPolicy))
}
// Variable is a named expression for composition.
type Variable struct {
Name string
Expression string
}
func (v *Variable) GetExpression() string {
return v.Expression
}
func (v *Variable) ReturnTypes() []*celgo.Type {
return []*celgo.Type{celgo.AnyType, celgo.DynType}
}
func (v *Variable) GetName() string {
return v.Name
}
func convertv1alpha1Variables(variables []v1alpha1.Variable) []cel.NamedExpressionAccessor {
namedExpressions := make([]cel.NamedExpressionAccessor, len(variables))
for i, variable := range variables {
namedExpressions[i] = &Variable{Name: variable.Name, Expression: variable.Expression}
}
return namedExpressions
}

View File

@ -0,0 +1,354 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating_test
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/api/admissionregistration/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/openapi/openapitest"
"k8s.io/utils/ptr"
)
func setupTest(
t *testing.T,
compiler func(*mutating.Policy) mutating.PolicyEvaluator,
) *generic.PolicyTestContext[*mutating.Policy, *mutating.PolicyBinding, mutating.PolicyEvaluator] {
testContext, testCancel, err := generic.NewPolicyTestContext[*mutating.Policy, *mutating.PolicyBinding, mutating.PolicyEvaluator](
mutating.NewMutatingAdmissionPolicyAccessor,
mutating.NewMutatingAdmissionPolicyBindingAccessor,
compiler,
func(a authorizer.Authorizer, m *matching.Matcher, i kubernetes.Interface) generic.Dispatcher[mutating.PolicyHook] {
// Use embedded schemas rather than discovery schemas
return mutating.NewDispatcher(a, m, patch.NewTypeConverterManager(nil, openapitest.NewEmbeddedFileClient()))
},
nil,
[]meta.RESTMapping{
{
Resource: schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "pods",
},
GroupVersionKind: schema.GroupVersionKind{
Group: "",
Version: "v1",
Kind: "Pod",
},
Scope: meta.RESTScopeNamespace,
},
})
require.NoError(t, err)
t.Cleanup(testCancel)
require.NoError(t, testContext.Start())
return testContext
}
// Show that a compiler that always sets an annotation on the object works
func TestBasicPatch(t *testing.T) {
expectedAnnotations := map[string]string{"foo": "bar"}
// Treat all policies as setting foo annotation to bar
testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator {
return mutating.PolicyEvaluator{Mutators: []patch.Patcher{annotationPatcher{expectedAnnotations}}}
})
// Set up a policy and binding that match, no params
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
))
// Show that if we run an object through the policy, it gets the annotation
testObject := &corev1.ConfigMap{}
err := testContext.Dispatch(testObject, nil, admission.Create)
require.NoError(t, err)
require.Equal(t, expectedAnnotations, testObject.Annotations)
}
func TestJSONPatch(t *testing.T) {
patchObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"foo": "bar",
},
},
"data": map[string]interface{}{
"myfield": "myvalue",
},
},
}
testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator {
return mutating.PolicyEvaluator{
Mutators: []patch.Patcher{smdPatcher{patch: patchObj}},
}
})
// Set up a policy and binding that match, no params
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
{
JSONPatch: &v1alpha1.JSONPatch{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
))
// Show that if we run an object through the policy, it gets the annotation
testObject := &corev1.ConfigMap{}
err := testContext.Dispatch(testObject, nil, admission.Create)
require.NoError(t, err)
require.Equal(t, &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"foo": "bar"},
},
Data: map[string]string{"myfield": "myvalue"},
}, testObject)
}
func TestSSAPatch(t *testing.T) {
patchObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"foo": "bar",
},
},
"data": map[string]interface{}{
"myfield": "myvalue",
},
},
}
testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator {
return mutating.PolicyEvaluator{
Mutators: []patch.Patcher{smdPatcher{patch: patchObj}},
}
})
// Set up a policy and binding that match, no params
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
))
// Show that if we run an object through the policy, it gets the annotation
testObject := &corev1.ConfigMap{}
err := testContext.Dispatch(testObject, nil, admission.Create)
require.NoError(t, err)
require.Equal(t, &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"foo": "bar"},
},
Data: map[string]string{"myfield": "myvalue"},
}, testObject)
}
func TestSSAMapList(t *testing.T) {
patchObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"foo": "bar",
},
},
"spec": map[string]interface{}{
"initContainers": []interface{}{
map[string]interface{}{
"name": "injected-init-container",
"image": "injected-image",
},
},
},
},
}
testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator {
return mutating.PolicyEvaluator{
Mutators: []patch.Patcher{smdPatcher{patch: patchObj}},
}
})
// Set up a policy and binding that match, no params
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
))
// Show that if we run an object through the policy, it gets the annotation
testObject := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init-container",
Image: "image",
},
},
},
}
err := testContext.Dispatch(testObject, nil, admission.Create)
require.NoError(t, err)
require.Equal(t, &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"foo": "bar"},
},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init-container",
Image: "image",
},
{
Name: "injected-init-container",
Image: "injected-image",
},
},
},
}, testObject)
}
type annotationPatcher struct {
annotations map[string]string
}
func (ap annotationPatcher) Patch(ctx context.Context, request patch.Request, runtimeCELCostBudget int64) (runtime.Object, error) {
obj := request.VersionedAttributes.VersionedObject.DeepCopyObject()
accessor, err := meta.Accessor(obj)
if err != nil {
return nil, err
}
accessor.SetAnnotations(ap.annotations)
return obj, nil
}
type smdPatcher struct {
patch *unstructured.Unstructured
}
func (sp smdPatcher) Patch(ctx context.Context, request patch.Request, runtimeCELCostBudget int64) (runtime.Object, error) {
return patch.ApplyStructuredMergeDiff(request.TypeConverter, request.VersionedAttributes.VersionedObject, sp.patch)
}

View File

@ -0,0 +1,76 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
)
type key struct {
PolicyUID types.NamespacedName
BindingUID types.NamespacedName
ParamUID types.NamespacedName
MutationIndex int
}
type policyReinvokeContext struct {
// lastPolicyOutput holds the result of the last Policy admission plugin call
lastPolicyOutput runtime.Object
// previouslyInvokedReinvocablePolicys holds the set of policies that have been invoked and
// should be reinvoked if a later mutation occurs
previouslyInvokedReinvocablePolicies sets.Set[key]
// reinvokePolicies holds the set of Policies that should be reinvoked
reinvokePolicies sets.Set[key]
}
func (rc *policyReinvokeContext) ShouldReinvoke(policy key) bool {
return rc.reinvokePolicies.Has(policy)
}
func (rc *policyReinvokeContext) IsOutputChangedSinceLastPolicyInvocation(object runtime.Object) bool {
return !apiequality.Semantic.DeepEqual(rc.lastPolicyOutput, object)
}
func (rc *policyReinvokeContext) SetLastPolicyInvocationOutput(object runtime.Object) {
if object == nil {
rc.lastPolicyOutput = nil
return
}
rc.lastPolicyOutput = object.DeepCopyObject()
}
func (rc *policyReinvokeContext) AddReinvocablePolicyToPreviouslyInvoked(policy key) {
if rc.previouslyInvokedReinvocablePolicies == nil {
rc.previouslyInvokedReinvocablePolicies = sets.New[key]()
}
rc.previouslyInvokedReinvocablePolicies.Insert(policy)
}
func (rc *policyReinvokeContext) RequireReinvokingPreviouslyInvokedPlugins() {
if len(rc.previouslyInvokedReinvocablePolicies) > 0 {
if rc.reinvokePolicies == nil {
rc.reinvokePolicies = sets.New[key]()
}
for s := range rc.previouslyInvokedReinvocablePolicies {
rc.reinvokePolicies.Insert(s)
}
rc.previouslyInvokedReinvocablePolicies = sets.New[key]()
}
}

View File

@ -0,0 +1,147 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutating
import (
"github.com/stretchr/testify/assert"
"testing"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
)
func TestFullReinvocation(t *testing.T) {
key1 := key{PolicyUID: types.NamespacedName{Name: "p1"}, BindingUID: types.NamespacedName{Name: "b1"}}
key2 := key{PolicyUID: types.NamespacedName{Name: "p2"}, BindingUID: types.NamespacedName{Name: "b2"}}
key3 := key{PolicyUID: types.NamespacedName{Name: "p3"}, BindingUID: types.NamespacedName{Name: "b3"}}
cm1v1 := &v1.ConfigMap{Data: map[string]string{"v": "1"}}
cm1v2 := &v1.ConfigMap{Data: map[string]string{"v": "2"}}
rc := policyReinvokeContext{}
// key1 is invoked and it updates the configmap
rc.SetLastPolicyInvocationOutput(cm1v1)
rc.RequireReinvokingPreviouslyInvokedPlugins()
rc.AddReinvocablePolicyToPreviouslyInvoked(key1)
assert.True(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v2))
// key2 is invoked and it updates the configmap
rc.SetLastPolicyInvocationOutput(cm1v2)
rc.RequireReinvokingPreviouslyInvokedPlugins()
rc.AddReinvocablePolicyToPreviouslyInvoked(key2)
assert.True(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v1))
// key3 is invoked but it does not change anything
rc.AddReinvocablePolicyToPreviouslyInvoked(key3)
assert.False(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v2))
// key1 is reinvoked
assert.True(t, rc.ShouldReinvoke(key1))
rc.AddReinvocablePolicyToPreviouslyInvoked(key1)
rc.SetLastPolicyInvocationOutput(cm1v1)
assert.True(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v2))
rc.RequireReinvokingPreviouslyInvokedPlugins()
// key2 is reinvoked
assert.True(t, rc.ShouldReinvoke(key2))
rc.AddReinvocablePolicyToPreviouslyInvoked(key2)
rc.SetLastPolicyInvocationOutput(cm1v2)
assert.True(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v1))
rc.RequireReinvokingPreviouslyInvokedPlugins()
// key3 is reinvoked, because the reinvocations have changed the resource
assert.True(t, rc.ShouldReinvoke(key3))
}
func TestPartialReinvocation(t *testing.T) {
key1 := key{PolicyUID: types.NamespacedName{Name: "p1"}, BindingUID: types.NamespacedName{Name: "b1"}}
key2 := key{PolicyUID: types.NamespacedName{Name: "p2"}, BindingUID: types.NamespacedName{Name: "b2"}}
key3 := key{PolicyUID: types.NamespacedName{Name: "p3"}, BindingUID: types.NamespacedName{Name: "b3"}}
cm1v1 := &v1.ConfigMap{Data: map[string]string{"v": "1"}}
cm1v2 := &v1.ConfigMap{Data: map[string]string{"v": "2"}}
rc := policyReinvokeContext{}
// key1 is invoked and it updates the configmap
rc.SetLastPolicyInvocationOutput(cm1v1)
rc.RequireReinvokingPreviouslyInvokedPlugins()
rc.AddReinvocablePolicyToPreviouslyInvoked(key1)
assert.True(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v2))
// key2 is invoked and it updates the configmap
rc.SetLastPolicyInvocationOutput(cm1v2)
rc.RequireReinvokingPreviouslyInvokedPlugins()
rc.AddReinvocablePolicyToPreviouslyInvoked(key2)
assert.True(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v1))
// key3 is invoked but it does not change anything
rc.AddReinvocablePolicyToPreviouslyInvoked(key3)
assert.False(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v2))
// key1 is reinvoked but does not change anything
assert.True(t, rc.ShouldReinvoke(key1))
// key2 is not reinvoked because nothing changed since last invocation
assert.False(t, rc.ShouldReinvoke(key2))
// key3 is not reinvoked because nothing changed since last invocation
assert.False(t, rc.ShouldReinvoke(key3))
}
func TestNoReinvocation(t *testing.T) {
key1 := key{PolicyUID: types.NamespacedName{Name: "p1"}, BindingUID: types.NamespacedName{Name: "b1"}}
key2 := key{PolicyUID: types.NamespacedName{Name: "p2"}, BindingUID: types.NamespacedName{Name: "b2"}}
key3 := key{PolicyUID: types.NamespacedName{Name: "p3"}, BindingUID: types.NamespacedName{Name: "b3"}}
cm1v1 := &v1.ConfigMap{Data: map[string]string{"v": "1"}}
rc := policyReinvokeContext{}
// key1 is invoked and it updates the configmap
rc.AddReinvocablePolicyToPreviouslyInvoked(key1)
rc.SetLastPolicyInvocationOutput(cm1v1)
assert.False(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v1))
// key2 is invoked but does not change anything
rc.AddReinvocablePolicyToPreviouslyInvoked(key2)
rc.SetLastPolicyInvocationOutput(cm1v1)
assert.False(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v1))
// key3 is invoked but it does not change anything
rc.AddReinvocablePolicyToPreviouslyInvoked(key3)
rc.SetLastPolicyInvocationOutput(cm1v1)
assert.False(t, rc.IsOutputChangedSinceLastPolicyInvocation(cm1v1))
// no keys are reinvoked
assert.False(t, rc.ShouldReinvoke(key1))
assert.False(t, rc.ShouldReinvoke(key2))
assert.False(t, rc.ShouldReinvoke(key3))
}

View File

@ -54,6 +54,10 @@ func (v *validatingAdmissionPolicyAccessor) GetMatchConstraints() *v1.MatchResou
return v.Spec.MatchConstraints
}
func (v *validatingAdmissionPolicyAccessor) GetFailurePolicy() *v1.FailurePolicyType {
return v.Spec.FailurePolicy
}
type validatingAdmissionPolicyBindingAccessor struct {
*v1.ValidatingAdmissionPolicyBinding
}

View File

@ -45,6 +45,7 @@ import (
auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/warning"
"k8s.io/client-go/kubernetes"
)
var (
@ -364,7 +365,7 @@ func setupTestCommon(
func(p *validating.Policy) validating.Validator {
return compiler.CompilePolicy(p)
},
func(a authorizer.Authorizer, m *matching.Matcher) generic.Dispatcher[validating.PolicyHook] {
func(a authorizer.Authorizer, m *matching.Matcher, client kubernetes.Interface) generic.Dispatcher[validating.PolicyHook] {
coolMatcher := matcher
if coolMatcher == nil {
coolMatcher = generic.NewPolicyMatcher(m)
@ -602,7 +603,7 @@ func TestDefinitionDoesntMatch(t *testing.T) {
nil, matchingParams,
admission.Create), &admission.RuntimeObjectInterfaces{}),
`Denied`)
require.Equal(t, numCompiles, 1)
require.Equal(t, 1, numCompiles)
}
func TestReconfigureBinding(t *testing.T) {

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
utiljson "k8s.io/apimachinery/pkg/util/json"
"k8s.io/apiserver/pkg/admission"
admissionauthorizer "k8s.io/apiserver/pkg/admission/plugin/authorizer"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
celmetrics "k8s.io/apiserver/pkg/admission/plugin/policy/validating/metrics"
celconfig "k8s.io/apiserver/pkg/apis/cel"
@ -63,6 +64,10 @@ type policyDecisionWithMetadata struct {
Binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding
}
func (c *dispatcher) Start(ctx context.Context) error {
return nil
}
// Dispatch implements generic.Dispatcher.
func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, hooks []PolicyHook) error {
@ -109,7 +114,7 @@ func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o adm
}
}
authz := newCachingAuthorizer(c.authz)
authz := admissionauthorizer.NewCachingAuthorizer(c.authz)
for _, hook := range hooks {
// versionedAttributes will be set to non-nil inside of the loop, but

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
package metrics
import (
"errors"

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
package metrics
import (
"context"

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
package metrics
import (
"context"

View File

@ -36,7 +36,6 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/featuregate"
)
const (
@ -93,13 +92,12 @@ type Plugin struct {
var _ admission.Interface = &Plugin{}
var _ admission.ValidationInterface = &Plugin{}
var _ initializer.WantsFeatures = &Plugin{}
var _ initializer.WantsExcludedAdmissionResources = &Plugin{}
func NewPlugin(_ io.Reader) *Plugin {
handler := admission.NewHandler(admission.Connect, admission.Create, admission.Delete, admission.Update)
return &Plugin{
p := &Plugin{
Plugin: generic.NewPlugin(
handler,
func(f informers.SharedInformerFactory, client kubernetes.Interface, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) generic.Source[PolicyHook] {
@ -114,11 +112,13 @@ func NewPlugin(_ io.Reader) *Plugin {
restMapper,
)
},
func(a authorizer.Authorizer, m *matching.Matcher) generic.Dispatcher[PolicyHook] {
func(a authorizer.Authorizer, m *matching.Matcher, client kubernetes.Interface) generic.Dispatcher[PolicyHook] {
return NewDispatcher(a, generic.NewPolicyMatcher(m))
},
),
}
p.SetEnabled(true)
return p
}
// Validate makes an admission decision based on the request attributes.
@ -126,10 +126,6 @@ func (a *Plugin) Validate(ctx context.Context, attr admission.Attributes, o admi
return a.Plugin.Dispatch(ctx, attr, o)
}
func (a *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
a.Plugin.SetEnabled(featureGates.Enabled(features.ValidatingAdmissionPolicy))
}
func compilePolicy(policy *Policy) Validator {
hasParam := false
if policy.Spec.ParamKind != nil {
@ -155,13 +151,13 @@ func compilePolicy(policy *Policy) Validator {
for i := range matchConditions {
matchExpressionAccessors[i] = (*matchconditions.MatchCondition)(&matchConditions[i])
}
matcher = matchconditions.NewMatcher(filterCompiler.Compile(matchExpressionAccessors, optionalVars, environment.StoredExpressions), failurePolicy, "policy", "validate", policy.Name)
matcher = matchconditions.NewMatcher(filterCompiler.CompileCondition(matchExpressionAccessors, optionalVars, environment.StoredExpressions), failurePolicy, "policy", "validate", policy.Name)
}
res := NewValidator(
filterCompiler.Compile(convertv1Validations(policy.Spec.Validations), optionalVars, environment.StoredExpressions),
filterCompiler.CompileCondition(convertv1Validations(policy.Spec.Validations), optionalVars, environment.StoredExpressions),
matcher,
filterCompiler.Compile(convertv1AuditAnnotations(policy.Spec.AuditAnnotations), optionalVars, environment.StoredExpressions),
filterCompiler.Compile(convertv1MessageExpressions(policy.Spec.Validations), expressionOptionalVars, environment.StoredExpressions),
filterCompiler.CompileCondition(convertv1AuditAnnotations(policy.Spec.AuditAnnotations), optionalVars, environment.StoredExpressions),
filterCompiler.CompileCondition(convertv1MessageExpressions(policy.Spec.Validations), expressionOptionalVars, environment.StoredExpressions),
failurePolicy,
)

View File

@ -41,13 +41,13 @@ import (
// validator implements the Validator interface
type validator struct {
celMatcher matchconditions.Matcher
validationFilter cel.Filter
auditAnnotationFilter cel.Filter
messageFilter cel.Filter
validationFilter cel.ConditionEvaluator
auditAnnotationFilter cel.ConditionEvaluator
messageFilter cel.ConditionEvaluator
failPolicy *v1.FailurePolicyType
}
func NewValidator(validationFilter cel.Filter, celMatcher matchconditions.Matcher, auditAnnotationFilter, messageFilter cel.Filter, failPolicy *v1.FailurePolicyType) Validator {
func NewValidator(validationFilter cel.ConditionEvaluator, celMatcher matchconditions.Matcher, auditAnnotationFilter, messageFilter cel.ConditionEvaluator, failPolicy *v1.FailurePolicyType) Validator {
return &validator{
celMatcher: celMatcher,
validationFilter: validationFilter,
@ -122,6 +122,7 @@ func (v *validator) Validate(ctx context.Context, matchedResource schema.GroupVe
messageResults, _, err := v.messageFilter.ForInput(ctx, versionedAttr, admissionRequest, expressionOptionalVars, ns, remainingBudget)
for i, evalResult := range evalResults {
var decision = &decisions[i]
decision.Elapsed = evalResult.Elapsed
// TODO: move this to generics
validation, ok := evalResult.ExpressionAccessor.(*ValidationCondition)
if !ok {
@ -146,6 +147,7 @@ func (v *validator) Validate(ctx context.Context, matchedResource schema.GroupVe
decision.Message = fmt.Sprintf("failed messageExpression: %s", err)
} else if evalResult.EvalResult != celtypes.True {
decision.Action = ActionDeny
decision.Evaluation = EvalDeny
if validation.Reason == nil {
decision.Reason = metav1.StatusReasonInvalid
} else {
@ -210,6 +212,7 @@ func (v *validator) Validate(ctx context.Context, matchedResource schema.GroupVe
continue
}
var auditAnnotationResult = &auditAnnotationResults[i]
auditAnnotationResult.Elapsed = evalResult.Elapsed
// TODO: move this to generics
validation, ok := evalResult.ExpressionAccessor.(*AuditAnnotationCondition)
if !ok {

View File

@ -22,6 +22,7 @@ import (
"fmt"
"strings"
"testing"
"time"
celtypes "github.com/google/cel-go/common/types"
"github.com/stretchr/testify/require"
@ -41,7 +42,7 @@ import (
"k8s.io/apiserver/pkg/cel/environment"
)
var _ cel.Filter = &fakeCelFilter{}
var _ cel.ConditionEvaluator = &fakeCelFilter{}
type fakeCelFilter struct {
evaluations []cel.EvaluationResult
@ -105,11 +106,13 @@ func TestValidate(t *testing.T) {
{
EvalResult: celtypes.True,
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
},
},
@ -119,18 +122,22 @@ func TestValidate(t *testing.T) {
{
EvalResult: celtypes.True,
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
EvalResult: celtypes.True,
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
},
},
@ -140,11 +147,13 @@ func TestValidate(t *testing.T) {
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
},
failPolicy: &ignore,
@ -155,11 +164,13 @@ func TestValidate(t *testing.T) {
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
},
},
@ -169,11 +180,13 @@ func TestValidate(t *testing.T) {
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -184,18 +197,22 @@ func TestValidate(t *testing.T) {
{
EvalResult: celtypes.True,
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
},
failPolicy: &ignore,
@ -206,18 +223,22 @@ func TestValidate(t *testing.T) {
{
EvalResult: celtypes.True,
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
},
},
@ -227,18 +248,22 @@ func TestValidate(t *testing.T) {
{
EvalResult: celtypes.True,
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -249,18 +274,22 @@ func TestValidate(t *testing.T) {
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
},
failPolicy: &ignore,
@ -271,18 +300,22 @@ func TestValidate(t *testing.T) {
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
},
},
@ -292,18 +325,22 @@ func TestValidate(t *testing.T) {
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
{
Error: errors.New(""),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Action: ActionDeny,
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -316,6 +353,7 @@ func TestValidate(t *testing.T) {
ExpressionAccessor: &ValidationCondition{
Expression: "this.expression == unit.test",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -323,6 +361,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Reason: metav1.StatusReasonInvalid,
Message: "failed expression: this.expression == unit.test",
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -336,6 +375,7 @@ func TestValidate(t *testing.T) {
Reason: &forbiddenReason,
Expression: "this.expression == unit.test",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -343,6 +383,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Reason: metav1.StatusReasonForbidden,
Message: "failed expression: this.expression == unit.test",
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -356,6 +397,7 @@ func TestValidate(t *testing.T) {
Reason: &forbiddenReason,
Expression: "this.expression == unit.test",
},
Elapsed: time.Millisecond,
},
{
EvalResult: celtypes.False,
@ -363,6 +405,7 @@ func TestValidate(t *testing.T) {
Reason: &unauthorizedReason,
Expression: "this.expression.2 == unit.test.2",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -370,11 +413,13 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Reason: metav1.StatusReasonForbidden,
Message: "failed expression: this.expression == unit.test",
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Reason: metav1.StatusReasonUnauthorized,
Message: "failed expression: this.expression.2 == unit.test.2",
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -389,6 +434,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -396,6 +442,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Reason: metav1.StatusReasonForbidden,
Message: "test",
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -410,6 +457,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
{
EvalResult: celtypes.False,
@ -418,6 +466,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test2",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -425,11 +474,13 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Reason: metav1.StatusReasonForbidden,
Message: "test1",
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Reason: metav1.StatusReasonForbidden,
Message: "test2",
Elapsed: time.Millisecond,
},
},
failPolicy: &fail,
@ -444,6 +495,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -465,6 +517,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
{
EvalResult: celtypes.False,
@ -473,6 +526,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test2",
},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -492,13 +546,15 @@ func TestValidate(t *testing.T) {
ExpressionAccessor: &AuditAnnotationCondition{
ValueExpression: "'string value'",
},
Elapsed: 2 * time.Millisecond,
},
},
failPolicy: &fail,
auditAnnotations: []PolicyAuditAnnotation{
{
Action: AuditAnnotationActionPublish,
Value: "string value",
Action: AuditAnnotationActionPublish,
Value: "string value",
Elapsed: 2 * time.Millisecond,
},
},
},
@ -512,6 +568,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
auditEvaluations: []cel.EvaluationResult{
@ -520,17 +577,20 @@ func TestValidate(t *testing.T) {
ExpressionAccessor: &AuditAnnotationCondition{
ValueExpression: "'string value'",
},
Elapsed: 2 * time.Millisecond,
},
},
policyDecision: []PolicyDecision{
{
Action: ActionAdmit,
Action: ActionAdmit,
Elapsed: time.Millisecond,
},
},
auditAnnotations: []PolicyAuditAnnotation{
{
Action: AuditAnnotationActionPublish,
Value: "string value",
Action: AuditAnnotationActionPublish,
Value: "string value",
Elapsed: 2 * time.Millisecond,
},
},
failPolicy: &fail,
@ -543,21 +603,25 @@ func TestValidate(t *testing.T) {
ExpressionAccessor: &AuditAnnotationCondition{
ValueExpression: "null",
},
Elapsed: 2 * time.Millisecond,
},
{
EvalResult: celtypes.String("string value"),
ExpressionAccessor: &AuditAnnotationCondition{
ValueExpression: "'string value'",
},
Elapsed: 2 * time.Millisecond,
},
},
auditAnnotations: []PolicyAuditAnnotation{
{
Action: AuditAnnotationActionExclude,
Action: AuditAnnotationActionExclude,
Elapsed: 2 * time.Millisecond,
},
{
Action: AuditAnnotationActionPublish,
Value: "string value",
Action: AuditAnnotationActionPublish,
Value: "string value",
Elapsed: 2 * time.Millisecond,
},
},
failPolicy: &fail,
@ -570,12 +634,14 @@ func TestValidate(t *testing.T) {
ExpressionAccessor: &AuditAnnotationCondition{
ValueExpression: "'this is not valid CEL",
},
Elapsed: 2 * time.Millisecond,
},
},
auditAnnotations: []PolicyAuditAnnotation{
{
Action: AuditAnnotationActionError,
Error: "valueExpression ''this is not valid CEL' resulted in error: <nil>",
Action: AuditAnnotationActionError,
Error: "valueExpression ''this is not valid CEL' resulted in error: <nil>",
Elapsed: 2 * time.Millisecond,
},
},
failPolicy: &fail,
@ -588,12 +654,14 @@ func TestValidate(t *testing.T) {
ExpressionAccessor: &AuditAnnotationCondition{
ValueExpression: "'this is not valid CEL",
},
Elapsed: 2 * time.Millisecond,
},
},
auditAnnotations: []PolicyAuditAnnotation{
{
Action: AuditAnnotationActionExclude, // TODO: is this right?
Error: "valueExpression ''this is not valid CEL' resulted in error: <nil>",
Action: AuditAnnotationActionExclude, // TODO: is this right?
Error: "valueExpression ''this is not valid CEL' resulted in error: <nil>",
Elapsed: 2 * time.Millisecond,
},
},
failPolicy: &ignore,
@ -607,11 +675,13 @@ func TestValidate(t *testing.T) {
Reason: &forbiddenReason,
Expression: "this.expression == unit.test",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{
EvalResult: celtypes.String("evaluated message"),
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -619,6 +689,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "evaluated message",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -632,6 +703,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "I am not overwritten",
},
Elapsed: time.Millisecond,
},
{
EvalResult: celtypes.False,
@ -640,12 +712,14 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "I am overwritten",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{},
{
EvalResult: celtypes.String("evaluated message"),
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -653,11 +727,13 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "I am not overwritten",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
{
Action: ActionDeny,
Message: "evaluated message",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -671,11 +747,13 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "I am overwritten",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{
EvalResult: celtypes.String("evaluated message"),
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -683,6 +761,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "evaluated message",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -696,11 +775,13 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{
Error: &apiservercel.Error{Type: apiservercel.ErrorTypeInvalid},
Error: &apiservercel.Error{Type: apiservercel.ErrorTypeInvalid},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -708,6 +789,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "test1", // original message used
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -721,11 +803,13 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{
EvalResult: celtypes.String(" "),
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -733,6 +817,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "test1",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -746,11 +831,13 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{
EvalResult: celtypes.String("hello\nthere"),
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -758,6 +845,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "test1",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -771,6 +859,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
@ -783,6 +872,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "test1",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -796,11 +886,13 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
{
EvalResult: celtypes.NullValue,
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -808,6 +900,7 @@ func TestValidate(t *testing.T) {
Action: ActionDeny,
Message: "test1",
Reason: forbiddenReason,
Elapsed: time.Millisecond,
},
},
},
@ -821,6 +914,7 @@ func TestValidate(t *testing.T) {
Expression: "this.expression == unit.test",
Message: "test1",
},
Elapsed: time.Millisecond,
},
},
messageEvaluations: []cel.EvaluationResult{
@ -832,6 +926,7 @@ func TestValidate(t *testing.T) {
{
Action: ActionDeny,
Message: "running out of cost budget",
Elapsed: time.Millisecond,
},
},
costBudget: 1, // shared between expression and messageExpression, needs 1 + 1 = 2 in total
@ -843,6 +938,7 @@ func TestValidate(t *testing.T) {
{
Error: errors.New("expected"),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{},
@ -855,6 +951,7 @@ func TestValidate(t *testing.T) {
{
Error: errors.New("expected"),
ExpressionAccessor: &ValidationCondition{},
Elapsed: time.Millisecond,
},
},
policyDecision: []PolicyDecision{
@ -908,6 +1005,9 @@ func TestValidate(t *testing.T) {
if policyDecision.Reason != validateResult.Decisions[i].Reason {
t.Errorf("Expected policy decision reason '%v' but got '%v'", policyDecision.Reason, validateResult.Decisions[i].Reason)
}
if policyDecision.Elapsed != validateResult.Decisions[i].Elapsed {
t.Errorf("Expected policy decision elapsed time '%v', but got '%v'", policyDecision.Elapsed, validateResult.Decisions[i].Elapsed)
}
}
require.Equal(t, len(tc.auditEvaluations), len(validateResult.AuditAnnotations))
@ -922,6 +1022,9 @@ func TestValidate(t *testing.T) {
if auditAnnotation.Value != actual.Value {
t.Errorf("Expected policy audit annotation value '%v' but got '%v'", auditAnnotation.Value, actual.Value)
}
if auditAnnotation.Elapsed != actual.Elapsed {
t.Errorf("Expected policy audit annotation elapsed time '%v', but got '%v'", auditAnnotation.Elapsed, actual.Elapsed)
}
}
})
}
@ -932,8 +1035,8 @@ func TestContextCanceled(t *testing.T) {
fakeAttr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "default", "foo", schema.GroupVersionResource{}, "", admission.Create, nil, false, nil)
fakeVersionedAttr, _ := admission.NewVersionedAttributes(fakeAttr, schema.GroupVersionKind{}, nil)
fc := cel.NewFilterCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
f := fc.Compile([]cel.ExpressionAccessor{&ValidationCondition{Expression: "[1,2,3,4,5,6,7,8,9,10].map(x, [1,2,3,4,5,6,7,8,9,10].map(y, x*y)) == []"}}, cel.OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, environment.StoredExpressions)
fc := cel.NewConditionCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
f := fc.CompileCondition([]cel.ExpressionAccessor{&ValidationCondition{Expression: "[1,2,3,4,5,6,7,8,9,10].map(x, [1,2,3,4,5,6,7,8,9,10].map(y, x*y)) == []"}}, cel.OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, environment.StoredExpressions)
v := validator{
failPolicy: &fail,
celMatcher: &fakeCELMatcher{matches: true},

View File

@ -114,7 +114,9 @@ func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) {
// SetExternalKubeInformerFactory registers an informer factory into QuotaAdmission
func (a *QuotaAdmission) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
a.quotaAccessor.lister = f.Core().V1().ResourceQuotas().Lister()
quotas := f.Core().V1().ResourceQuotas()
a.quotaAccessor.lister = quotas.Lister()
a.quotaAccessor.hasSynced = quotas.Informer().HasSynced
}
// SetQuotaConfiguration assigns and initializes configuration and evaluator for QuotaAdmission
@ -144,6 +146,9 @@ func (a *QuotaAdmission) ValidateInitialization() error {
if a.quotaAccessor.lister == nil {
return fmt.Errorf("missing quotaAccessor.lister")
}
if a.quotaAccessor.hasSynced == nil {
return fmt.Errorf("missing quotaAccessor.hasSynced")
}
if a.quotaConfiguration == nil {
return fmt.Errorf("missing quotaConfiguration")
}
@ -155,10 +160,6 @@ func (a *QuotaAdmission) ValidateInitialization() error {
// Validate makes admission decisions while enforcing quota
func (a *QuotaAdmission) Validate(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) (err error) {
// ignore all operations that correspond to sub-resource actions
if attr.GetSubresource() != "" {
return nil
}
// ignore all operations that are not namespaced or creation of namespaces
if attr.GetNamespace() == "" || isNamespaceCreation(attr) {
return nil

View File

@ -143,10 +143,6 @@ func TestExcludedOperations(t *testing.T) {
desc string
attr admission.Attributes
}{
{
"subresource",
admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "namespace", "name", schema.GroupVersionResource{}, "subresource", admission.Create, nil, false, nil),
},
{
"non-namespaced resource",
admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "namespace", schema.GroupVersionResource{}, "", admission.Create, nil, false, nil),

View File

@ -16,4 +16,4 @@ limitations under the License.
// +k8s:deepcopy-gen=package
package resourcequota // import "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
package resourcequota

View File

@ -20,4 +20,4 @@ limitations under the License.
// +groupName=resourcequota.admission.k8s.io
// Package v1 is the v1 version of the API.
package v1 // import "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1"
package v1

View File

@ -20,4 +20,4 @@ limitations under the License.
// +groupName=resourcequota.admission.k8s.io
// Package v1alpha1 is the v1alpha1 version of the API.
package v1alpha1 // import "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1alpha1"
package v1alpha1

View File

@ -20,4 +20,4 @@ limitations under the License.
// +groupName=resourcequota.admission.k8s.io
// Package v1beta1 is the v1beta1 version of the API.
package v1beta1 // import "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1beta1"
package v1beta1

View File

@ -29,7 +29,7 @@ import (
var (
scheme = runtime.NewScheme()
codecs = serializer.NewCodecFactory(scheme)
codecs = serializer.NewCodecFactory(scheme, serializer.EnableStrict)
)
func init() {

View File

@ -43,6 +43,34 @@ func TestLoadConfiguration(t *testing.T) {
input: `{"kind":"Unknown","apiVersion":"v1"}`,
expectErr: `no kind "Unknown" is registered`,
},
{
name: "duplicate field error; strict validation",
input: `
kind: ResourceQuotaConfiguration
apiVersion: apiserver.config.k8s.io/v1
limitedResources:
- apiGroup: ""
resource: persistentvolumeclaims
resource: persistentvolumeclaims
matchContains:
- .storageclass.storage.k8s.io/requests.storage
`,
expectErr: `strict decoding error`,
},
{
name: "unknown field error; strict validation",
input: `
kind: ResourceQuotaConfiguration
apiVersion: apiserver.config.k8s.io/v1
limitedResources:
- apiGroup: ""
foo: bar
resource: persistentvolumeclaims
matchContains:
- .storageclass.storage.k8s.io/requests.storage
`,
expectErr: `strict decoding error`,
},
{
name: "valid v1alpha1 config",
input: `

View File

@ -492,16 +492,26 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat
// as a result, we need to measure the usage of this object for quota
// on updates, we need to subtract the previous measured usage
// if usage shows no change, just return since it has no impact on quota
deltaUsage, err := evaluator.Usage(inputObject)
inputUsage, err := evaluator.Usage(inputObject)
if err != nil {
return quotas, err
}
// ensure that usage for input object is never negative (this would mean a resource made a negative resource requirement)
if negativeUsage := quota.IsNegative(deltaUsage); len(negativeUsage) > 0 {
if negativeUsage := quota.IsNegative(inputUsage); len(negativeUsage) > 0 {
return nil, admission.NewForbidden(a, fmt.Errorf("quota usage is negative for resource(s): %s", prettyPrintResourceNames(negativeUsage)))
}
// initialize a map of delta usage for each interesting quota index.
deltaUsageIndexMap := make(map[int]corev1.ResourceList, len(interestingQuotaIndexes))
for _, index := range interestingQuotaIndexes {
deltaUsageIndexMap[index] = inputUsage
}
var deltaUsageWhenNoInterestingQuota corev1.ResourceList
if admission.Create == a.GetOperation() && len(interestingQuotaIndexes) == 0 {
deltaUsageWhenNoInterestingQuota = inputUsage
}
if admission.Update == a.GetOperation() {
prevItem := a.GetOldObject()
if prevItem == nil {
@ -511,20 +521,55 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat
// if we can definitively determine that this is not a case of "create on update",
// then charge based on the delta. Otherwise, bill the maximum
metadata, err := meta.Accessor(prevItem)
if err == nil && len(metadata.GetResourceVersion()) > 0 {
prevUsage, innerErr := evaluator.Usage(prevItem)
if innerErr != nil {
return quotas, innerErr
if err == nil {
if len(metadata.GetResourceVersion()) > 0 {
prevUsage, innerErr := evaluator.Usage(prevItem)
if innerErr != nil {
return quotas, innerErr
}
deltaUsage := quota.SubtractWithNonNegativeResult(inputUsage, prevUsage)
if len(interestingQuotaIndexes) == 0 {
deltaUsageWhenNoInterestingQuota = deltaUsage
}
for _, index := range interestingQuotaIndexes {
resourceQuota := quotas[index]
match, err := evaluator.Matches(&resourceQuota, prevItem)
if err != nil {
klog.ErrorS(err, "Error occurred while matching resource quota against the existing object",
"resourceQuota", resourceQuota)
return quotas, err
}
if match {
deltaUsageIndexMap[index] = deltaUsage
}
}
} else if len(interestingQuotaIndexes) == 0 {
deltaUsageWhenNoInterestingQuota = inputUsage
}
deltaUsage = quota.SubtractWithNonNegativeResult(deltaUsage, prevUsage)
}
}
// ignore items in deltaUsage with zero usage
deltaUsage = quota.RemoveZeros(deltaUsage)
// ignore items in deltaUsageIndexMap with zero usage,
// as they will not impact the quota.
for index := range deltaUsageIndexMap {
deltaUsageIndexMap[index] = quota.RemoveZeros(deltaUsageIndexMap[index])
if len(deltaUsageIndexMap[index]) == 0 {
delete(deltaUsageIndexMap, index)
}
}
// if there is no remaining non-zero usage, short-circuit and return
if len(deltaUsage) == 0 {
return quotas, nil
if len(interestingQuotaIndexes) != 0 {
if len(deltaUsageIndexMap) == 0 {
return quotas, nil
}
} else {
deltaUsage := quota.RemoveZeros(deltaUsageWhenNoInterestingQuota)
if len(deltaUsage) == 0 {
return quotas, nil
}
}
// verify that for every resource that had limited by default consumption
@ -557,22 +602,29 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat
for _, index := range interestingQuotaIndexes {
resourceQuota := outQuotas[index]
deltaUsage, ok := deltaUsageIndexMap[index]
if !ok {
continue
}
hardResources := quota.ResourceNames(resourceQuota.Status.Hard)
requestedUsage := quota.Mask(deltaUsage, hardResources)
newUsage := quota.Add(resourceQuota.Status.Used, requestedUsage)
maskedNewUsage := quota.Mask(newUsage, quota.ResourceNames(requestedUsage))
if allowed, exceeded := quota.LessThanOrEqual(maskedNewUsage, resourceQuota.Status.Hard); !allowed {
failedRequestedUsage := quota.Mask(requestedUsage, exceeded)
failedUsed := quota.Mask(resourceQuota.Status.Used, exceeded)
failedHard := quota.Mask(resourceQuota.Status.Hard, exceeded)
return nil, admission.NewForbidden(a,
fmt.Errorf("exceeded quota: %s, requested: %s, used: %s, limited: %s",
resourceQuota.Name,
prettyPrint(failedRequestedUsage),
prettyPrint(failedUsed),
prettyPrint(failedHard)))
if a.GetSubresource() != "status" {
maskedNewUsage := quota.Mask(newUsage, quota.ResourceNames(requestedUsage))
if allowed, exceeded := quota.LessThanOrEqual(maskedNewUsage, resourceQuota.Status.Hard); !allowed {
failedRequestedUsage := quota.Mask(requestedUsage, exceeded)
failedUsed := quota.Mask(resourceQuota.Status.Used, exceeded)
failedHard := quota.Mask(resourceQuota.Status.Hard, exceeded)
return nil, admission.NewForbidden(a,
fmt.Errorf("exceeded quota: %s, requested: %s, used: %s, limited: %s",
resourceQuota.Name,
prettyPrint(failedRequestedUsage),
prettyPrint(failedUsed),
prettyPrint(failedHard)))
}
}
// update to the new usage number

View File

@ -16,4 +16,4 @@ limitations under the License.
// Package resourcequota enforces all incoming requests against any applied quota
// in the namespace context of the request
package resourcequota // import "k8s.io/apiserver/pkg/admission/plugin/resourcequota"
package resourcequota

View File

@ -48,6 +48,9 @@ type quotaAccessor struct {
// lister can list/get quota objects from a shared informer's cache
lister corev1listers.ResourceQuotaLister
// hasSynced indicates whether the lister has completed its initial sync
hasSynced func() bool
// liveLookups holds the last few live lookups we've done to help ammortize cost on repeated lookup failures.
// This lets us handle the case of latent caches, by looking up actual results for a namespace on cache miss/no results.
// We track the lookup result here so that for repeated requests, we don't look it up very often.
@ -112,8 +115,8 @@ func (e *quotaAccessor) GetQuotas(namespace string) ([]corev1.ResourceQuota, err
return nil, fmt.Errorf("error resolving quota: %v", err)
}
// if there are no items held in our indexer, check our live-lookup LRU, if that misses, do the live lookup to prime it.
if len(items) == 0 {
// if there are no items held in our unsynced lister, check our live-lookup LRU, if that misses, do the live lookup to prime it.
if len(items) == 0 && !e.hasSynced() {
lruItemObj, ok := e.liveLookupCache.Get(namespace)
if !ok || lruItemObj.(liveLookupEntry).expiry.Before(time.Now()) {
// use singleflight.Group to avoid flooding the apiserver with repeated

View File

@ -97,6 +97,7 @@ func TestLRUCacheLookup(t *testing.T) {
accessor, _ := newQuotaAccessor()
accessor.client = kubeClient
accessor.lister = informerFactory.Core().V1().ResourceQuotas().Lister()
accessor.hasSynced = func() bool { return false }
accessor.liveLookupCache = liveLookupCache
for _, q := range tc.cacheInput {
@ -151,6 +152,7 @@ func TestGetQuotas(t *testing.T) {
accessor, _ := newQuotaAccessor()
accessor.client = kubeClient
accessor.lister = informerFactory.Core().V1().ResourceQuotas().Lister()
accessor.hasSynced = func() bool { return false }
kubeClient.AddReactor("list", "resourcequotas", func(action core.Action) (bool, runtime.Object, error) {
switch action.GetNamespace() {

View File

@ -50,7 +50,7 @@ type WebhookAccessor interface {
GetRESTClient(clientManager *webhookutil.ClientManager) (*rest.RESTClient, error)
// GetCompiledMatcher gets the compiled matcher object
GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher
GetCompiledMatcher(compiler cel.ConditionCompiler) matchconditions.Matcher
// GetName gets the webhook Name field. Note that the name is scoped to the webhook
// configuration and does not provide a globally unique identity, if a unique identity is
@ -132,7 +132,7 @@ func (m *mutatingWebhookAccessor) GetType() string {
return "admit"
}
func (m *mutatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher {
func (m *mutatingWebhookAccessor) GetCompiledMatcher(compiler cel.ConditionCompiler) matchconditions.Matcher {
m.compileMatcher.Do(func() {
expressions := make([]cel.ExpressionAccessor, len(m.MutatingWebhook.MatchConditions))
for i, matchCondition := range m.MutatingWebhook.MatchConditions {
@ -145,7 +145,7 @@ func (m *mutatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler
if utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks) {
strictCost = true
}
m.compiledMatcher = matchconditions.NewMatcher(compiler.Compile(
m.compiledMatcher = matchconditions.NewMatcher(compiler.CompileCondition(
expressions,
cel.OptionalVariableDeclarations{
HasParams: false,
@ -265,7 +265,7 @@ func (v *validatingWebhookAccessor) GetRESTClient(clientManager *webhookutil.Cli
return v.client, v.clientErr
}
func (v *validatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher {
func (v *validatingWebhookAccessor) GetCompiledMatcher(compiler cel.ConditionCompiler) matchconditions.Matcher {
v.compileMatcher.Do(func() {
expressions := make([]cel.ExpressionAccessor, len(v.ValidatingWebhook.MatchConditions))
for i, matchCondition := range v.ValidatingWebhook.MatchConditions {
@ -278,7 +278,7 @@ func (v *validatingWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompil
if utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks) {
strictCost = true
}
v.compiledMatcher = matchconditions.NewMatcher(compiler.Compile(
v.compiledMatcher = matchconditions.NewMatcher(compiler.CompileCondition(
expressions,
cel.OptionalVariableDeclarations{
HasParams: false,

View File

@ -22,16 +22,16 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
fuzz "github.com/google/gofuzz"
v1 "k8s.io/api/admissionregistration/v1"
"sigs.k8s.io/randfill"
)
func TestMutatingWebhookAccessor(t *testing.T) {
f := fuzz.New()
f := randfill.New()
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("Run %d/100", i), func(t *testing.T) {
orig := &v1.MutatingWebhook{}
f.Fuzz(orig)
f.Fill(orig)
// zero out any accessor type specific fields not included in the accessor
orig.ReinvocationPolicy = nil
@ -72,11 +72,11 @@ func TestMutatingWebhookAccessor(t *testing.T) {
}
func TestValidatingWebhookAccessor(t *testing.T) {
f := fuzz.New()
f := randfill.New()
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("Run %d/100", i), func(t *testing.T) {
orig := &v1.ValidatingWebhook{}
f.Fuzz(orig)
f.Fill(orig)
uid := fmt.Sprintf("test.configuration.admission/%s/0", orig.Name)
accessor := NewValidatingWebhookAccessor(uid, "test.configuration.admission", orig)
if uid != accessor.GetUID() {

View File

@ -16,4 +16,4 @@ limitations under the License.
// +k8s:deepcopy-gen=package
package webhookadmission // import "k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission"
package webhookadmission

View File

@ -20,4 +20,4 @@ limitations under the License.
// +groupName=apiserver.config.k8s.io
// Package v1 is the v1 version of the API.
package v1 // import "k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1"
package v1

View File

@ -20,4 +20,4 @@ limitations under the License.
// +groupName=apiserver.config.k8s.io
// Package v1alpha1 is the v1alpha1 version of the API.
package v1alpha1 // import "k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1"
package v1alpha1

View File

@ -15,4 +15,4 @@ limitations under the License.
*/
// Package errors contains utilities for admission webhook specific errors
package errors // import "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
package errors

View File

@ -21,6 +21,9 @@ import (
"fmt"
"io"
"k8s.io/apiserver/pkg/cel/environment"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
admissionv1 "k8s.io/api/admission/v1"
@ -38,9 +41,6 @@ import (
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/rules"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/cel/environment"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
@ -57,7 +57,7 @@ type Webhook struct {
namespaceMatcher *namespace.Matcher
objectMatcher *object.Matcher
dispatcher Dispatcher
filterCompiler cel.FilterCompiler
filterCompiler cel.ConditionCompiler
authorizer authorizer.Authorizer
}
@ -102,7 +102,7 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
namespaceMatcher: &namespace.Matcher{},
objectMatcher: &object.Matcher{},
dispatcher: dispatcherFactory(&cm),
filterCompiler: cel.NewFilterCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks))),
filterCompiler: cel.NewConditionCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks))),
}, nil
}

View File

@ -76,7 +76,7 @@ type fakeWebhookAccessor struct {
matchResult bool
}
func (f *fakeWebhookAccessor) GetCompiledMatcher(compiler cel.FilterCompiler) matchconditions.Matcher {
func (f *fakeWebhookAccessor) GetCompiledMatcher(compiler cel.ConditionCompiler) matchconditions.Matcher {
return &fakeMatcher{
throwError: f.throwError,
matchResult: f.matchResult,

View File

@ -54,14 +54,14 @@ var _ Matcher = &matcher{}
// matcher evaluates compiled cel expressions and determines if they match the given request or not
type matcher struct {
filter celplugin.Filter
filter celplugin.ConditionEvaluator
failPolicy v1.FailurePolicyType
matcherType string
matcherKind string
objectName string
}
func NewMatcher(filter celplugin.Filter, failPolicy *v1.FailurePolicyType, matcherKind, matcherType, objectName string) Matcher {
func NewMatcher(filter celplugin.ConditionEvaluator, failPolicy *v1.FailurePolicyType, matcherKind, matcherType, objectName string) Matcher {
var f v1.FailurePolicyType
if failPolicy == nil {
f = v1.Fail

View File

@ -35,7 +35,7 @@ import (
"k8s.io/apiserver/pkg/admission/plugin/cel"
)
var _ cel.Filter = &fakeCelFilter{}
var _ cel.ConditionEvaluator = &fakeCelFilter{}
type fakeCelFilter struct {
evaluations []cel.EvaluationResult

View File

@ -190,7 +190,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
admissionmetrics.Metrics.ObserveWebhook(ctx, hook.Name, time.Since(t), rejected, versionedAttr.Attributes, "admit", 200)
}
if changed {
// Patch had changed the object. Prepare to reinvoke all previous webhooks that are eligible for re-invocation.
// Patch had changed the object. Prepare to reinvoke all previous mutations that are eligible for re-invocation.
webhookReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
reinvokeCtx.SetShouldReinvoke()
}
@ -261,7 +261,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
// Make the webhook request
client, err := invocation.Webhook.GetRESTClient(a.cm)
if err != nil {
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("could not get REST client: %w", err), Status: apierrors.NewBadRequest("error getting REST client")}
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("could not get REST client: %w", err), Status: apierrors.NewInternalError(err)}
}
ctx, span := tracing.Start(ctx, "Call mutating webhook",
attribute.String("configuration", configurationName),
@ -305,7 +305,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
if se, ok := err.(*apierrors.StatusError); ok {
status = se
} else {
status = apierrors.NewBadRequest("error calling webhook")
status = apierrors.NewServiceUnavailable("error calling webhook")
}
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("failed to call webhook: %w", err), Status: status}
}
@ -335,7 +335,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
}
patchObj, err := jsonpatch.DecodePatch(result.Patch)
if err != nil {
return false, apierrors.NewInternalError(err)
return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("received undecodable patch in webhook response: %w", err), Status: apierrors.NewServiceUnavailable("error decoding patch in webhook response")}
}
if len(patchObj) == 0 {
@ -348,7 +348,7 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *admiss
}
var patchedJS []byte
jsonSerializer := json.NewSerializer(json.DefaultMetaFactory, o.GetObjectCreater(), o.GetObjectTyper(), false)
jsonSerializer := json.NewSerializerWithOptions(json.DefaultMetaFactory, o.GetObjectCreater(), o.GetObjectTyper(), json.SerializerOptions{})
switch result.PatchType {
// VerifyAdmissionResponse normalizes to v1 patch types, regardless of the AdmissionReview version used
case admissionv1.PatchTypeJSONPatch:

View File

@ -16,4 +16,4 @@ limitations under the License.
// Package mutating makes calls to mutating webhooks during the admission
// process.
package mutating // import "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
package mutating

View File

@ -26,14 +26,16 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apiserver/pkg/endpoints/request"
clocktesting "k8s.io/utils/clock/testing"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
webhooktesting "k8s.io/apiserver/pkg/admission/plugin/webhook/testing"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/component-base/metrics/testutil"
clocktesting "k8s.io/utils/clock/testing"
)
// BenchmarkAdmit tests the performance cost of invoking a mutating webhook
@ -156,6 +158,9 @@ func TestAdmit(t *testing.T) {
attr = webhooktesting.NewAttribute(ns, tt.AdditionalLabels, tt.IsDryRun)
}
if len(tt.ExpectRejectionMetrics) > 0 {
admissionmetrics.Metrics.WebhookRejectionGathererForTest().Reset()
}
err = wh.Admit(context.TODO(), attr, objectInterfaces)
if tt.ExpectAllow != (err == nil) {
t.Errorf("expected allowed=%v, but got err=%v", tt.ExpectAllow, err)
@ -178,6 +183,15 @@ func TestAdmit(t *testing.T) {
t.Errorf("expected status code %d, got %d", tt.ExpectStatusCode, statusErr.ErrStatus.Code)
}
}
if len(tt.ExpectRejectionMetrics) > 0 {
expectedMetrics := `
# HELP apiserver_admission_webhook_rejection_count [ALPHA] Admission webhook rejection count, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify an error type (calling_webhook_error or apiserver_internal_error if an error occurred; no_error otherwise) and optionally a non-zero rejection code if the webhook rejects the request with an HTTP status code (honored by the apiserver when the code is greater or equal to 400). Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.
# TYPE apiserver_admission_webhook_rejection_count counter
` + tt.ExpectRejectionMetrics + "\n"
if err := testutil.CollectAndCompare(admissionmetrics.Metrics.WebhookRejectionGathererForTest(), strings.NewReader(expectedMetrics), "apiserver_admission_webhook_rejection_count"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
fakeAttr, ok := attr.(*webhooktesting.FakeAttributes)
if !ok {
t.Errorf("Unexpected error, failed to convert attr to webhooktesting.FakeAttributes")

View File

@ -17,4 +17,4 @@ limitations under the License.
// Package namespace defines the utilities that are used by the webhook
// plugin to decide if a webhook should be applied to an object based on its
// namespace.
package namespace // import "k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
package namespace

View File

@ -95,8 +95,8 @@ func (m *Matcher) GetNamespaceLabels(attr admission.Attributes) (map[string]stri
return namespace.Labels, nil
}
// MatchNamespaceSelector decideds whether the request matches the
// namespaceSelctor of the webhook. Only when they match, the webhook is called.
// MatchNamespaceSelector decides whether the request matches the
// namespaceSelector of the webhook. Only when they match, the webhook is called.
func (m *Matcher) MatchNamespaceSelector(p NamespaceSelectorProvider, attr admission.Attributes) (bool, *apierrors.StatusError) {
namespaceName := attr.GetNamespace()
if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" {

View File

@ -17,4 +17,4 @@ limitations under the License.
// Package object defines the utilities that are used by the webhook plugin to
// decide if a webhook should run, as long as either the old object or the new
// object has labels matching the webhook config's objectSelector.
package object // import "k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
package object

View File

@ -47,7 +47,7 @@ func matchObject(obj runtime.Object, selector labels.Selector) bool {
}
// MatchObjectSelector decideds whether the request matches the ObjectSelector
// MatchObjectSelector decides whether the request matches the ObjectSelector
// of the webhook. Only when they match, the webhook is called.
func (m *Matcher) MatchObjectSelector(p ObjectSelectorProvider, attr admission.Attributes) (bool, *apierrors.StatusError) {
selector, err := p.GetParsedObjectSelector()

View File

@ -121,7 +121,7 @@ func (r *Matcher) resource() bool {
func IsExemptAdmissionConfigurationResource(attr admission.Attributes) bool {
gvk := attr.GetKind()
if gvk.Group == "admissionregistration.k8s.io" {
if gvk.Kind == "ValidatingWebhookConfiguration" || gvk.Kind == "MutatingWebhookConfiguration" || gvk.Kind == "ValidatingAdmissionPolicy" || gvk.Kind == "ValidatingAdmissionPolicyBinding" {
if gvk.Kind == "ValidatingWebhookConfiguration" || gvk.Kind == "MutatingWebhookConfiguration" || gvk.Kind == "ValidatingAdmissionPolicy" || gvk.Kind == "ValidatingAdmissionPolicyBinding" || gvk.Kind == "MutatingAdmissionPolicy" || gvk.Kind == "MutatingAdmissionPolicyBinding" {
return true
}
}

View File

@ -36,7 +36,7 @@ import (
"k8s.io/apiserver/pkg/admission/plugin/webhook"
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
"k8s.io/apiserver/pkg/authentication/user"
utilpointer "k8s.io/utils/pointer"
"k8s.io/utils/ptr"
)
func TestVerifyAdmissionResponse(t *testing.T) {
@ -535,7 +535,7 @@ func TestCreateAdmissionObjects(t *testing.T) {
},
Object: runtime.RawExtension{Object: versionedObj},
OldObject: runtime.RawExtension{Object: versionedObjOld},
DryRun: utilpointer.BoolPtr(false),
DryRun: ptr.To(false),
Options: runtime.RawExtension{Object: &metav1.UpdateOptions{FieldManager: "foo"}},
},
}
@ -578,7 +578,7 @@ func TestCreateAdmissionObjects(t *testing.T) {
},
Object: runtime.RawExtension{Object: versionedObj},
OldObject: runtime.RawExtension{Object: versionedObjOld},
DryRun: utilpointer.BoolPtr(false),
DryRun: ptr.To(false),
Options: runtime.RawExtension{Object: &metav1.UpdateOptions{FieldManager: "foo"}},
},
}

View File

@ -15,4 +15,4 @@ limitations under the License.
*/
// Package request creates admissionReview request based on admission attributes.
package request // import "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
package request

View File

@ -16,4 +16,4 @@ limitations under the License.
// Package testcerts contains generated key pairs used by the unit tests of
// mutating and validating webhooks. They are for testing only.
package testcerts // import "k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts"
package testcerts

View File

@ -38,6 +38,7 @@ import (
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
fakeclientset "k8s.io/client-go/kubernetes/fake"
"k8s.io/utils/ptr"
)
var matchEverythingRules = []registrationv1.RuleWithOperations{{
@ -224,6 +225,7 @@ type ValidatingTest struct {
ErrorContains string
ExpectAnnotations map[string]string
ExpectStatusCode int32
ExpectRejectionMetrics string
ExpectReinvokeWebhooks map[string]bool
}
@ -241,6 +243,7 @@ type MutatingTest struct {
ErrorContains string
ExpectAnnotations map[string]string
ExpectStatusCode int32
ExpectRejectionMetrics string
ExpectReinvokeWebhooks map[string]bool
}
@ -288,7 +291,8 @@ func ConvertToMutatingTestCases(tests []ValidatingTest, configurationName string
t.ExpectAnnotations[newKey] = value
delete(t.ExpectAnnotations, key)
}
r[i] = MutatingTest{t.Name, ConvertToMutatingWebhooks(t.Webhooks), t.Path, t.IsCRD, t.IsDryRun, t.AdditionalLabels, t.SkipBenchmark, t.ExpectLabels, t.ExpectAllow, t.ErrorContains, t.ExpectAnnotations, t.ExpectStatusCode, t.ExpectReinvokeWebhooks}
expectedMetrics := strings.ReplaceAll(t.ExpectRejectionMetrics, `type="validating"`, `type="admit"`)
r[i] = MutatingTest{t.Name, ConvertToMutatingWebhooks(t.Webhooks), t.Path, t.IsCRD, t.IsDryRun, t.AdditionalLabels, t.SkipBenchmark, t.ExpectLabels, t.ExpectAllow, t.ErrorContains, t.ExpectAnnotations, t.ExpectStatusCode, expectedMetrics, t.ExpectReinvokeWebhooks}
}
return r
}
@ -506,6 +510,34 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest {
ExpectStatusCode: http.StatusInternalServerError,
ExpectAllow: false,
},
{
Name: "match & invalid client config",
Webhooks: []registrationv1.ValidatingWebhook{{
Name: "invalidClientConfig",
ClientConfig: registrationv1.WebhookClientConfig{},
Rules: matchEverythingRules,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
AdmissionReviewVersions: []string{"v1beta1"},
}},
ExpectStatusCode: http.StatusInternalServerError,
ExpectRejectionMetrics: `apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalidClientConfig",operation="UPDATE",rejection_code="500",type="validating"} 1`,
ErrorContains: "could not get REST client",
},
{
Name: "match & non-status error",
Webhooks: []registrationv1.ValidatingWebhook{{
Name: "nonStatusError",
ClientConfig: ccfgSVC("nonStatusError"),
Rules: matchEverythingRules,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
AdmissionReviewVersions: []string{"v1beta1"},
}},
ExpectStatusCode: http.StatusInternalServerError,
ExpectRejectionMetrics: `apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="nonStatusError",operation="UPDATE",rejection_code="503",type="validating"} 1`,
ErrorContains: "failed to call webhook",
},
{
Name: "match & allow (url)",
Webhooks: []registrationv1.ValidatingWebhook{{
@ -897,6 +929,74 @@ func NewMutatingTestCases(url *url.URL, configurationName string) []MutatingTest
"mutation.webhook.admission.k8s.io/round_0_index_0": mutationAnnotationValue(configurationName, "invalidMutation", false),
},
},
{
Name: "match & invalid patch",
Webhooks: []registrationv1.MutatingWebhook{{
Name: "invalidPatch",
ClientConfig: ccfgSVC("invalidPatch"),
Rules: matchEverythingRules,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
AdmissionReviewVersions: []string{"v1beta1"},
}},
ExpectStatusCode: http.StatusInternalServerError,
ErrorContains: "unexpected end of JSON input",
ExpectAnnotations: map[string]string{
"mutation.webhook.admission.k8s.io/round_0_index_0": mutationAnnotationValue(configurationName, "invalidPatch", false),
},
},
{
Name: "match & invalid patch fail open",
Webhooks: []registrationv1.MutatingWebhook{{
Name: "invalidPatch",
ClientConfig: ccfgSVC("invalidPatch"),
Rules: matchEverythingRules,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
AdmissionReviewVersions: []string{"v1beta1"},
FailurePolicy: ptr.To(registrationv1.Ignore),
}},
ExpectAllow: true,
ExpectStatusCode: http.StatusOK,
ExpectAnnotations: map[string]string{
"failed-open.mutation.webhook.admission.k8s.io/round_0_index_0": "invalidPatch",
"mutation.webhook.admission.k8s.io/round_0_index_0": mutationAnnotationValue(configurationName, "invalidPatch", false),
},
},
{
Name: "match & invalid client config",
Webhooks: []registrationv1.MutatingWebhook{{
Name: "invalidClientConfig",
ClientConfig: registrationv1.WebhookClientConfig{},
Rules: matchEverythingRules,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
AdmissionReviewVersions: []string{"v1beta1"},
}},
ExpectStatusCode: http.StatusInternalServerError,
ExpectRejectionMetrics: `apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalidClientConfig",operation="UPDATE",rejection_code="500",type="admit"} 1`,
ErrorContains: "could not get REST client",
ExpectAnnotations: map[string]string{
"mutation.webhook.admission.k8s.io/round_0_index_0": mutationAnnotationValue(configurationName, "invalidClientConfig", false),
},
},
{
Name: "match & non-status error",
Webhooks: []registrationv1.MutatingWebhook{{
Name: "nonStatusError",
ClientConfig: ccfgSVC("nonStatusError"),
Rules: matchEverythingRules,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
AdmissionReviewVersions: []string{"v1beta1"},
}},
ExpectStatusCode: http.StatusInternalServerError,
ExpectRejectionMetrics: `apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="nonStatusError",operation="UPDATE",rejection_code="503",type="admit"} 1`,
ErrorContains: "failed to call webhook",
ExpectAnnotations: map[string]string{
"mutation.webhook.admission.k8s.io/round_0_index_0": mutationAnnotationValue(configurationName, "nonStatusError", false),
},
},
{
Name: "match & remove label dry run unsupported",
Webhooks: []registrationv1.MutatingWebhook{{

View File

@ -137,6 +137,16 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) {
Patch: []byte(`[{"op": "add", "path": "/metadata/labels/added", "value": "test"}]`),
},
})
case "/invalidPatch":
w.Header().Set("Content-Type", "application/json")
pt := v1beta1.PatchTypeJSONPatch
json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{
Response: &v1beta1.AdmissionResponse{
Allowed: true,
PatchType: &pt,
Patch: []byte(`[{`),
},
})
case "/invalidMutation":
w.Header().Set("Content-Type", "application/json")
pt := v1beta1.PatchTypeJSONPatch
@ -147,6 +157,11 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) {
Patch: []byte(`[{"op": "add", "CORRUPTED_KEY":}]`),
},
})
case "/nonStatusError":
hj, _ := w.(http.Hijacker)
conn, _, _ := hj.Hijack()
defer conn.Close() //nolint:errcheck
conn.Write([]byte("bad-http")) //nolint:errcheck
case "/nilResponse":
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{})

View File

@ -262,7 +262,7 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1.ValidatingWeb
// Make the webhook request
client, err := invocation.Webhook.GetRESTClient(d.cm)
if err != nil {
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("could not get REST client: %w", err), Status: apierrors.NewBadRequest("error getting REST client")}
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("could not get REST client: %w", err), Status: apierrors.NewInternalError(err)}
}
ctx, span := tracing.Start(ctx, "Call validating webhook",
attribute.String("configuration", invocation.Webhook.GetConfigurationName()),
@ -306,7 +306,7 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1.ValidatingWeb
if se, ok := err.(*apierrors.StatusError); ok {
status = se
} else {
status = apierrors.NewBadRequest("error calling webhook")
status = apierrors.NewServiceUnavailable("error calling webhook")
}
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("failed to call webhook: %w", err), Status: status}
}

View File

@ -16,4 +16,4 @@ limitations under the License.
// Package validating makes calls to validating (i.e., non-mutating) webhooks
// during the admission process.
package validating // import "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
package validating

View File

@ -24,12 +24,14 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/apiserver/pkg/endpoints/request"
clocktesting "k8s.io/utils/clock/testing"
"k8s.io/apimachinery/pkg/api/errors"
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
webhooktesting "k8s.io/apiserver/pkg/admission/plugin/webhook/testing"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/component-base/metrics/testutil"
clocktesting "k8s.io/utils/clock/testing"
)
// BenchmarkValidate tests that ValidatingWebhook#Validate works as expected
@ -135,6 +137,10 @@ func TestValidate(t *testing.T) {
continue
}
if len(tt.ExpectRejectionMetrics) > 0 {
admissionmetrics.Metrics.WebhookRejectionGathererForTest().Reset()
}
attr := webhooktesting.NewAttribute(ns, nil, tt.IsDryRun)
err = wh.Validate(context.TODO(), attr, objectInterfaces)
if tt.ExpectAllow != (err == nil) {
@ -149,6 +155,15 @@ func TestValidate(t *testing.T) {
if _, isStatusErr := err.(*errors.StatusError); err != nil && !isStatusErr {
t.Errorf("%s: expected a StatusError, got %T", tt.Name, err)
}
if len(tt.ExpectRejectionMetrics) > 0 {
expectedMetrics := `
# HELP apiserver_admission_webhook_rejection_count [ALPHA] Admission webhook rejection count, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify an error type (calling_webhook_error or apiserver_internal_error if an error occurred; no_error otherwise) and optionally a non-zero rejection code if the webhook rejects the request with an HTTP status code (honored by the apiserver when the code is greater or equal to 400). Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.
# TYPE apiserver_admission_webhook_rejection_count counter
` + tt.ExpectRejectionMetrics + "\n"
if err := testutil.CollectAndCompare(admissionmetrics.Metrics.WebhookRejectionGathererForTest(), strings.NewReader(expectedMetrics), "apiserver_admission_webhook_rejection_count"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
fakeAttr, ok := attr.(*webhooktesting.FakeAttributes)
if !ok {
t.Errorf("Unexpected error, failed to convert attr to webhooktesting.FakeAttributes")

View File

@ -16,4 +16,4 @@ limitations under the License.
// +groupName=apidiscovery.k8s.io
package v2 // import "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
package v2

View File

@ -29,8 +29,8 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
"github.com/google/go-cmp/cmp"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
"sigs.k8s.io/randfill"
)
func TestConversionRoundTrip(t *testing.T) {
@ -42,12 +42,12 @@ func TestConversionRoundTrip(t *testing.T) {
err = v2scheme.RegisterConversions(scheme)
require.NoError(t, err)
fuzzer := fuzz.NewWithSeed(2374375)
fuzzer := randfill.NewWithSeed(2374375)
// v2 -> v2beta1 -> v2
for i := 0; i < 100; i++ {
expected := &v2.APIGroupDiscoveryList{}
fuzzer.Fuzz(expected)
fuzzer.Fill(expected)
expected.TypeMeta = metav1.TypeMeta{
Kind: "APIGroupDiscoveryList",
APIVersion: "apidiscovery.k8s.io/v2",
@ -68,7 +68,7 @@ func TestConversionRoundTrip(t *testing.T) {
// v2beta1 -> v2 -> v2beta1
for i := 0; i < 100; i++ {
expected := &v2beta1.APIGroupDiscoveryList{}
fuzzer.Fuzz(expected)
fuzzer.Fill(expected)
expected.TypeMeta = metav1.TypeMeta{
Kind: "APIGroupDiscoveryList",
APIVersion: "apidiscovery.k8s.io/v2beta1",

View File

@ -16,4 +16,4 @@ limitations under the License.
// +groupName=apidiscovery.k8s.io
package v2beta1 // import "k8s.io/apiserver/pkg/apis/apidiscovery/v2beta1"
package v2beta1

View File

@ -18,4 +18,4 @@ limitations under the License.
// +groupName=apiserver.k8s.io
// Package apiserver is the internal version of the API.
package apiserver // import "k8s.io/apiserver/pkg/apis/apiserver"
package apiserver

View File

@ -213,8 +213,10 @@ func TestLoadFromData(t *testing.T) {
Type: "Webhook",
Name: "default",
Webhook: &api.WebhookConfiguration{
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
CacheAuthorizedRequests: true,
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
CacheUnauthorizedRequests: true,
},
}},
},
@ -252,8 +254,10 @@ authorizers:
Type: "Webhook",
Name: "default",
Webhook: &api.WebhookConfiguration{
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
CacheAuthorizedRequests: true,
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
CacheUnauthorizedRequests: true,
},
}},
},
@ -265,6 +269,47 @@ apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
- type: Webhook
`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
},
},
{
name: "v1 - json",
data: []byte(`{
"apiVersion":"apiserver.config.k8s.io/v1",
"kind":"AuthorizationConfiguration",
"authorizers":[{"type":"Webhook"}]}`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
},
},
{
name: "v1 - defaults",
data: []byte(`{
"apiVersion":"apiserver.config.k8s.io/v1",
"kind":"AuthorizationConfiguration",
"authorizers":[{"type":"Webhook","name":"default","webhook":{}}]}`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{
Type: "Webhook",
Name: "default",
Webhook: &api.WebhookConfiguration{
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
CacheAuthorizedRequests: true,
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
CacheUnauthorizedRequests: true,
},
}},
},
},
{
name: "v1 - yaml",
data: []byte(`
apiVersion: apiserver.config.k8s.io/v1
kind: AuthorizationConfiguration
authorizers:
- type: Webhook
`),
expectConfig: &api.AuthorizationConfiguration{
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},

View File

@ -334,11 +334,21 @@ type WebhookConfiguration struct {
// Same as setting `--authorization-webhook-cache-authorized-ttl` flag
// Default: 5m0s
AuthorizedTTL metav1.Duration
// CacheAuthorizedRequests specifies whether authorized requests should be cached.
// If set to true, the TTL for cached decisions can be configured via the
// AuthorizedTTL field.
// Default: true
CacheAuthorizedRequests bool
// The duration to cache 'unauthorized' responses from the webhook
// authorizer.
// Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
// Default: 30s
UnauthorizedTTL metav1.Duration
// CacheUnauthorizedRequests specifies whether unauthorized requests should be cached.
// If set to true, the TTL for cached decisions can be configured via the
// UnauthorizedTTL field.
// Default: true
CacheUnauthorizedRequests bool
// Timeout for the webhook request
// Maximum allowed value is 30s.
// Required, no default value.
@ -401,6 +411,13 @@ type WebhookMatchCondition struct {
// If version specified by subjectAccessReviewVersion in the request variable is v1beta1,
// the contents would be converted to the v1 version before evaluating the CEL expression.
//
// - 'resourceAttributes' describes information for a resource access request and is unset for non-resource requests. e.g. has(request.resourceAttributes) && request.resourceAttributes.namespace == 'default'
// - 'nonResourceAttributes' describes information for a non-resource access request and is unset for resource requests. e.g. has(request.nonResourceAttributes) && request.nonResourceAttributes.path == '/healthz'.
// - 'user' is the user to test for. e.g. request.user == 'alice'
// - 'groups' is the groups to test for. e.g. ('group1' in request.groups)
// - 'extra' corresponds to the user.Info.GetExtra() method from the authenticator.
// - 'uid' is the information about the requesting user. e.g. request.uid == '1'
//
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
Expression string
}

View File

@ -21,6 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"
)
var (
@ -48,3 +49,18 @@ func SetDefaults_KMSConfiguration(obj *KMSConfiguration) {
obj.CacheSize = &defaultCacheSize
}
}
func SetDefaults_WebhookConfiguration(obj *WebhookConfiguration) {
if obj.AuthorizedTTL.Duration == 0 {
obj.AuthorizedTTL.Duration = 5 * time.Minute
}
if obj.CacheAuthorizedRequests == nil {
obj.CacheAuthorizedRequests = ptr.To(true)
}
if obj.UnauthorizedTTL.Duration == 0 {
obj.UnauthorizedTTL.Duration = 30 * time.Second
}
if obj.CacheUnauthorizedRequests == nil {
obj.CacheUnauthorizedRequests = ptr.To(true)
}
}

View File

@ -20,4 +20,4 @@ limitations under the License.
// +groupName=apiserver.config.k8s.io
// Package v1 is the v1 version of the API.
package v1 // import "k8s.io/apiserver/pkg/apis/apiserver/v1"
package v1

Some files were not shown because too many files have changed in this diff Show More