Compare commits
492 Commits
kubernetes
...
master
Author | SHA1 | Date |
---|---|---|
|
6016cdfb88 | |
|
6dc2a56d3a | |
|
dda2fcb797 | |
|
34f5472b35 | |
|
532260d25f | |
|
0a000a99f6 | |
|
e824ccb0fa | |
|
2f077fcbca | |
|
d7827fc7e8 | |
|
32cbb819fe | |
|
bf8c1a6d90 | |
|
c42cceba12 | |
|
d972fefbb0 | |
|
8cd1907cc9 | |
|
5e1c1db1da | |
|
03b538a8e8 | |
|
d0ee71ec17 | |
|
9b1ef94042 | |
|
f482a77223 | |
|
7364e005dc | |
|
d2a696650a | |
|
ab47d6f39c | |
|
c93fe8d40f | |
|
2898f40277 | |
|
3637a3a5aa | |
|
179348d538 | |
|
6bc5ac2a1a | |
|
b6975dfbe9 | |
|
61137484d0 | |
|
e26c3d7e23 | |
|
7710d9ff3e | |
|
08b1bbe4b9 | |
|
ff84ca3520 | |
|
2c9f20fd49 | |
|
3565462805 | |
|
09be259d76 | |
|
ee98ed2094 | |
|
6874062f41 | |
|
06b7bf2af7 | |
|
af5056a3db | |
|
8c685320dc | |
|
895f2b3947 | |
|
2ca55b4189 | |
|
67c4a8a9ee | |
|
df850b5a51 | |
|
b805c4d851 | |
|
19608fe81a | |
|
cf27dab482 | |
|
eaf6e48501 | |
|
07ec5c5838 | |
|
662ba26adb | |
|
a77ab41bc0 | |
|
1a56a2f375 | |
|
8ee91f3b4d | |
|
942549e2f9 | |
|
0d3a31d996 | |
|
3f2a0b72ba | |
|
75e69719a1 | |
|
389ff851fc | |
|
bab2fbdc6f | |
|
3d4aed0ded | |
|
0e36545f56 | |
|
a47cc30656 | |
|
0dd61ee79d | |
|
baef40d3ec | |
|
d16c916aad | |
|
fdccb8b2dc | |
|
7a9026cb3a | |
|
dd8cd79556 | |
|
d76c79a4e9 | |
|
3b43a1a7f8 | |
|
8162f5ea9d | |
|
deb30e35ad | |
|
9b695a5efa | |
|
6db9ead72a | |
|
1f47d05362 | |
|
081f25136a | |
|
89f1e0c9f7 | |
|
0e38ecf83f | |
|
a2f3ed4733 | |
|
e4c9db3825 | |
|
6bf08e15b6 | |
|
d9e31a74fe | |
|
65bd2fadfd | |
|
f294209f1e | |
|
dc0ee6eda8 | |
|
8aa4209508 | |
|
80cef81846 | |
|
c5cc58d0de | |
|
f8343759d9 | |
|
6b794dbd82 | |
|
8a7311dce3 | |
|
d9b6bd1d4c | |
|
ed9f8fce83 | |
|
f55aaff838 | |
|
eadbc19007 | |
|
13e6a3c329 | |
|
9388b76b21 | |
|
7cd49caffc | |
|
92c5638afd | |
|
2c9afbf9bc | |
|
053a8b2a4c | |
|
7d6aa8a395 | |
|
02450b6f98 | |
|
851e917827 | |
|
99e4fe9de1 | |
|
b50caf5d66 | |
|
73e127faf5 | |
|
255fa69bd1 | |
|
784d2892e6 | |
|
4b98f13de7 | |
|
b887c9ebec | |
|
7937de32ff | |
|
08959fe41f | |
|
2055ba24cd | |
|
df39bcd7dd | |
|
73a9ddd3c3 | |
|
d6a8a1af27 | |
|
2f8595c47b | |
|
5009571787 | |
|
a559cb8be1 | |
|
569dd0421b | |
|
062128c843 | |
|
2ce736f1ce | |
|
12fab5de10 | |
|
6764e31cac | |
|
9333a5f386 | |
|
80961690c3 | |
|
067a2139bb | |
|
057e0d26a0 | |
|
e80da78881 | |
|
9e1714f200 | |
|
bc489d7b1b | |
|
b45c4547ed | |
|
bda7e28bbb | |
|
9b509bf53b | |
|
2ffdf9039f | |
|
f2b80d742a | |
|
1ffdd2403f | |
|
2bc51918fe | |
|
46dd96ca03 | |
|
1527793495 | |
|
93c8ec73c8 | |
|
5f14a1e225 | |
|
007464aa0f | |
|
97250949cd | |
|
724cb010da | |
|
11e35f7e67 | |
|
71463feb30 | |
|
62514be921 | |
|
7e75755a5a | |
|
b6cfd80a0d | |
|
32e916b79d | |
|
1552f6cea3 | |
|
61451f57b1 | |
|
0de899d1ed | |
|
fd89bf895d | |
|
2bff744497 | |
|
8cf49c7c86 | |
|
8285a27f7b | |
|
0f91510ab0 | |
|
23913f0bf4 | |
|
eeced267ad | |
|
d8f5cf79ad | |
|
3dae57efb5 | |
|
73b2a2235b | |
|
f2b320dc52 | |
|
c199f9d392 | |
|
0d11839195 | |
|
b5e431bd82 | |
|
d22318d3d2 | |
|
8776678b52 | |
|
cb5a7a865d | |
|
a18370ff46 | |
|
de95d3511f | |
|
847f0cb7d3 | |
|
dbbb6a075e | |
|
f52dfa0eb4 | |
|
a50d13fe86 | |
|
201e7749f2 | |
|
3a062dc7cb | |
|
4a106441d6 | |
|
fc69168d19 | |
|
f541759309 | |
|
85b0cb4ae1 | |
|
42db0bf60a | |
|
fe06bdc873 | |
|
cab2303b49 | |
|
c160237b46 | |
|
492c873a80 | |
|
6da3566330 | |
|
213eed6ea1 | |
|
82f6fe39b0 | |
|
8181c85b8b | |
|
056896e970 | |
|
f5da7d2a32 | |
|
4c858d18eb | |
|
03ddc411f7 | |
|
2a113dbe6b | |
|
e87c9dbf8f | |
|
cd728bc91b | |
|
5a4ec0a797 | |
|
d4f2fc56b5 | |
|
beaef1d3ec | |
|
1776f0c3f2 | |
|
e228aeaf39 | |
|
29a5d82129 | |
|
30b60eb0a6 | |
|
aed144f141 | |
|
51e9232644 | |
|
4f42467c04 | |
|
b9e86eb851 | |
|
8ff7962ff8 | |
|
0710a6fd0a | |
|
81c5d13c4d | |
|
26bd744afc | |
|
37736ec2c1 | |
|
b1b5987c22 | |
|
6811fdeb2d | |
|
b1020cee14 | |
|
3a206e1457 | |
|
a11f874f2c | |
|
b94acad08e | |
|
8702b38eb1 | |
|
7d24a9eb1b | |
|
23be1a4749 | |
|
dfc7cb5fe1 | |
|
a581683e3c | |
|
fa558b198a | |
|
91724cfd41 | |
|
edd1d1f6ba | |
|
1a83f0ce07 | |
|
2566cd2659 | |
|
7db8aee1f8 | |
|
f50e0ed3bf | |
|
98865c84e6 | |
|
0f51ac53c8 | |
|
50f48ce4b3 | |
|
52e218fc61 | |
|
3482808f92 | |
|
aac1558d35 | |
|
3a95207b18 | |
|
67dfc24d13 | |
|
4da0062093 | |
|
d750e2f2bf | |
|
ba3fbbb53e | |
|
fa03ee3ea0 | |
|
544ce3df68 | |
|
cc5ef43352 | |
|
5df5358ff9 | |
|
2265efa65a | |
|
8aa636f1b5 | |
|
c59961a007 | |
|
05ed2e066d | |
|
b6404642e4 | |
|
3a2e1b53e5 | |
|
6ace22f694 | |
|
6f6da8e97b | |
|
a67992576e | |
|
84cc815637 | |
|
70820c9c98 | |
|
a91b0013af | |
|
543428d1c4 | |
|
d88392f1f6 | |
|
c31bd9905e | |
|
cba5376d4f | |
|
79feac10d8 | |
|
7b991eb8ee | |
|
24d512c91f | |
|
8e8b957b64 | |
|
6056c27674 | |
|
36c1a58d7e | |
|
ae901d5b33 | |
|
96b9726a3c | |
|
4fe686a430 | |
|
3cb2448d98 | |
|
6ed423348f | |
|
1623f6691b | |
|
3832c300e8 | |
|
7af25044da | |
|
1a6db11afb | |
|
9ff9bbbc47 | |
|
b2bc62b37f | |
|
67d2550df7 | |
|
a2c8b5531e | |
|
408f50382f | |
|
dccab55151 | |
|
7a33f524c6 | |
|
855c0d44c8 | |
|
92171c8c10 | |
|
782d90765e | |
|
73e72d16c5 | |
|
b8750e7396 | |
|
97b6cb1aeb | |
|
2b4f068bdb | |
|
4c88a5c5ae | |
|
f76f81aa6f | |
|
7ee837dd68 | |
|
d44a862c51 | |
|
bd0e774416 | |
|
01307e4b8d | |
|
23e9b2c9d4 | |
|
1b0427a576 | |
|
9641d30242 | |
|
c2e94ca503 | |
|
e36558fb20 | |
|
7377dfa3e5 | |
|
337542d65b | |
|
4305a14262 | |
|
d3e44dff6c | |
|
c5b8df417a | |
|
163865fa38 | |
|
0b0eaa35e1 | |
|
b12d7a1290 | |
|
5edb11aa43 | |
|
821b679880 | |
|
79ec45326d | |
|
345ad05c0c | |
|
7c0ba21066 | |
|
70fe4e2735 | |
|
9b3bebdbac | |
|
990bed7c73 | |
|
6a65641968 | |
|
f2b1ab6bbc | |
|
69cfb424ed | |
|
e1e2202f0a | |
|
205c0f56b5 | |
|
64791740ae | |
|
82c8af5dbe | |
|
e03f7e4910 | |
|
9e523945a2 | |
|
585eff559f | |
|
6faeace534 | |
|
3ad93853da | |
|
2ebe70a0a7 | |
|
0230ffb631 | |
|
a4587170bd | |
|
a64613cb08 | |
|
ed3efcd313 | |
|
1e7b28d116 | |
|
fcfce5aef7 | |
|
4a787e9b76 | |
|
710763dd43 | |
|
8dccd76acd | |
|
a3d85c236b | |
|
95d27c97c9 | |
|
afb79f83b3 | |
|
efe7a1b26d | |
|
c576a626de | |
|
09c0e7d0cd | |
|
d522031d97 | |
|
f422062046 | |
|
4831145b6d | |
|
10c2760fab | |
|
13a27b8dd7 | |
|
942f114031 | |
|
b6717882da | |
|
3a5523026c | |
|
644f8e67b2 | |
|
476d81d4ba | |
|
b9bc02fc91 | |
|
b9f760c579 | |
|
9da16fa551 | |
|
f98455cb25 | |
|
8dd4460107 | |
|
1e6e6f0a46 | |
|
b6fda29776 | |
|
938d466e86 | |
|
ff5a72e86f | |
|
52dde339a1 | |
|
35a4ee03bf | |
|
11288ef6c1 | |
|
2a04234bdc | |
|
08a05f9af7 | |
|
87cccf7779 | |
|
c4717788c4 | |
|
c533eff8e7 | |
|
4628bb89a6 | |
|
9bb5fd51d1 | |
|
7b8dc61f0b | |
|
0198fdbe95 | |
|
a9904eef9e | |
|
5a72bc815b | |
|
2ce508cc43 | |
|
69f7857a7a | |
|
c1a2d5992c | |
|
0167eb5d20 | |
|
66f788143e | |
|
55da1afddb | |
|
39a73cd3bd | |
|
3b2a820e45 | |
|
23e8a6cc2f | |
|
52f2fda3f7 | |
|
8a149c9296 | |
|
7e0cbb5924 | |
|
6c0d90b944 | |
|
2911f5b534 | |
|
0a703e3517 | |
|
ee59411d80 | |
|
f6d3d889a6 | |
|
a3f27ceda4 | |
|
e35b5a7595 | |
|
cf7237e44d | |
|
736476d22b | |
|
a2ab8dc0e3 | |
|
6e7ca2c0dc | |
|
6885e995e1 | |
|
0e1bb963f7 | |
|
b66aedfe5c | |
|
626adbf67c | |
|
ae92d91104 | |
|
2387b5d4a9 | |
|
2c1a1fa4ee | |
|
695a3a4c4e | |
|
e802cf6daf | |
|
cd99eadfc3 | |
|
37d4665f53 | |
|
5f060d93ca | |
|
7ca4de7334 | |
|
76f1672b11 | |
|
3a132bfa52 | |
|
30363fdd34 | |
|
5edcd3c1b7 | |
|
e62b626c0f | |
|
6c470468a0 | |
|
a6c0914bb9 | |
|
29de537bfa | |
|
a4a670721b | |
|
74be087390 | |
|
b43177846d | |
|
a2cdf2cd0f | |
|
2241ea0052 | |
|
3c47ed7b1d | |
|
9bb4aa730a | |
|
67ec836891 | |
|
ca7102a0c4 | |
|
38b01a1f78 | |
|
8161d73803 | |
|
c7ea66111e | |
|
893803dc85 | |
|
2cad252f0c | |
|
e6eb58901f | |
|
006dd593ee | |
|
7c924dc3c8 | |
|
825f36b139 | |
|
62c90b1dff | |
|
84b3c4c640 | |
|
a53d8f189c | |
|
494066f4a4 | |
|
15b54ba29c | |
|
c7fb780f6b | |
|
0daeb9f98b | |
|
b3597c01bb | |
|
f5b4a60379 | |
|
665a8df3fd | |
|
68ddbb0384 | |
|
0dcf3e9d26 | |
|
b62222be7c | |
|
6ecac9f4d1 | |
|
dbfbe37046 | |
|
988c0f82a8 | |
|
0a3c89578c | |
|
47024d970a | |
|
ea0b9ced4d | |
|
df1723ca5c | |
|
3658357fea | |
|
9add4d0573 | |
|
99abadede2 | |
|
79e6f1fbcf | |
|
aaeb01be6d | |
|
5047b8fe41 | |
|
b84662911c | |
|
753940e964 | |
|
b858201395 | |
|
c8d9720a72 | |
|
cccad306d6 | |
|
51b4b4971a | |
|
d513755309 | |
|
fd21d18170 | |
|
ae08979dcc | |
|
ada3ee1529 | |
|
3aea7778be | |
|
026bd8791b | |
|
81ab7e0c68 | |
|
c9834df380 | |
|
c2262d518b | |
|
6898b8387d | |
|
5a059075db | |
|
21560784da | |
|
2146712740 | |
|
e6df86ea96 | |
|
1bac1a3a1c |
2
doc.go
2
doc.go
|
@ -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
|
||||
|
|
139
go.mod
139
go.mod
|
@ -2,124 +2,125 @@
|
|||
|
||||
module k8s.io/apiserver
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
godebug default=go1.23
|
||||
godebug default=go1.24
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
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/btree v1.0.1
|
||||
github.com/google/cel-go v0.22.0
|
||||
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.16
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16
|
||||
go.etcd.io/etcd/client/v3 v3.5.16
|
||||
go.etcd.io/etcd/server/v3 v3.5.16
|
||||
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/metric v1.28.0
|
||||
go.opentelemetry.io/otel/sdk v1.28.0
|
||||
go.opentelemetry.io/otel/trace v1.28.0
|
||||
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.28.0
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/time v0.7.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.35.1
|
||||
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-20241108114315-3f43b5a94246
|
||||
k8s.io/apimachinery v0.0.0-20241108022104-96b97de8d6ba
|
||||
k8s.io/client-go v0.0.0-20241108115827-ec126553e25c
|
||||
k8s.io/component-base v0.0.0-20241108123302-804c0075c12f
|
||||
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-20241107031913-7a7a59ea9c74
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2
|
||||
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.18.0 // indirect
|
||||
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/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/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // 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.23.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // 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/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.4.0 // 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.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/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-20221125231312-a49e3df8f510 // indirect
|
||||
go.etcd.io/bbolt v1.3.11 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.16 // indirect
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.16 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // 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
|
||||
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.23.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // 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.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
352
go.sum
352
go.sum
|
@ -1,59 +1,42 @@
|
|||
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
|
||||
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
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=
|
||||
|
@ -69,63 +52,52 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
|
|||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
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/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.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
|
||||
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
|
||||
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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
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/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.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
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=
|
||||
|
@ -133,6 +105,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
|
||||
|
@ -140,8 +114,9 @@ github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVO
|
|||
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=
|
||||
|
@ -150,54 +125,48 @@ 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/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/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/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.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=
|
||||
|
@ -206,116 +175,97 @@ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chq
|
|||
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.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
|
||||
go.etcd.io/etcd/client/v2 v2.305.16 h1:kQrn9o5czVNaukf2A2At43cE9ZtWauOtf9vRZuiKXow=
|
||||
go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE=
|
||||
go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE=
|
||||
go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.16 h1:cnavs5WSPWeK4TYwPYfmcr3Joz9BH+TZ6qoUtz6/+mc=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
|
||||
go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE=
|
||||
go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s=
|
||||
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.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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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/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/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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
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.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
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=
|
||||
|
@ -325,67 +275,53 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
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-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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.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-20241108114315-3f43b5a94246 h1:lTiwDILcPkCelpoYPkQ0jt8tmY0/s6Q9sM2mq2qie8g=
|
||||
k8s.io/api v0.0.0-20241108114315-3f43b5a94246/go.mod h1:jw6pQTESH9mdZL2vOK3twojvpPxipl5TpLZpPyl5ZYU=
|
||||
k8s.io/apimachinery v0.0.0-20241108022104-96b97de8d6ba h1:ghB5Iygt6Ge8UyIwW7C1kJx4kP7AUTCL9Qg6GCsUUOY=
|
||||
k8s.io/apimachinery v0.0.0-20241108022104-96b97de8d6ba/go.mod h1:HqhdaJUgQqky29T1V0o2yFkt/pZqLFIDyn9Zi/8rxoY=
|
||||
k8s.io/client-go v0.0.0-20241108115827-ec126553e25c h1:oFkwLbL3wiQ1J3kiXB3e8RekXFhWB4G3jtWxX4qz/fk=
|
||||
k8s.io/client-go v0.0.0-20241108115827-ec126553e25c/go.mod h1:qdg0yiHKZF4DimKIPqdu9N9y1VLkHDn8Jtbsblkf6O0=
|
||||
k8s.io/component-base v0.0.0-20241108123302-804c0075c12f h1:zBczlQ6ESDT3aiohj4+/Bl8IWFr8VX6WHXX785ovbIg=
|
||||
k8s.io/component-base v0.0.0-20241108123302-804c0075c12f/go.mod h1:sML5FQmBLTD6jQvLcucPw9JohoW6PnlBvP7NBkLIYb8=
|
||||
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-20241107031913-7a7a59ea9c74 h1:4Ib0RezwyOw06/9W8ACHhLDxtRCgZc0IERyyivhOp/E=
|
||||
k8s.io/kms v0.0.0-20241107031913-7a7a59ea9c74/go.mod h1:SYc/vvTslZFejoOBnAF/ZLaqnmJMXVJzxWZakvq0LOg=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -184,6 +184,7 @@ func TestCondition(t *testing.T) {
|
|||
|
||||
v130 := version.MajorMinor(1, 30)
|
||||
v131 := version.MajorMinor(1, 31)
|
||||
v127 := version.MajorMinor(1, 27)
|
||||
|
||||
var nilUnstructured *unstructured.Unstructured
|
||||
cases := []struct {
|
||||
|
@ -200,6 +201,7 @@ func TestCondition(t *testing.T) {
|
|||
enableSelectors bool
|
||||
|
||||
compatibilityVersion *version.Version
|
||||
envType environment.Type
|
||||
}{
|
||||
{
|
||||
name: "valid syntax for object",
|
||||
|
@ -867,6 +869,52 @@ func TestCondition(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 {
|
||||
|
@ -891,7 +939,11 @@ func TestCondition(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
c := NewConditionCompiler(env)
|
||||
f := c.CompileCondition(tc.validations, OptionalVariableDeclarations{HasParams: tc.hasParamKind, HasAuthorizer: tc.authorizer != nil, StrictCost: tc.strictCost}, environment.NewExpressions)
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -656,8 +656,8 @@ func TestDispatcher(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
informerFactory.WaitForCacheSync(ctx.Done())
|
||||
informerFactory.Start(ctx.Done())
|
||||
informerFactory.WaitForCacheSync(ctx.Done())
|
||||
for i, h := range tc.policyHooks {
|
||||
tc.policyHooks[i].ParamInformer = paramInformer
|
||||
tc.policyHooks[i].ParamScope = testParamScope{}
|
||||
|
|
|
@ -46,16 +46,6 @@ func TestJSONPatch(t *testing.T) {
|
|||
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 false test operation",
|
||||
expression: `[
|
||||
|
|
|
@ -51,35 +51,6 @@ func TestApplyConfiguration(t *testing.T) {
|
|||
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 add to listType=map",
|
||||
expression: `Object{
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"}},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{{
|
||||
|
|
|
@ -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{})
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -16,4 +16,4 @@ limitations under the License.
|
|||
|
||||
// +groupName=apidiscovery.k8s.io
|
||||
|
||||
package v2 // import "k8s.io/apiserver/pkg/apis/apidiscovery/v2"
|
||||
package v2
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -16,4 +16,4 @@ limitations under the License.
|
|||
|
||||
// +groupName=apidiscovery.k8s.io
|
||||
|
||||
package v2beta1 // import "k8s.io/apiserver/pkg/apis/apidiscovery/v2beta1"
|
||||
package v2beta1
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}},
|
||||
},
|
||||
|
@ -291,8 +295,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,
|
||||
},
|
||||
}},
|
||||
},
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -53,7 +54,13 @@ 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,6 +47,7 @@ func init() {
|
|||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&AdmissionConfiguration{},
|
||||
&AuthenticationConfiguration{},
|
||||
&AuthorizationConfiguration{},
|
||||
&EncryptionConfiguration{},
|
||||
)
|
||||
|
|
|
@ -51,6 +51,349 @@ type AdmissionPluginConfiguration struct {
|
|||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// AuthenticationConfiguration provides versioned configuration for authentication.
|
||||
type AuthenticationConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
// jwt is a list of authenticator to authenticate Kubernetes users using
|
||||
// JWT compliant tokens. The authenticator will attempt to parse a raw ID token,
|
||||
// verify it's been signed by the configured issuer. The public key to verify the
|
||||
// signature is discovered from the issuer's public endpoint using OIDC discovery.
|
||||
// For an incoming token, each JWT authenticator will be attempted in
|
||||
// the order in which it is specified in this list. Note however that
|
||||
// other authenticators may run before or after the JWT authenticators.
|
||||
// The specific position of JWT authenticators in relation to other
|
||||
// authenticators is neither defined nor stable across releases. Since
|
||||
// each JWT authenticator must have a unique issuer URL, at most one
|
||||
// JWT authenticator will attempt to cryptographically validate the token.
|
||||
//
|
||||
// The minimum valid JWT payload must contain the following claims:
|
||||
// {
|
||||
// "iss": "https://issuer.example.com",
|
||||
// "aud": ["audience"],
|
||||
// "exp": 1234567890,
|
||||
// "<username claim>": "username"
|
||||
// }
|
||||
JWT []JWTAuthenticator `json:"jwt"`
|
||||
|
||||
// If present --anonymous-auth must not be set
|
||||
Anonymous *AnonymousAuthConfig `json:"anonymous,omitempty"`
|
||||
}
|
||||
|
||||
// AnonymousAuthConfig provides the configuration for the anonymous authenticator.
|
||||
type AnonymousAuthConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
// If set, anonymous auth is only allowed if the request meets one of the
|
||||
// conditions.
|
||||
Conditions []AnonymousAuthCondition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// AnonymousAuthCondition describes the condition under which anonymous auth
|
||||
// should be enabled.
|
||||
type AnonymousAuthCondition struct {
|
||||
// Path for which anonymous auth is enabled.
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// JWTAuthenticator provides the configuration for a single JWT authenticator.
|
||||
type JWTAuthenticator struct {
|
||||
// issuer contains the basic OIDC provider connection options.
|
||||
// +required
|
||||
Issuer Issuer `json:"issuer"`
|
||||
|
||||
// claimValidationRules are rules that are applied to validate token claims to authenticate users.
|
||||
// +optional
|
||||
ClaimValidationRules []ClaimValidationRule `json:"claimValidationRules,omitempty"`
|
||||
|
||||
// claimMappings points claims of a token to be treated as user attributes.
|
||||
// +required
|
||||
ClaimMappings ClaimMappings `json:"claimMappings"`
|
||||
|
||||
// userValidationRules are rules that are applied to final user before completing authentication.
|
||||
// These allow invariants to be applied to incoming identities such as preventing the
|
||||
// use of the system: prefix that is commonly used by Kubernetes components.
|
||||
// The validation rules are logically ANDed together and must all return true for the validation to pass.
|
||||
// +optional
|
||||
UserValidationRules []UserValidationRule `json:"userValidationRules,omitempty"`
|
||||
}
|
||||
|
||||
// Issuer provides the configuration for an external provider's specific settings.
|
||||
type Issuer struct {
|
||||
// url points to the issuer URL in a format https://url or https://url/path.
|
||||
// This must match the "iss" claim in the presented JWT, and the issuer returned from discovery.
|
||||
// Same value as the --oidc-issuer-url flag.
|
||||
// Discovery information is fetched from "{url}/.well-known/openid-configuration" unless overridden by discoveryURL.
|
||||
// Required to be unique across all JWT authenticators.
|
||||
// Note that egress selection configuration is not used for this network connection.
|
||||
// +required
|
||||
URL string `json:"url"`
|
||||
|
||||
// discoveryURL, if specified, overrides the URL used to fetch discovery
|
||||
// information instead of using "{url}/.well-known/openid-configuration".
|
||||
// The exact value specified is used, so "/.well-known/openid-configuration"
|
||||
// must be included in discoveryURL if needed.
|
||||
//
|
||||
// The "issuer" field in the fetched discovery information must match the "issuer.url" field
|
||||
// in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT.
|
||||
// This is for scenarios where the well-known and jwks endpoints are hosted at a different
|
||||
// location than the issuer (such as locally in the cluster).
|
||||
//
|
||||
// Example:
|
||||
// A discovery url that is exposed using kubernetes service 'oidc' in namespace 'oidc-namespace'
|
||||
// and discovery information is available at '/.well-known/openid-configuration'.
|
||||
// discoveryURL: "https://oidc.oidc-namespace/.well-known/openid-configuration"
|
||||
// certificateAuthority is used to verify the TLS connection and the hostname on the leaf certificate
|
||||
// must be set to 'oidc.oidc-namespace'.
|
||||
//
|
||||
// curl https://oidc.oidc-namespace/.well-known/openid-configuration (.discoveryURL field)
|
||||
// {
|
||||
// issuer: "https://oidc.example.com" (.url field)
|
||||
// }
|
||||
//
|
||||
// discoveryURL must be different from url.
|
||||
// Required to be unique across all JWT authenticators.
|
||||
// Note that egress selection configuration is not used for this network connection.
|
||||
// +optional
|
||||
DiscoveryURL *string `json:"discoveryURL,omitempty"`
|
||||
|
||||
// certificateAuthority contains PEM-encoded certificate authority certificates
|
||||
// used to validate the connection when fetching discovery information.
|
||||
// If unset, the system verifier is used.
|
||||
// Same value as the content of the file referenced by the --oidc-ca-file flag.
|
||||
// +optional
|
||||
CertificateAuthority string `json:"certificateAuthority,omitempty"`
|
||||
|
||||
// audiences is the set of acceptable audiences the JWT must be issued to.
|
||||
// At least one of the entries must match the "aud" claim in presented JWTs.
|
||||
// Same value as the --oidc-client-id flag (though this field supports an array).
|
||||
// Required to be non-empty.
|
||||
// +required
|
||||
Audiences []string `json:"audiences"`
|
||||
|
||||
// audienceMatchPolicy defines how the "audiences" field is used to match the "aud" claim in the presented JWT.
|
||||
// Allowed values are:
|
||||
// 1. "MatchAny" when multiple audiences are specified and
|
||||
// 2. empty (or unset) or "MatchAny" when a single audience is specified.
|
||||
//
|
||||
// - MatchAny: the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field.
|
||||
// For example, if "audiences" is ["foo", "bar"], the "aud" claim in the presented JWT must contain either "foo" or "bar" (and may contain both).
|
||||
//
|
||||
// - "": The match policy can be empty (or unset) when a single audience is specified in the "audiences" field. The "aud" claim in the presented JWT must contain the single audience (and may contain others).
|
||||
//
|
||||
// For more nuanced audience validation, use claimValidationRules.
|
||||
// example: claimValidationRule[].expression: 'sets.equivalent(claims.aud, ["bar", "foo", "baz"])' to require an exact match.
|
||||
// +optional
|
||||
AudienceMatchPolicy AudienceMatchPolicyType `json:"audienceMatchPolicy,omitempty"`
|
||||
}
|
||||
|
||||
// AudienceMatchPolicyType is a set of valid values for issuer.audienceMatchPolicy
|
||||
type AudienceMatchPolicyType string
|
||||
|
||||
// Valid types for AudienceMatchPolicyType
|
||||
const (
|
||||
// MatchAny means the "aud" claim in the presented JWT must match at least one of the entries in the "audiences" field.
|
||||
AudienceMatchPolicyMatchAny AudienceMatchPolicyType = "MatchAny"
|
||||
)
|
||||
|
||||
// ClaimValidationRule provides the configuration for a single claim validation rule.
|
||||
type ClaimValidationRule struct {
|
||||
// claim is the name of a required claim.
|
||||
// Same as --oidc-required-claim flag.
|
||||
// Only string claim keys are supported.
|
||||
// Mutually exclusive with expression and message.
|
||||
// +optional
|
||||
Claim string `json:"claim,omitempty"`
|
||||
// requiredValue is the value of a required claim.
|
||||
// Same as --oidc-required-claim flag.
|
||||
// Only string claim values are supported.
|
||||
// If claim is set and requiredValue is not set, the claim must be present with a value set to the empty string.
|
||||
// Mutually exclusive with expression and message.
|
||||
// +optional
|
||||
RequiredValue string `json:"requiredValue,omitempty"`
|
||||
|
||||
// expression represents the expression which will be evaluated by CEL.
|
||||
// Must produce a boolean.
|
||||
//
|
||||
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
|
||||
// - 'claims' is a map of claim names to claim values.
|
||||
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
|
||||
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
|
||||
// Must return true for the validation to pass.
|
||||
//
|
||||
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
|
||||
//
|
||||
// Mutually exclusive with claim and requiredValue.
|
||||
// +optional
|
||||
Expression string `json:"expression,omitempty"`
|
||||
// message customizes the returned error message when expression returns false.
|
||||
// message is a literal string.
|
||||
// Mutually exclusive with claim and requiredValue.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// ClaimMappings provides the configuration for claim mapping
|
||||
type ClaimMappings struct {
|
||||
// username represents an option for the username attribute.
|
||||
// The claim's value must be a singular string.
|
||||
// Same as the --oidc-username-claim and --oidc-username-prefix flags.
|
||||
// If username.expression is set, the expression must produce a string value.
|
||||
// If username.expression uses 'claims.email', then 'claims.email_verified' must be used in
|
||||
// username.expression or extra[*].valueExpression or claimValidationRules[*].expression.
|
||||
// An example claim validation rule expression that matches the validation automatically
|
||||
// applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. 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.
|
||||
//
|
||||
// In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set,
|
||||
// the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly.
|
||||
// For claim, if --oidc-username-claim was not set with legacy flag approach, configure username.claim="sub" in the authentication config.
|
||||
// For prefix:
|
||||
// (1) --oidc-username-prefix="-", no prefix was added to the username. For the same behavior using authentication config,
|
||||
// set username.prefix=""
|
||||
// (2) --oidc-username-prefix="" and --oidc-username-claim != "email", prefix was "<value of --oidc-issuer-url>#". For the same
|
||||
// behavior using authentication config, set username.prefix="<value of issuer.url>#"
|
||||
// (3) --oidc-username-prefix="<value>". For the same behavior using authentication config, set username.prefix="<value>"
|
||||
// +required
|
||||
Username PrefixedClaimOrExpression `json:"username"`
|
||||
// groups represents an option for the groups attribute.
|
||||
// The claim's value must be a string or string array claim.
|
||||
// If groups.claim is set, the prefix must be specified (and can be the empty string).
|
||||
// If groups.expression is set, the expression must produce a string or string array value.
|
||||
// "", [], and null values are treated as the group mapping not being present.
|
||||
// +optional
|
||||
Groups PrefixedClaimOrExpression `json:"groups,omitempty"`
|
||||
|
||||
// uid represents an option for the uid attribute.
|
||||
// Claim must be a singular string claim.
|
||||
// If uid.expression is set, the expression must produce a string value.
|
||||
// +optional
|
||||
UID ClaimOrExpression `json:"uid"`
|
||||
|
||||
// extra represents an option for the extra attribute.
|
||||
// expression must produce a string or string array value.
|
||||
// If the value is empty, the extra mapping will not be present.
|
||||
//
|
||||
// hard-coded extra key/value
|
||||
// - key: "foo"
|
||||
// valueExpression: "'bar'"
|
||||
// This will result in an extra attribute - foo: ["bar"]
|
||||
//
|
||||
// hard-coded key, value copying claim value
|
||||
// - key: "foo"
|
||||
// valueExpression: "claims.some_claim"
|
||||
// This will result in an extra attribute - foo: [value of some_claim]
|
||||
//
|
||||
// hard-coded key, value derived from claim value
|
||||
// - key: "admin"
|
||||
// valueExpression: '(has(claims.is_admin) && claims.is_admin) ? "true":""'
|
||||
// This will result in:
|
||||
// - if is_admin claim is present and true, extra attribute - admin: ["true"]
|
||||
// - if is_admin claim is present and false or is_admin claim is not present, no extra attribute will be added
|
||||
//
|
||||
// +optional
|
||||
Extra []ExtraMapping `json:"extra,omitempty"`
|
||||
}
|
||||
|
||||
// PrefixedClaimOrExpression provides the configuration for a single prefixed claim or expression.
|
||||
type PrefixedClaimOrExpression struct {
|
||||
// claim is the JWT claim to use.
|
||||
// Mutually exclusive with expression.
|
||||
// +optional
|
||||
Claim string `json:"claim,omitempty"`
|
||||
// prefix is prepended to claim's value to prevent clashes with existing names.
|
||||
// prefix needs to be set if claim is set and can be the empty string.
|
||||
// Mutually exclusive with expression.
|
||||
// +optional
|
||||
Prefix *string `json:"prefix,omitempty"`
|
||||
|
||||
// expression represents the expression which will be evaluated by CEL.
|
||||
//
|
||||
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
|
||||
// - 'claims' is a map of claim names to claim values.
|
||||
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
|
||||
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
|
||||
//
|
||||
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
|
||||
//
|
||||
// Mutually exclusive with claim and prefix.
|
||||
// +optional
|
||||
Expression string `json:"expression,omitempty"`
|
||||
}
|
||||
|
||||
// ClaimOrExpression provides the configuration for a single claim or expression.
|
||||
type ClaimOrExpression struct {
|
||||
// claim is the JWT claim to use.
|
||||
// Either claim or expression must be set.
|
||||
// Mutually exclusive with expression.
|
||||
// +optional
|
||||
Claim string `json:"claim,omitempty"`
|
||||
|
||||
// expression represents the expression which will be evaluated by CEL.
|
||||
//
|
||||
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
|
||||
// - 'claims' is a map of claim names to claim values.
|
||||
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
|
||||
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
|
||||
//
|
||||
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
|
||||
//
|
||||
// Mutually exclusive with claim.
|
||||
// +optional
|
||||
Expression string `json:"expression,omitempty"`
|
||||
}
|
||||
|
||||
// ExtraMapping provides the configuration for a single extra mapping.
|
||||
type ExtraMapping struct {
|
||||
// key is a string to use as the extra attribute key.
|
||||
// key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
|
||||
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
|
||||
// be valid HTTP Path characters as defined by RFC 3986.
|
||||
// key must be lowercase.
|
||||
// Required to be unique.
|
||||
// +required
|
||||
Key string `json:"key"`
|
||||
|
||||
// valueExpression is a CEL expression to extract extra attribute value.
|
||||
// valueExpression must produce a string or string array value.
|
||||
// "", [], and null values are treated as the extra mapping not being present.
|
||||
// Empty string values contained within a string array are filtered out.
|
||||
//
|
||||
// CEL expressions have access to the contents of the token claims, organized into CEL variable:
|
||||
// - 'claims' is a map of claim names to claim values.
|
||||
// For example, a variable named 'sub' can be accessed as 'claims.sub'.
|
||||
// Nested claims can be accessed using dot notation, e.g. 'claims.foo.bar'.
|
||||
//
|
||||
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
|
||||
//
|
||||
// +required
|
||||
ValueExpression string `json:"valueExpression"`
|
||||
}
|
||||
|
||||
// UserValidationRule provides the configuration for a single user info validation rule.
|
||||
type UserValidationRule struct {
|
||||
// expression represents the expression which will be evaluated by CEL.
|
||||
// Must return true for the validation to pass.
|
||||
//
|
||||
// CEL expressions have access to the contents of UserInfo, organized into CEL variable:
|
||||
// - 'user' - authentication.k8s.io/v1, Kind=UserInfo object
|
||||
// Refer to https://github.com/kubernetes/api/blob/release-1.28/authentication/v1/types.go#L105-L122 for the definition.
|
||||
// API documentation: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#userinfo-v1-authentication-k8s-io
|
||||
//
|
||||
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
|
||||
//
|
||||
// +required
|
||||
Expression string `json:"expression"`
|
||||
|
||||
// message customizes the returned error message when rule returns false.
|
||||
// message is a literal string.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type AuthorizationConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
|
@ -97,11 +440,23 @@ type WebhookConfiguration struct {
|
|||
// Same as setting `--authorization-webhook-cache-authorized-ttl` flag
|
||||
// Default: 5m0s
|
||||
AuthorizedTTL metav1.Duration `json:"authorizedTTL"`
|
||||
// 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
|
||||
// +optional
|
||||
CacheAuthorizedRequests *bool `json:"cacheAuthorizedRequests,omitempty"`
|
||||
// The duration to cache 'unauthorized' responses from the webhook
|
||||
// authorizer.
|
||||
// Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
|
||||
// Default: 30s
|
||||
UnauthorizedTTL metav1.Duration `json:"unauthorizedTTL"`
|
||||
// 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
|
||||
// +optional
|
||||
CacheUnauthorizedRequests *bool `json:"cacheUnauthorizedRequests,omitempty"`
|
||||
// Timeout for the webhook request
|
||||
// Maximum allowed value is 30s.
|
||||
// Required, no default value.
|
||||
|
|
|
@ -67,6 +67,36 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*AnonymousAuthCondition)(nil), (*apiserver.AnonymousAuthCondition)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(a.(*AnonymousAuthCondition), b.(*apiserver.AnonymousAuthCondition), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.AnonymousAuthCondition)(nil), (*AnonymousAuthCondition)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_AnonymousAuthCondition_To_v1_AnonymousAuthCondition(a.(*apiserver.AnonymousAuthCondition), b.(*AnonymousAuthCondition), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*AnonymousAuthConfig)(nil), (*apiserver.AnonymousAuthConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(a.(*AnonymousAuthConfig), b.(*apiserver.AnonymousAuthConfig), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.AnonymousAuthConfig)(nil), (*AnonymousAuthConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_AnonymousAuthConfig_To_v1_AnonymousAuthConfig(a.(*apiserver.AnonymousAuthConfig), b.(*AnonymousAuthConfig), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*AuthenticationConfiguration)(nil), (*apiserver.AuthenticationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(a.(*AuthenticationConfiguration), b.(*apiserver.AuthenticationConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.AuthenticationConfiguration)(nil), (*AuthenticationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_AuthenticationConfiguration_To_v1_AuthenticationConfiguration(a.(*apiserver.AuthenticationConfiguration), b.(*AuthenticationConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*AuthorizationConfiguration)(nil), (*apiserver.AuthorizationConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(a.(*AuthorizationConfiguration), b.(*apiserver.AuthorizationConfiguration), scope)
|
||||
}); err != nil {
|
||||
|
@ -87,6 +117,36 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*ClaimMappings)(nil), (*apiserver.ClaimMappings)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ClaimMappings_To_apiserver_ClaimMappings(a.(*ClaimMappings), b.(*apiserver.ClaimMappings), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.ClaimMappings)(nil), (*ClaimMappings)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_ClaimMappings_To_v1_ClaimMappings(a.(*apiserver.ClaimMappings), b.(*ClaimMappings), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*ClaimOrExpression)(nil), (*apiserver.ClaimOrExpression)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ClaimOrExpression_To_apiserver_ClaimOrExpression(a.(*ClaimOrExpression), b.(*apiserver.ClaimOrExpression), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.ClaimOrExpression)(nil), (*ClaimOrExpression)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_ClaimOrExpression_To_v1_ClaimOrExpression(a.(*apiserver.ClaimOrExpression), b.(*ClaimOrExpression), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*ClaimValidationRule)(nil), (*apiserver.ClaimValidationRule)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ClaimValidationRule_To_apiserver_ClaimValidationRule(a.(*ClaimValidationRule), b.(*apiserver.ClaimValidationRule), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.ClaimValidationRule)(nil), (*ClaimValidationRule)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_ClaimValidationRule_To_v1_ClaimValidationRule(a.(*apiserver.ClaimValidationRule), b.(*ClaimValidationRule), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*EncryptionConfiguration)(nil), (*apiserver.EncryptionConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_EncryptionConfiguration_To_apiserver_EncryptionConfiguration(a.(*EncryptionConfiguration), b.(*apiserver.EncryptionConfiguration), scope)
|
||||
}); err != nil {
|
||||
|
@ -97,6 +157,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*ExtraMapping)(nil), (*apiserver.ExtraMapping)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ExtraMapping_To_apiserver_ExtraMapping(a.(*ExtraMapping), b.(*apiserver.ExtraMapping), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.ExtraMapping)(nil), (*ExtraMapping)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_ExtraMapping_To_v1_ExtraMapping(a.(*apiserver.ExtraMapping), b.(*ExtraMapping), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*IdentityConfiguration)(nil), (*apiserver.IdentityConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_IdentityConfiguration_To_apiserver_IdentityConfiguration(a.(*IdentityConfiguration), b.(*apiserver.IdentityConfiguration), scope)
|
||||
}); err != nil {
|
||||
|
@ -107,6 +177,26 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*Issuer)(nil), (*apiserver.Issuer)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_Issuer_To_apiserver_Issuer(a.(*Issuer), b.(*apiserver.Issuer), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.Issuer)(nil), (*Issuer)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_Issuer_To_v1_Issuer(a.(*apiserver.Issuer), b.(*Issuer), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*JWTAuthenticator)(nil), (*apiserver.JWTAuthenticator)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_JWTAuthenticator_To_apiserver_JWTAuthenticator(a.(*JWTAuthenticator), b.(*apiserver.JWTAuthenticator), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.JWTAuthenticator)(nil), (*JWTAuthenticator)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_JWTAuthenticator_To_v1_JWTAuthenticator(a.(*apiserver.JWTAuthenticator), b.(*JWTAuthenticator), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*KMSConfiguration)(nil), (*apiserver.KMSConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_KMSConfiguration_To_apiserver_KMSConfiguration(a.(*KMSConfiguration), b.(*apiserver.KMSConfiguration), scope)
|
||||
}); err != nil {
|
||||
|
@ -127,6 +217,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*PrefixedClaimOrExpression)(nil), (*apiserver.PrefixedClaimOrExpression)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(a.(*PrefixedClaimOrExpression), b.(*apiserver.PrefixedClaimOrExpression), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.PrefixedClaimOrExpression)(nil), (*PrefixedClaimOrExpression)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression(a.(*apiserver.PrefixedClaimOrExpression), b.(*PrefixedClaimOrExpression), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*ProviderConfiguration)(nil), (*apiserver.ProviderConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_ProviderConfiguration_To_apiserver_ProviderConfiguration(a.(*ProviderConfiguration), b.(*apiserver.ProviderConfiguration), scope)
|
||||
}); err != nil {
|
||||
|
@ -157,6 +257,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*UserValidationRule)(nil), (*apiserver.UserValidationRule)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_UserValidationRule_To_apiserver_UserValidationRule(a.(*UserValidationRule), b.(*apiserver.UserValidationRule), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.UserValidationRule)(nil), (*UserValidationRule)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_UserValidationRule_To_v1_UserValidationRule(a.(*apiserver.UserValidationRule), b.(*UserValidationRule), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*WebhookConfiguration)(nil), (*apiserver.WebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(a.(*WebhookConfiguration), b.(*apiserver.WebhookConfiguration), scope)
|
||||
}); err != nil {
|
||||
|
@ -254,8 +364,102 @@ func Convert_apiserver_AdmissionPluginConfiguration_To_v1_AdmissionPluginConfigu
|
|||
return autoConvert_apiserver_AdmissionPluginConfiguration_To_v1_AdmissionPluginConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in *AnonymousAuthCondition, out *apiserver.AnonymousAuthCondition, s conversion.Scope) error {
|
||||
out.Path = in.Path
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition is an autogenerated conversion function.
|
||||
func Convert_v1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in *AnonymousAuthCondition, out *apiserver.AnonymousAuthCondition, s conversion.Scope) error {
|
||||
return autoConvert_v1_AnonymousAuthCondition_To_apiserver_AnonymousAuthCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_AnonymousAuthCondition_To_v1_AnonymousAuthCondition(in *apiserver.AnonymousAuthCondition, out *AnonymousAuthCondition, s conversion.Scope) error {
|
||||
out.Path = in.Path
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_AnonymousAuthCondition_To_v1_AnonymousAuthCondition is an autogenerated conversion function.
|
||||
func Convert_apiserver_AnonymousAuthCondition_To_v1_AnonymousAuthCondition(in *apiserver.AnonymousAuthCondition, out *AnonymousAuthCondition, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_AnonymousAuthCondition_To_v1_AnonymousAuthCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in *AnonymousAuthConfig, out *apiserver.AnonymousAuthConfig, s conversion.Scope) error {
|
||||
out.Enabled = in.Enabled
|
||||
out.Conditions = *(*[]apiserver.AnonymousAuthCondition)(unsafe.Pointer(&in.Conditions))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig is an autogenerated conversion function.
|
||||
func Convert_v1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in *AnonymousAuthConfig, out *apiserver.AnonymousAuthConfig, s conversion.Scope) error {
|
||||
return autoConvert_v1_AnonymousAuthConfig_To_apiserver_AnonymousAuthConfig(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_AnonymousAuthConfig_To_v1_AnonymousAuthConfig(in *apiserver.AnonymousAuthConfig, out *AnonymousAuthConfig, s conversion.Scope) error {
|
||||
out.Enabled = in.Enabled
|
||||
out.Conditions = *(*[]AnonymousAuthCondition)(unsafe.Pointer(&in.Conditions))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_AnonymousAuthConfig_To_v1_AnonymousAuthConfig is an autogenerated conversion function.
|
||||
func Convert_apiserver_AnonymousAuthConfig_To_v1_AnonymousAuthConfig(in *apiserver.AnonymousAuthConfig, out *AnonymousAuthConfig, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_AnonymousAuthConfig_To_v1_AnonymousAuthConfig(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(in *AuthenticationConfiguration, out *apiserver.AuthenticationConfiguration, s conversion.Scope) error {
|
||||
if in.JWT != nil {
|
||||
in, out := &in.JWT, &out.JWT
|
||||
*out = make([]apiserver.JWTAuthenticator, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_v1_JWTAuthenticator_To_apiserver_JWTAuthenticator(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.JWT = nil
|
||||
}
|
||||
out.Anonymous = (*apiserver.AnonymousAuthConfig)(unsafe.Pointer(in.Anonymous))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(in *AuthenticationConfiguration, out *apiserver.AuthenticationConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1_AuthenticationConfiguration_To_apiserver_AuthenticationConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_AuthenticationConfiguration_To_v1_AuthenticationConfiguration(in *apiserver.AuthenticationConfiguration, out *AuthenticationConfiguration, s conversion.Scope) error {
|
||||
if in.JWT != nil {
|
||||
in, out := &in.JWT, &out.JWT
|
||||
*out = make([]JWTAuthenticator, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_apiserver_JWTAuthenticator_To_v1_JWTAuthenticator(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.JWT = nil
|
||||
}
|
||||
out.Anonymous = (*AnonymousAuthConfig)(unsafe.Pointer(in.Anonymous))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_AuthenticationConfiguration_To_v1_AuthenticationConfiguration is an autogenerated conversion function.
|
||||
func Convert_apiserver_AuthenticationConfiguration_To_v1_AuthenticationConfiguration(in *apiserver.AuthenticationConfiguration, out *AuthenticationConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_AuthenticationConfiguration_To_v1_AuthenticationConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]apiserver.AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
if in.Authorizers != nil {
|
||||
in, out := &in.Authorizers, &out.Authorizers
|
||||
*out = make([]apiserver.AuthorizerConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Authorizers = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -265,7 +469,17 @@ func Convert_v1_AuthorizationConfiguration_To_apiserver_AuthorizationConfigurati
|
|||
}
|
||||
|
||||
func autoConvert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
if in.Authorizers != nil {
|
||||
in, out := &in.Authorizers, &out.Authorizers
|
||||
*out = make([]AuthorizerConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Authorizers = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -277,7 +491,15 @@ func Convert_apiserver_AuthorizationConfiguration_To_v1_AuthorizationConfigurati
|
|||
func autoConvert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = apiserver.AuthorizerType(in.Type)
|
||||
out.Name = in.Name
|
||||
out.Webhook = (*apiserver.WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
if in.Webhook != nil {
|
||||
in, out := &in.Webhook, &out.Webhook
|
||||
*out = new(apiserver.WebhookConfiguration)
|
||||
if err := Convert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Webhook = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -289,7 +511,15 @@ func Convert_v1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in
|
|||
func autoConvert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = string(in.Type)
|
||||
out.Name = in.Name
|
||||
out.Webhook = (*WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
if in.Webhook != nil {
|
||||
in, out := &in.Webhook, &out.Webhook
|
||||
*out = new(WebhookConfiguration)
|
||||
if err := Convert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Webhook = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -298,6 +528,92 @@ func Convert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(in
|
|||
return autoConvert_apiserver_AuthorizerConfiguration_To_v1_AuthorizerConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ClaimMappings_To_apiserver_ClaimMappings(in *ClaimMappings, out *apiserver.ClaimMappings, s conversion.Scope) error {
|
||||
if err := Convert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(&in.Username, &out.Username, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(&in.Groups, &out.Groups, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_v1_ClaimOrExpression_To_apiserver_ClaimOrExpression(&in.UID, &out.UID, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Extra = *(*[]apiserver.ExtraMapping)(unsafe.Pointer(&in.Extra))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_ClaimMappings_To_apiserver_ClaimMappings is an autogenerated conversion function.
|
||||
func Convert_v1_ClaimMappings_To_apiserver_ClaimMappings(in *ClaimMappings, out *apiserver.ClaimMappings, s conversion.Scope) error {
|
||||
return autoConvert_v1_ClaimMappings_To_apiserver_ClaimMappings(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_ClaimMappings_To_v1_ClaimMappings(in *apiserver.ClaimMappings, out *ClaimMappings, s conversion.Scope) error {
|
||||
if err := Convert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression(&in.Username, &out.Username, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression(&in.Groups, &out.Groups, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_apiserver_ClaimOrExpression_To_v1_ClaimOrExpression(&in.UID, &out.UID, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Extra = *(*[]ExtraMapping)(unsafe.Pointer(&in.Extra))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_ClaimMappings_To_v1_ClaimMappings is an autogenerated conversion function.
|
||||
func Convert_apiserver_ClaimMappings_To_v1_ClaimMappings(in *apiserver.ClaimMappings, out *ClaimMappings, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_ClaimMappings_To_v1_ClaimMappings(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ClaimOrExpression_To_apiserver_ClaimOrExpression(in *ClaimOrExpression, out *apiserver.ClaimOrExpression, s conversion.Scope) error {
|
||||
out.Claim = in.Claim
|
||||
out.Expression = in.Expression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_ClaimOrExpression_To_apiserver_ClaimOrExpression is an autogenerated conversion function.
|
||||
func Convert_v1_ClaimOrExpression_To_apiserver_ClaimOrExpression(in *ClaimOrExpression, out *apiserver.ClaimOrExpression, s conversion.Scope) error {
|
||||
return autoConvert_v1_ClaimOrExpression_To_apiserver_ClaimOrExpression(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_ClaimOrExpression_To_v1_ClaimOrExpression(in *apiserver.ClaimOrExpression, out *ClaimOrExpression, s conversion.Scope) error {
|
||||
out.Claim = in.Claim
|
||||
out.Expression = in.Expression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_ClaimOrExpression_To_v1_ClaimOrExpression is an autogenerated conversion function.
|
||||
func Convert_apiserver_ClaimOrExpression_To_v1_ClaimOrExpression(in *apiserver.ClaimOrExpression, out *ClaimOrExpression, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_ClaimOrExpression_To_v1_ClaimOrExpression(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ClaimValidationRule_To_apiserver_ClaimValidationRule(in *ClaimValidationRule, out *apiserver.ClaimValidationRule, s conversion.Scope) error {
|
||||
out.Claim = in.Claim
|
||||
out.RequiredValue = in.RequiredValue
|
||||
out.Expression = in.Expression
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_ClaimValidationRule_To_apiserver_ClaimValidationRule is an autogenerated conversion function.
|
||||
func Convert_v1_ClaimValidationRule_To_apiserver_ClaimValidationRule(in *ClaimValidationRule, out *apiserver.ClaimValidationRule, s conversion.Scope) error {
|
||||
return autoConvert_v1_ClaimValidationRule_To_apiserver_ClaimValidationRule(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_ClaimValidationRule_To_v1_ClaimValidationRule(in *apiserver.ClaimValidationRule, out *ClaimValidationRule, s conversion.Scope) error {
|
||||
out.Claim = in.Claim
|
||||
out.RequiredValue = in.RequiredValue
|
||||
out.Expression = in.Expression
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_ClaimValidationRule_To_v1_ClaimValidationRule is an autogenerated conversion function.
|
||||
func Convert_apiserver_ClaimValidationRule_To_v1_ClaimValidationRule(in *apiserver.ClaimValidationRule, out *ClaimValidationRule, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_ClaimValidationRule_To_v1_ClaimValidationRule(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_EncryptionConfiguration_To_apiserver_EncryptionConfiguration(in *EncryptionConfiguration, out *apiserver.EncryptionConfiguration, s conversion.Scope) error {
|
||||
out.Resources = *(*[]apiserver.ResourceConfiguration)(unsafe.Pointer(&in.Resources))
|
||||
return nil
|
||||
|
@ -318,6 +634,28 @@ func Convert_apiserver_EncryptionConfiguration_To_v1_EncryptionConfiguration(in
|
|||
return autoConvert_apiserver_EncryptionConfiguration_To_v1_EncryptionConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ExtraMapping_To_apiserver_ExtraMapping(in *ExtraMapping, out *apiserver.ExtraMapping, s conversion.Scope) error {
|
||||
out.Key = in.Key
|
||||
out.ValueExpression = in.ValueExpression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_ExtraMapping_To_apiserver_ExtraMapping is an autogenerated conversion function.
|
||||
func Convert_v1_ExtraMapping_To_apiserver_ExtraMapping(in *ExtraMapping, out *apiserver.ExtraMapping, s conversion.Scope) error {
|
||||
return autoConvert_v1_ExtraMapping_To_apiserver_ExtraMapping(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_ExtraMapping_To_v1_ExtraMapping(in *apiserver.ExtraMapping, out *ExtraMapping, s conversion.Scope) error {
|
||||
out.Key = in.Key
|
||||
out.ValueExpression = in.ValueExpression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_ExtraMapping_To_v1_ExtraMapping is an autogenerated conversion function.
|
||||
func Convert_apiserver_ExtraMapping_To_v1_ExtraMapping(in *apiserver.ExtraMapping, out *ExtraMapping, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_ExtraMapping_To_v1_ExtraMapping(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_IdentityConfiguration_To_apiserver_IdentityConfiguration(in *IdentityConfiguration, out *apiserver.IdentityConfiguration, s conversion.Scope) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -336,6 +674,72 @@ func Convert_apiserver_IdentityConfiguration_To_v1_IdentityConfiguration(in *api
|
|||
return autoConvert_apiserver_IdentityConfiguration_To_v1_IdentityConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_Issuer_To_apiserver_Issuer(in *Issuer, out *apiserver.Issuer, s conversion.Scope) error {
|
||||
out.URL = in.URL
|
||||
if err := metav1.Convert_Pointer_string_To_string(&in.DiscoveryURL, &out.DiscoveryURL, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.CertificateAuthority = in.CertificateAuthority
|
||||
out.Audiences = *(*[]string)(unsafe.Pointer(&in.Audiences))
|
||||
out.AudienceMatchPolicy = apiserver.AudienceMatchPolicyType(in.AudienceMatchPolicy)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_Issuer_To_apiserver_Issuer is an autogenerated conversion function.
|
||||
func Convert_v1_Issuer_To_apiserver_Issuer(in *Issuer, out *apiserver.Issuer, s conversion.Scope) error {
|
||||
return autoConvert_v1_Issuer_To_apiserver_Issuer(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_Issuer_To_v1_Issuer(in *apiserver.Issuer, out *Issuer, s conversion.Scope) error {
|
||||
out.URL = in.URL
|
||||
if err := metav1.Convert_string_To_Pointer_string(&in.DiscoveryURL, &out.DiscoveryURL, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.CertificateAuthority = in.CertificateAuthority
|
||||
out.Audiences = *(*[]string)(unsafe.Pointer(&in.Audiences))
|
||||
out.AudienceMatchPolicy = AudienceMatchPolicyType(in.AudienceMatchPolicy)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_Issuer_To_v1_Issuer is an autogenerated conversion function.
|
||||
func Convert_apiserver_Issuer_To_v1_Issuer(in *apiserver.Issuer, out *Issuer, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_Issuer_To_v1_Issuer(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_JWTAuthenticator_To_apiserver_JWTAuthenticator(in *JWTAuthenticator, out *apiserver.JWTAuthenticator, s conversion.Scope) error {
|
||||
if err := Convert_v1_Issuer_To_apiserver_Issuer(&in.Issuer, &out.Issuer, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.ClaimValidationRules = *(*[]apiserver.ClaimValidationRule)(unsafe.Pointer(&in.ClaimValidationRules))
|
||||
if err := Convert_v1_ClaimMappings_To_apiserver_ClaimMappings(&in.ClaimMappings, &out.ClaimMappings, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UserValidationRules = *(*[]apiserver.UserValidationRule)(unsafe.Pointer(&in.UserValidationRules))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_JWTAuthenticator_To_apiserver_JWTAuthenticator is an autogenerated conversion function.
|
||||
func Convert_v1_JWTAuthenticator_To_apiserver_JWTAuthenticator(in *JWTAuthenticator, out *apiserver.JWTAuthenticator, s conversion.Scope) error {
|
||||
return autoConvert_v1_JWTAuthenticator_To_apiserver_JWTAuthenticator(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_JWTAuthenticator_To_v1_JWTAuthenticator(in *apiserver.JWTAuthenticator, out *JWTAuthenticator, s conversion.Scope) error {
|
||||
if err := Convert_apiserver_Issuer_To_v1_Issuer(&in.Issuer, &out.Issuer, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.ClaimValidationRules = *(*[]ClaimValidationRule)(unsafe.Pointer(&in.ClaimValidationRules))
|
||||
if err := Convert_apiserver_ClaimMappings_To_v1_ClaimMappings(&in.ClaimMappings, &out.ClaimMappings, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UserValidationRules = *(*[]UserValidationRule)(unsafe.Pointer(&in.UserValidationRules))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_JWTAuthenticator_To_v1_JWTAuthenticator is an autogenerated conversion function.
|
||||
func Convert_apiserver_JWTAuthenticator_To_v1_JWTAuthenticator(in *apiserver.JWTAuthenticator, out *JWTAuthenticator, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_JWTAuthenticator_To_v1_JWTAuthenticator(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_KMSConfiguration_To_apiserver_KMSConfiguration(in *KMSConfiguration, out *apiserver.KMSConfiguration, s conversion.Scope) error {
|
||||
out.APIVersion = in.APIVersion
|
||||
out.Name = in.Name
|
||||
|
@ -386,6 +790,30 @@ func Convert_apiserver_Key_To_v1_Key(in *apiserver.Key, out *Key, s conversion.S
|
|||
return autoConvert_apiserver_Key_To_v1_Key(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(in *PrefixedClaimOrExpression, out *apiserver.PrefixedClaimOrExpression, s conversion.Scope) error {
|
||||
out.Claim = in.Claim
|
||||
out.Prefix = (*string)(unsafe.Pointer(in.Prefix))
|
||||
out.Expression = in.Expression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression is an autogenerated conversion function.
|
||||
func Convert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(in *PrefixedClaimOrExpression, out *apiserver.PrefixedClaimOrExpression, s conversion.Scope) error {
|
||||
return autoConvert_v1_PrefixedClaimOrExpression_To_apiserver_PrefixedClaimOrExpression(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression(in *apiserver.PrefixedClaimOrExpression, out *PrefixedClaimOrExpression, s conversion.Scope) error {
|
||||
out.Claim = in.Claim
|
||||
out.Prefix = (*string)(unsafe.Pointer(in.Prefix))
|
||||
out.Expression = in.Expression
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression is an autogenerated conversion function.
|
||||
func Convert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression(in *apiserver.PrefixedClaimOrExpression, out *PrefixedClaimOrExpression, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_PrefixedClaimOrExpression_To_v1_PrefixedClaimOrExpression(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ProviderConfiguration_To_apiserver_ProviderConfiguration(in *ProviderConfiguration, out *apiserver.ProviderConfiguration, s conversion.Scope) error {
|
||||
out.AESGCM = (*apiserver.AESConfiguration)(unsafe.Pointer(in.AESGCM))
|
||||
out.AESCBC = (*apiserver.AESConfiguration)(unsafe.Pointer(in.AESCBC))
|
||||
|
@ -456,9 +884,37 @@ func Convert_apiserver_SecretboxConfiguration_To_v1_SecretboxConfiguration(in *a
|
|||
return autoConvert_apiserver_SecretboxConfiguration_To_v1_SecretboxConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_UserValidationRule_To_apiserver_UserValidationRule(in *UserValidationRule, out *apiserver.UserValidationRule, s conversion.Scope) error {
|
||||
out.Expression = in.Expression
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_UserValidationRule_To_apiserver_UserValidationRule is an autogenerated conversion function.
|
||||
func Convert_v1_UserValidationRule_To_apiserver_UserValidationRule(in *UserValidationRule, out *apiserver.UserValidationRule, s conversion.Scope) error {
|
||||
return autoConvert_v1_UserValidationRule_To_apiserver_UserValidationRule(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_UserValidationRule_To_v1_UserValidationRule(in *apiserver.UserValidationRule, out *UserValidationRule, s conversion.Scope) error {
|
||||
out.Expression = in.Expression
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_UserValidationRule_To_v1_UserValidationRule is an autogenerated conversion function.
|
||||
func Convert_apiserver_UserValidationRule_To_v1_UserValidationRule(in *apiserver.UserValidationRule, out *UserValidationRule, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_UserValidationRule_To_v1_UserValidationRule(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if err := metav1.Convert_Pointer_bool_To_bool(&in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if err := metav1.Convert_Pointer_bool_To_bool(&in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
|
||||
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
|
||||
|
@ -477,7 +933,13 @@ func Convert_v1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *Webho
|
|||
|
||||
func autoConvert_apiserver_WebhookConfiguration_To_v1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if err := metav1.Convert_bool_To_Pointer_bool(&in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if err := metav1.Convert_bool_To_Pointer_bool(&in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
|
||||
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
|
||||
|
|
|
@ -100,6 +100,80 @@ func (in *AdmissionPluginConfiguration) DeepCopy() *AdmissionPluginConfiguration
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnonymousAuthCondition) DeepCopyInto(out *AnonymousAuthCondition) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthCondition.
|
||||
func (in *AnonymousAuthCondition) DeepCopy() *AnonymousAuthCondition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AnonymousAuthCondition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnonymousAuthConfig) DeepCopyInto(out *AnonymousAuthConfig) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]AnonymousAuthCondition, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnonymousAuthConfig.
|
||||
func (in *AnonymousAuthConfig) DeepCopy() *AnonymousAuthConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AnonymousAuthConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.JWT != nil {
|
||||
in, out := &in.JWT, &out.JWT
|
||||
*out = make([]JWTAuthenticator, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Anonymous != nil {
|
||||
in, out := &in.Anonymous, &out.Anonymous
|
||||
*out = new(AnonymousAuthConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationConfiguration.
|
||||
func (in *AuthenticationConfiguration) DeepCopy() *AuthenticationConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AuthenticationConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *AuthenticationConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AuthorizationConfiguration) DeepCopyInto(out *AuthorizationConfiguration) {
|
||||
*out = *in
|
||||
|
@ -153,6 +227,62 @@ func (in *AuthorizerConfiguration) DeepCopy() *AuthorizerConfiguration {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClaimMappings) DeepCopyInto(out *ClaimMappings) {
|
||||
*out = *in
|
||||
in.Username.DeepCopyInto(&out.Username)
|
||||
in.Groups.DeepCopyInto(&out.Groups)
|
||||
out.UID = in.UID
|
||||
if in.Extra != nil {
|
||||
in, out := &in.Extra, &out.Extra
|
||||
*out = make([]ExtraMapping, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimMappings.
|
||||
func (in *ClaimMappings) DeepCopy() *ClaimMappings {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClaimMappings)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClaimOrExpression) DeepCopyInto(out *ClaimOrExpression) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimOrExpression.
|
||||
func (in *ClaimOrExpression) DeepCopy() *ClaimOrExpression {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClaimOrExpression)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClaimValidationRule) DeepCopyInto(out *ClaimValidationRule) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClaimValidationRule.
|
||||
func (in *ClaimValidationRule) DeepCopy() *ClaimValidationRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClaimValidationRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EncryptionConfiguration) DeepCopyInto(out *EncryptionConfiguration) {
|
||||
*out = *in
|
||||
|
@ -185,6 +315,22 @@ func (in *EncryptionConfiguration) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ExtraMapping) DeepCopyInto(out *ExtraMapping) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMapping.
|
||||
func (in *ExtraMapping) DeepCopy() *ExtraMapping {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ExtraMapping)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IdentityConfiguration) DeepCopyInto(out *IdentityConfiguration) {
|
||||
*out = *in
|
||||
|
@ -201,6 +347,60 @@ func (in *IdentityConfiguration) DeepCopy() *IdentityConfiguration {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Issuer) DeepCopyInto(out *Issuer) {
|
||||
*out = *in
|
||||
if in.DiscoveryURL != nil {
|
||||
in, out := &in.DiscoveryURL, &out.DiscoveryURL
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.Audiences != nil {
|
||||
in, out := &in.Audiences, &out.Audiences
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Issuer.
|
||||
func (in *Issuer) DeepCopy() *Issuer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Issuer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JWTAuthenticator) DeepCopyInto(out *JWTAuthenticator) {
|
||||
*out = *in
|
||||
in.Issuer.DeepCopyInto(&out.Issuer)
|
||||
if in.ClaimValidationRules != nil {
|
||||
in, out := &in.ClaimValidationRules, &out.ClaimValidationRules
|
||||
*out = make([]ClaimValidationRule, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.ClaimMappings.DeepCopyInto(&out.ClaimMappings)
|
||||
if in.UserValidationRules != nil {
|
||||
in, out := &in.UserValidationRules, &out.UserValidationRules
|
||||
*out = make([]UserValidationRule, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWTAuthenticator.
|
||||
func (in *JWTAuthenticator) DeepCopy() *JWTAuthenticator {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JWTAuthenticator)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KMSConfiguration) DeepCopyInto(out *KMSConfiguration) {
|
||||
*out = *in
|
||||
|
@ -243,6 +443,27 @@ func (in *Key) DeepCopy() *Key {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PrefixedClaimOrExpression) DeepCopyInto(out *PrefixedClaimOrExpression) {
|
||||
*out = *in
|
||||
if in.Prefix != nil {
|
||||
in, out := &in.Prefix, &out.Prefix
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrefixedClaimOrExpression.
|
||||
func (in *PrefixedClaimOrExpression) DeepCopy() *PrefixedClaimOrExpression {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PrefixedClaimOrExpression)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ProviderConfiguration) DeepCopyInto(out *ProviderConfiguration) {
|
||||
*out = *in
|
||||
|
@ -333,11 +554,37 @@ func (in *SecretboxConfiguration) DeepCopy() *SecretboxConfiguration {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *UserValidationRule) DeepCopyInto(out *UserValidationRule) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserValidationRule.
|
||||
func (in *UserValidationRule) DeepCopy() *UserValidationRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(UserValidationRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) {
|
||||
*out = *in
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if in.CacheAuthorizedRequests != nil {
|
||||
in, out := &in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if in.CacheUnauthorizedRequests != nil {
|
||||
in, out := &in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
in.ConnectionInfo.DeepCopyInto(&out.ConnectionInfo)
|
||||
if in.MatchConditions != nil {
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
|
@ -30,7 +31,13 @@ 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,4 +21,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/apis/apiserver/v1alpha1"
|
||||
package v1alpha1
|
||||
|
|
|
@ -352,7 +352,9 @@ type ClaimMappings struct {
|
|||
// If username.expression uses 'claims.email', then 'claims.email_verified' must be used in
|
||||
// username.expression or extra[*].valueExpression or claimValidationRules[*].expression.
|
||||
// An example claim validation rule expression that matches the validation automatically
|
||||
// applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true)'.
|
||||
// applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. 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.
|
||||
//
|
||||
// In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set,
|
||||
// the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly.
|
||||
|
@ -548,11 +550,23 @@ type WebhookConfiguration struct {
|
|||
// Same as setting `--authorization-webhook-cache-authorized-ttl` flag
|
||||
// Default: 5m0s
|
||||
AuthorizedTTL metav1.Duration `json:"authorizedTTL"`
|
||||
// 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
|
||||
// +optional
|
||||
CacheAuthorizedRequests *bool `json:"cacheAuthorizedRequests,omitempty"`
|
||||
// The duration to cache 'unauthorized' responses from the webhook
|
||||
// authorizer.
|
||||
// Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
|
||||
// Default: 30s
|
||||
UnauthorizedTTL metav1.Duration `json:"unauthorizedTTL"`
|
||||
// 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
|
||||
// +optional
|
||||
CacheUnauthorizedRequests *bool `json:"cacheUnauthorizedRequests,omitempty"`
|
||||
// Timeout for the webhook request
|
||||
// Maximum allowed value is 30s.
|
||||
// Required, no default value.
|
||||
|
|
|
@ -429,7 +429,17 @@ func Convert_apiserver_AuthenticationConfiguration_To_v1alpha1_AuthenticationCon
|
|||
}
|
||||
|
||||
func autoConvert_v1alpha1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]apiserver.AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
if in.Authorizers != nil {
|
||||
in, out := &in.Authorizers, &out.Authorizers
|
||||
*out = make([]apiserver.AuthorizerConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Authorizers = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -439,7 +449,17 @@ func Convert_v1alpha1_AuthorizationConfiguration_To_apiserver_AuthorizationConfi
|
|||
}
|
||||
|
||||
func autoConvert_apiserver_AuthorizationConfiguration_To_v1alpha1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
if in.Authorizers != nil {
|
||||
in, out := &in.Authorizers, &out.Authorizers
|
||||
*out = make([]AuthorizerConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_apiserver_AuthorizerConfiguration_To_v1alpha1_AuthorizerConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Authorizers = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -451,7 +471,15 @@ func Convert_apiserver_AuthorizationConfiguration_To_v1alpha1_AuthorizationConfi
|
|||
func autoConvert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = apiserver.AuthorizerType(in.Type)
|
||||
out.Name = in.Name
|
||||
out.Webhook = (*apiserver.WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
if in.Webhook != nil {
|
||||
in, out := &in.Webhook, &out.Webhook
|
||||
*out = new(apiserver.WebhookConfiguration)
|
||||
if err := Convert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Webhook = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -463,7 +491,15 @@ func Convert_v1alpha1_AuthorizerConfiguration_To_apiserver_AuthorizerConfigurati
|
|||
func autoConvert_apiserver_AuthorizerConfiguration_To_v1alpha1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = string(in.Type)
|
||||
out.Name = in.Name
|
||||
out.Webhook = (*WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
if in.Webhook != nil {
|
||||
in, out := &in.Webhook, &out.Webhook
|
||||
*out = new(WebhookConfiguration)
|
||||
if err := Convert_apiserver_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Webhook = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -885,7 +921,13 @@ func Convert_apiserver_UserValidationRule_To_v1alpha1_UserValidationRule(in *api
|
|||
|
||||
func autoConvert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
|
||||
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
|
||||
|
@ -904,7 +946,13 @@ func Convert_v1alpha1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in
|
|||
|
||||
func autoConvert_apiserver_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
|
||||
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
|
||||
|
|
|
@ -547,7 +547,17 @@ func (in *UserValidationRule) DeepCopy() *UserValidationRule {
|
|||
func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) {
|
||||
*out = *in
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if in.CacheAuthorizedRequests != nil {
|
||||
in, out := &in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if in.CacheUnauthorizedRequests != nil {
|
||||
in, out := &in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
in.ConnectionInfo.DeepCopyInto(&out.ConnectionInfo)
|
||||
if in.MatchConditions != nil {
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
|
@ -30,7 +31,13 @@ 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,4 @@ limitations under the License.
|
|||
// +groupName=apiserver.k8s.io
|
||||
|
||||
// Package v1beta1 is the v1beta1 version of the API.
|
||||
package v1beta1 // import "k8s.io/apiserver/pkg/apis/apiserver/v1beta1"
|
||||
package v1beta1
|
||||
|
|
|
@ -323,7 +323,9 @@ type ClaimMappings struct {
|
|||
// If username.expression uses 'claims.email', then 'claims.email_verified' must be used in
|
||||
// username.expression or extra[*].valueExpression or claimValidationRules[*].expression.
|
||||
// An example claim validation rule expression that matches the validation automatically
|
||||
// applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true)'.
|
||||
// applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true) == true'. 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.
|
||||
//
|
||||
// In the flag based approach, the --oidc-username-claim and --oidc-username-prefix are optional. If --oidc-username-claim is not set,
|
||||
// the default value is "sub". For the authentication config, there is no defaulting for claim or prefix. The claim and prefix must be set explicitly.
|
||||
|
@ -519,11 +521,23 @@ type WebhookConfiguration struct {
|
|||
// Same as setting `--authorization-webhook-cache-authorized-ttl` flag
|
||||
// Default: 5m0s
|
||||
AuthorizedTTL metav1.Duration `json:"authorizedTTL"`
|
||||
// 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
|
||||
// +optional
|
||||
CacheAuthorizedRequests *bool `json:"cacheAuthorizedRequests,omitempty"`
|
||||
// The duration to cache 'unauthorized' responses from the webhook
|
||||
// authorizer.
|
||||
// Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
|
||||
// Default: 30s
|
||||
UnauthorizedTTL metav1.Duration `json:"unauthorizedTTL"`
|
||||
// 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
|
||||
// +optional
|
||||
CacheUnauthorizedRequests *bool `json:"cacheUnauthorizedRequests,omitempty"`
|
||||
// Timeout for the webhook request
|
||||
// Maximum allowed value is 30s.
|
||||
// Required, no default value.
|
||||
|
|
|
@ -365,7 +365,17 @@ func Convert_apiserver_AuthenticationConfiguration_To_v1beta1_AuthenticationConf
|
|||
}
|
||||
|
||||
func autoConvert_v1beta1_AuthorizationConfiguration_To_apiserver_AuthorizationConfiguration(in *AuthorizationConfiguration, out *apiserver.AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]apiserver.AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
if in.Authorizers != nil {
|
||||
in, out := &in.Authorizers, &out.Authorizers
|
||||
*out = make([]apiserver.AuthorizerConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_v1beta1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Authorizers = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -375,7 +385,17 @@ func Convert_v1beta1_AuthorizationConfiguration_To_apiserver_AuthorizationConfig
|
|||
}
|
||||
|
||||
func autoConvert_apiserver_AuthorizationConfiguration_To_v1beta1_AuthorizationConfiguration(in *apiserver.AuthorizationConfiguration, out *AuthorizationConfiguration, s conversion.Scope) error {
|
||||
out.Authorizers = *(*[]AuthorizerConfiguration)(unsafe.Pointer(&in.Authorizers))
|
||||
if in.Authorizers != nil {
|
||||
in, out := &in.Authorizers, &out.Authorizers
|
||||
*out = make([]AuthorizerConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
if err := Convert_apiserver_AuthorizerConfiguration_To_v1beta1_AuthorizerConfiguration(&(*in)[i], &(*out)[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Authorizers = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -387,7 +407,15 @@ func Convert_apiserver_AuthorizationConfiguration_To_v1beta1_AuthorizationConfig
|
|||
func autoConvert_v1beta1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguration(in *AuthorizerConfiguration, out *apiserver.AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = apiserver.AuthorizerType(in.Type)
|
||||
out.Name = in.Name
|
||||
out.Webhook = (*apiserver.WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
if in.Webhook != nil {
|
||||
in, out := &in.Webhook, &out.Webhook
|
||||
*out = new(apiserver.WebhookConfiguration)
|
||||
if err := Convert_v1beta1_WebhookConfiguration_To_apiserver_WebhookConfiguration(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Webhook = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -399,7 +427,15 @@ func Convert_v1beta1_AuthorizerConfiguration_To_apiserver_AuthorizerConfiguratio
|
|||
func autoConvert_apiserver_AuthorizerConfiguration_To_v1beta1_AuthorizerConfiguration(in *apiserver.AuthorizerConfiguration, out *AuthorizerConfiguration, s conversion.Scope) error {
|
||||
out.Type = string(in.Type)
|
||||
out.Name = in.Name
|
||||
out.Webhook = (*WebhookConfiguration)(unsafe.Pointer(in.Webhook))
|
||||
if in.Webhook != nil {
|
||||
in, out := &in.Webhook, &out.Webhook
|
||||
*out = new(WebhookConfiguration)
|
||||
if err := Convert_apiserver_WebhookConfiguration_To_v1beta1_WebhookConfiguration(*in, *out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Webhook = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -821,7 +857,13 @@ func Convert_apiserver_UserValidationRule_To_v1beta1_UserValidationRule(in *apis
|
|||
|
||||
func autoConvert_v1beta1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *WebhookConfiguration, out *apiserver.WebhookConfiguration, s conversion.Scope) error {
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
|
||||
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
|
||||
|
@ -840,7 +882,13 @@ func Convert_v1beta1_WebhookConfiguration_To_apiserver_WebhookConfiguration(in *
|
|||
|
||||
func autoConvert_apiserver_WebhookConfiguration_To_v1beta1_WebhookConfiguration(in *apiserver.WebhookConfiguration, out *WebhookConfiguration, s conversion.Scope) error {
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
out.SubjectAccessReviewVersion = in.SubjectAccessReviewVersion
|
||||
out.MatchConditionSubjectAccessReviewVersion = in.MatchConditionSubjectAccessReviewVersion
|
||||
|
|
|
@ -494,7 +494,17 @@ func (in *UserValidationRule) DeepCopy() *UserValidationRule {
|
|||
func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) {
|
||||
*out = *in
|
||||
out.AuthorizedTTL = in.AuthorizedTTL
|
||||
if in.CacheAuthorizedRequests != nil {
|
||||
in, out := &in.CacheAuthorizedRequests, &out.CacheAuthorizedRequests
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.UnauthorizedTTL = in.UnauthorizedTTL
|
||||
if in.CacheUnauthorizedRequests != nil {
|
||||
in, out := &in.CacheUnauthorizedRequests, &out.CacheUnauthorizedRequests
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
out.Timeout = in.Timeout
|
||||
in.ConnectionInfo.DeepCopyInto(&out.ConnectionInfo)
|
||||
if in.MatchConditions != nil {
|
||||
|
|
|
@ -128,7 +128,7 @@ func validateIssuer(issuer api.Issuer, disallowedIssuers sets.Set[string], fldPa
|
|||
|
||||
func validateIssuerURL(issuerURL string, disallowedIssuers sets.Set[string], fldPath *field.Path) field.ErrorList {
|
||||
if len(issuerURL) == 0 {
|
||||
return field.ErrorList{field.Required(fldPath, "URL is required")}
|
||||
return field.ErrorList{field.Required(fldPath, "")}
|
||||
}
|
||||
|
||||
return validateURL(issuerURL, disallowedIssuers, fldPath)
|
||||
|
@ -198,7 +198,7 @@ func validateAudiences(audiences []string, audienceMatchPolicy api.AudienceMatch
|
|||
for i, audience := range audiences {
|
||||
fldPath := fldPath.Index(i)
|
||||
if len(audience) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "audience can't be empty"))
|
||||
allErrs = append(allErrs, field.Required(fldPath, ""))
|
||||
}
|
||||
if seenAudiences.Has(audience) {
|
||||
allErrs = append(allErrs, field.Duplicate(fldPath, audience))
|
||||
|
@ -364,7 +364,7 @@ func validateClaimMappings(compiler authenticationcel.Compiler, state *validatio
|
|||
seenExtraKeys.Insert(mapping.Key)
|
||||
|
||||
if len(mapping.ValueExpression) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("valueExpression"), "valueExpression is required"))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("valueExpression"), ""))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -564,7 +564,7 @@ func validateUserValidationRules(compiler authenticationcel.Compiler, state *val
|
|||
fldPath := fldPath.Index(i)
|
||||
|
||||
if len(rule.Expression) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("expression"), "expression is required"))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("expression"), ""))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -35,16 +35,11 @@ import (
|
|||
api "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
authenticationcel "k8s.io/apiserver/pkg/authentication/cel"
|
||||
authorizationcel "k8s.io/apiserver/pkg/authorization/cel"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/utils/pointer"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func TestValidateAuthenticationConfiguration(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StructuredAuthenticationConfiguration, true)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
in *api.AuthenticationConfiguration
|
||||
|
@ -74,7 +69,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -92,7 +87,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -119,7 +114,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -138,7 +133,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -158,7 +153,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -188,7 +183,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -213,7 +208,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
},
|
||||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -239,7 +234,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
UserValidationRules: []api.UserValidationRule{
|
||||
|
@ -269,7 +264,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -585,6 +580,61 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "valid authentication configuration that uses verified email via claim validation rule",
|
||||
in: &api.AuthenticationConfiguration{
|
||||
JWT: []api.JWTAuthenticator{
|
||||
{
|
||||
Issuer: api.Issuer{
|
||||
URL: "https://issuer-url",
|
||||
Audiences: []string{"audience"},
|
||||
},
|
||||
ClaimValidationRules: []api.ClaimValidationRule{
|
||||
{
|
||||
// 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.
|
||||
Expression: `claims.?email_verified.orValue(true) == true`,
|
||||
},
|
||||
},
|
||||
// allow email claim only when email_verified is present and true
|
||||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Expression: `{claims.?email: "panda"}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "valid authentication configuration that uses verified email via claim validation rule incorrectly",
|
||||
in: &api.AuthenticationConfiguration{
|
||||
JWT: []api.JWTAuthenticator{
|
||||
{
|
||||
Issuer: api.Issuer{
|
||||
URL: "https://issuer-url",
|
||||
Audiences: []string{"audience"},
|
||||
},
|
||||
ClaimValidationRules: []api.ClaimValidationRule{
|
||||
{
|
||||
// This expression was previously documented in the godoc for the JWT authenticator
|
||||
// and was incorrect. It was changed to the above expression in the previous test case.
|
||||
// Testing the old expression here to confirm it fails validation.
|
||||
Expression: `claims.?email_verified.orValue(true)`,
|
||||
},
|
||||
},
|
||||
// allow email claim only when email_verified is present and true
|
||||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Expression: `{claims.?email: "panda"}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `[jwt[0].claimValidationRules[0].expression: Invalid value: "claims.?email_verified.orValue(true)": must evaluate to bool, jwt[0].claimMappings.username.expression: Invalid value: "{claims.?email: \"panda\"}": claims.email_verified must be used in claimMappings.username.expression or claimMappings.extra[*].valueExpression or claimValidationRules[*].expression when claims.email is used in claimMappings.username.expression]`,
|
||||
},
|
||||
{
|
||||
name: "valid authentication configuration",
|
||||
in: &api.AuthenticationConfiguration{
|
||||
|
@ -603,7 +653,7 @@ func TestValidateAuthenticationConfiguration(t *testing.T) {
|
|||
ClaimMappings: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "sub",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -635,7 +685,7 @@ func TestValidateIssuerURL(t *testing.T) {
|
|||
{
|
||||
name: "url is empty",
|
||||
in: "",
|
||||
want: "issuer.url: Required value: URL is required",
|
||||
want: "issuer.url: Required value",
|
||||
},
|
||||
{
|
||||
name: "url parse error",
|
||||
|
@ -807,7 +857,7 @@ func TestValidateAudiences(t *testing.T) {
|
|||
{
|
||||
name: "audience is empty",
|
||||
in: []string{""},
|
||||
want: "issuer.audiences[0]: Required value: audience can't be empty",
|
||||
want: "issuer.audiences[0]: Required value",
|
||||
},
|
||||
{
|
||||
name: "invalid match policy with single audience",
|
||||
|
@ -850,7 +900,7 @@ func TestValidateAudiences(t *testing.T) {
|
|||
name: "multiple audiences set when structured authn feature is disabled",
|
||||
in: []string{"audience1", "audience2"},
|
||||
matchPolicy: "MatchAny",
|
||||
want: `issuer.audiences: Invalid value: []string{"audience1", "audience2"}: multiple audiences are not supported when StructuredAuthenticationConfiguration feature gate is disabled`,
|
||||
want: `issuer.audiences: Invalid value: ["audience1","audience2"]: multiple audiences are not supported when StructuredAuthenticationConfiguration feature gate is disabled`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1087,7 +1137,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Expression: "claims.username",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
structuredAuthnFeatureEnabled: true,
|
||||
|
@ -1120,7 +1170,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Groups: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
|
@ -1135,11 +1185,11 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Groups: api.PrefixedClaimOrExpression{
|
||||
Expression: "claims.groups",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
},
|
||||
structuredAuthnFeatureEnabled: true,
|
||||
|
@ -1150,7 +1200,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Groups: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
|
@ -1164,7 +1214,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Groups: api.PrefixedClaimOrExpression{
|
||||
Expression: "foo.bar",
|
||||
|
@ -1180,7 +1230,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
UID: api.ClaimOrExpression{
|
||||
Claim: "claim",
|
||||
|
@ -1195,7 +1245,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
UID: api.ClaimOrExpression{
|
||||
Expression: "foo.bar",
|
||||
|
@ -1211,7 +1261,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Extra: []api.ExtraMapping{
|
||||
{Key: "", ValueExpression: "claims.extra"},
|
||||
|
@ -1225,21 +1275,21 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Extra: []api.ExtraMapping{
|
||||
{Key: "example.org/foo", ValueExpression: ""},
|
||||
},
|
||||
},
|
||||
structuredAuthnFeatureEnabled: true,
|
||||
want: `issuer.claimMappings.extra[0].valueExpression: Required value: valueExpression is required`,
|
||||
want: `issuer.claimMappings.extra[0].valueExpression: Required value`,
|
||||
},
|
||||
{
|
||||
name: "extra mapping value expression is invalid",
|
||||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Extra: []api.ExtraMapping{
|
||||
{Key: "example.org/foo", ValueExpression: "foo.bar"},
|
||||
|
@ -1267,7 +1317,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Groups: api.PrefixedClaimOrExpression{
|
||||
Expression: "foo.bar",
|
||||
|
@ -1283,7 +1333,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
UID: api.ClaimOrExpression{
|
||||
Expression: "foo.bar",
|
||||
|
@ -1299,7 +1349,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
UID: api.ClaimOrExpression{
|
||||
Claim: "claim",
|
||||
|
@ -1313,7 +1363,7 @@ func TestValidateClaimMappings(t *testing.T) {
|
|||
in: api.ClaimMappings{
|
||||
Username: api.PrefixedClaimOrExpression{
|
||||
Claim: "claim",
|
||||
Prefix: pointer.String("prefix"),
|
||||
Prefix: ptr.To("prefix"),
|
||||
},
|
||||
Extra: []api.ExtraMapping{
|
||||
{Key: "example.org/foo", ValueExpression: "claims.extra"},
|
||||
|
@ -1605,7 +1655,7 @@ func TestValidateUserValidationRules(t *testing.T) {
|
|||
name: "user info validation rule, expression is empty",
|
||||
in: []api.UserValidationRule{{}},
|
||||
structuredAuthnFeatureEnabled: true,
|
||||
want: "issuer.userValidationRules[0].expression: Required value: expression is required",
|
||||
want: "issuer.userValidationRules[0].expression: Required value",
|
||||
},
|
||||
{
|
||||
name: "duplicate expression",
|
||||
|
|
|
@ -17,4 +17,4 @@ limitations under the License.
|
|||
// +k8s:deepcopy-gen=package
|
||||
// +groupName=audit.k8s.io
|
||||
|
||||
package audit // import "k8s.io/apiserver/pkg/apis/audit"
|
||||
package audit
|
||||
|
|
|
@ -19,7 +19,7 @@ package fuzzer
|
|||
import (
|
||||
"strings"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
"sigs.k8s.io/randfill"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
|
@ -29,9 +29,9 @@ import (
|
|||
// Funcs returns the fuzzer functions for the audit api group.
|
||||
func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(e *audit.Event, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(e)
|
||||
switch c.RandBool() {
|
||||
func(e *audit.Event, c randfill.Continue) {
|
||||
c.FillNoCustom(e)
|
||||
switch c.Bool() {
|
||||
case true:
|
||||
e.RequestObject = nil
|
||||
case false:
|
||||
|
@ -41,7 +41,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
ContentType: runtime.ContentTypeJSON,
|
||||
}
|
||||
}
|
||||
switch c.RandBool() {
|
||||
switch c.Bool() {
|
||||
case true:
|
||||
e.ResponseObject = nil
|
||||
case false:
|
||||
|
@ -52,8 +52,8 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
}
|
||||
}
|
||||
},
|
||||
func(o *audit.ObjectReference, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(o)
|
||||
func(o *audit.ObjectReference, c randfill.Continue) {
|
||||
c.FillNoCustom(o)
|
||||
switch c.Intn(3) {
|
||||
case 0:
|
||||
// core api group
|
||||
|
|
|
@ -22,4 +22,4 @@ limitations under the License.
|
|||
|
||||
// +groupName=audit.k8s.io
|
||||
|
||||
package v1 // import "k8s.io/apiserver/pkg/apis/audit/v1"
|
||||
package v1
|
||||
|
|
|
@ -19,4 +19,4 @@ limitations under the License.
|
|||
|
||||
// package example contains an example API used to demonstrate how to create api groups. Moreover, this is
|
||||
// used within tests.
|
||||
package example // import "k8s.io/apiserver/pkg/apis/example"
|
||||
package example
|
||||
|
|
|
@ -19,7 +19,7 @@ package fuzzer
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
"sigs.k8s.io/randfill"
|
||||
|
||||
apitesting "k8s.io/apimachinery/pkg/api/apitesting"
|
||||
"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
|
||||
|
@ -33,9 +33,9 @@ import (
|
|||
// values in a Kubernetes context.
|
||||
func overrideMetaFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(j *runtime.Object, c fuzz.Continue) {
|
||||
func(j *runtime.Object, c randfill.Continue) {
|
||||
// TODO: uncomment when round trip starts from a versioned object
|
||||
if true { //c.RandBool() {
|
||||
if true { // c.Bool() {
|
||||
*j = &runtime.Unknown{
|
||||
// We do not set TypeMeta here because it is not carried through a round trip
|
||||
Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`),
|
||||
|
@ -44,15 +44,15 @@ func overrideMetaFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
} else {
|
||||
types := []runtime.Object{&example.Pod{}}
|
||||
t := types[c.Rand.Intn(len(types))]
|
||||
c.Fuzz(t)
|
||||
c.Fill(t)
|
||||
*j = t
|
||||
}
|
||||
},
|
||||
func(r *runtime.RawExtension, c fuzz.Continue) {
|
||||
func(r *runtime.RawExtension, c randfill.Continue) {
|
||||
// Pick an arbitrary type and fuzz it
|
||||
types := []runtime.Object{&example.Pod{}}
|
||||
obj := types[c.Rand.Intn(len(types))]
|
||||
c.Fuzz(obj)
|
||||
c.Fill(obj)
|
||||
|
||||
// Convert the object to raw bytes
|
||||
bytes, err := runtime.Encode(apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion), obj)
|
||||
|
@ -68,11 +68,11 @@ func overrideMetaFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
|
||||
func exampleFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(s *example.PodSpec, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(s)
|
||||
func(s *example.PodSpec, c randfill.Continue) {
|
||||
c.FillNoCustom(s)
|
||||
// has a default value
|
||||
ttl := int64(30)
|
||||
if c.RandBool() {
|
||||
if c.Bool() {
|
||||
ttl = int64(c.Uint32())
|
||||
}
|
||||
s.TerminationGracePeriodSeconds = &ttl
|
||||
|
@ -81,11 +81,11 @@ func exampleFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
s.SchedulerName = "default-scheduler"
|
||||
}
|
||||
},
|
||||
func(j *example.PodPhase, c fuzz.Continue) {
|
||||
func(j *example.PodPhase, c randfill.Continue) {
|
||||
statuses := []example.PodPhase{"Pending", "Running", "Succeeded", "Failed", "Unknown"}
|
||||
*j = statuses[c.Rand.Intn(len(statuses))]
|
||||
},
|
||||
func(rp *example.RestartPolicy, c fuzz.Continue) {
|
||||
func(rp *example.RestartPolicy, c randfill.Continue) {
|
||||
policies := []example.RestartPolicy{"Always", "Never", "OnFailure"}
|
||||
*rp = policies[c.Rand.Intn(len(policies))]
|
||||
},
|
||||
|
|
|
@ -22,4 +22,4 @@ limitations under the License.
|
|||
|
||||
// +groupName=example.apiserver.k8s.io
|
||||
|
||||
package v1 // import "k8s.io/apiserver/pkg/apis/example/v1"
|
||||
package v1
|
||||
|
|
|
@ -138,7 +138,6 @@ message PodSpec {
|
|||
optional string nodeName = 10;
|
||||
|
||||
// Host networking requested for this pod. Use the host's network namespace.
|
||||
// If this option is set, the ports that will be used must be specified.
|
||||
// Default to false.
|
||||
// +k8s:conversion-gen=false
|
||||
// +optional
|
||||
|
|
|
@ -151,7 +151,6 @@ type PodSpec struct {
|
|||
// +optional
|
||||
NodeName string `json:"nodeName,omitempty" protobuf:"bytes,10,opt,name=nodeName"`
|
||||
// Host networking requested for this pod. Use the host's network namespace.
|
||||
// If this option is set, the ports that will be used must be specified.
|
||||
// Default to false.
|
||||
// +k8s:conversion-gen=false
|
||||
// +optional
|
||||
|
|
|
@ -21,4 +21,4 @@ limitations under the License.
|
|||
// another group ("example"). This happens if a type is moved to a different
|
||||
// group. It's not recommended to move types across groups, though Kubernetes
|
||||
// have a few cases due to historical reasons. This package is for tests.
|
||||
package example2 // import "k8s.io/apiserver/pkg/apis/example2"
|
||||
package example2
|
||||
|
|
|
@ -23,4 +23,4 @@ limitations under the License.
|
|||
|
||||
// +groupName=example2.apiserver.k8s.io
|
||||
|
||||
package v1 // import "k8s.io/apiserver/pkg/apis/example2/v1"
|
||||
package v1
|
||||
|
|
|
@ -73,8 +73,6 @@ var (
|
|||
SuggestedFlowSchemaSystemNodeHigh, // references "node-high" priority-level
|
||||
SuggestedFlowSchemaProbes, // references "exempt" priority-level
|
||||
SuggestedFlowSchemaSystemLeaderElection, // references "leader-election" priority-level
|
||||
SuggestedFlowSchemaWorkloadLeaderElection, // references "leader-election" priority-level
|
||||
SuggestedFlowSchemaEndpointsController, // references "workload-high" priority-level
|
||||
SuggestedFlowSchemaKubeControllerManager, // references "workload-high" priority-level
|
||||
SuggestedFlowSchemaKubeScheduler, // references "workload-high" priority-level
|
||||
SuggestedFlowSchemaKubeSystemServiceAccounts, // references "workload-high" priority-level
|
||||
|
@ -310,52 +308,6 @@ var (
|
|||
},
|
||||
},
|
||||
)
|
||||
// We add an explicit rule for endpoint-controller with high precedence
|
||||
// to ensure that those calls won't get caught by the following
|
||||
// <workload-leader-election> flow-schema.
|
||||
//
|
||||
// TODO(#80289): Get rid of this rule once we get rid of support for
|
||||
// using endpoints and configmaps objects for leader election.
|
||||
SuggestedFlowSchemaEndpointsController = newFlowSchema(
|
||||
"endpoint-controller", "workload-high", 150,
|
||||
flowcontrol.FlowDistinguisherMethodByUserType,
|
||||
flowcontrol.PolicyRulesWithSubjects{
|
||||
Subjects: append(
|
||||
users(user.KubeControllerManager),
|
||||
kubeSystemServiceAccount("endpoint-controller", "endpointslicemirroring-controller")...),
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
resourceRule(
|
||||
[]string{"get", "create", "update"},
|
||||
[]string{corev1.GroupName},
|
||||
[]string{"endpoints"},
|
||||
[]string{flowcontrol.NamespaceEvery},
|
||||
false),
|
||||
},
|
||||
},
|
||||
)
|
||||
// TODO(#80289): Get rid of this rule once we get rid of support for
|
||||
// using endpoints and configmaps objects for leader election.
|
||||
SuggestedFlowSchemaWorkloadLeaderElection = newFlowSchema(
|
||||
"workload-leader-election", "leader-election", 200,
|
||||
flowcontrol.FlowDistinguisherMethodByUserType,
|
||||
flowcontrol.PolicyRulesWithSubjects{
|
||||
Subjects: kubeSystemServiceAccount(flowcontrol.NameAll),
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
resourceRule(
|
||||
[]string{"get", "create", "update"},
|
||||
[]string{corev1.GroupName},
|
||||
[]string{"endpoints", "configmaps"},
|
||||
[]string{flowcontrol.NamespaceEvery},
|
||||
false),
|
||||
resourceRule(
|
||||
[]string{"get", "create", "update"},
|
||||
[]string{coordinationv1.GroupName},
|
||||
[]string{"leases"},
|
||||
[]string{flowcontrol.NamespaceEvery},
|
||||
false),
|
||||
},
|
||||
},
|
||||
)
|
||||
SuggestedFlowSchemaSystemNodeHigh = newFlowSchema(
|
||||
"system-node-high", "node-high", 400,
|
||||
flowcontrol.FlowDistinguisherMethodByUserType,
|
||||
|
|
|
@ -18,10 +18,18 @@ package audit
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"maps"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
authnv1 "k8s.io/api/authentication/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
@ -35,22 +43,223 @@ const auditKey key = iota
|
|||
|
||||
// AuditContext holds the information for constructing the audit events for the current request.
|
||||
type AuditContext struct {
|
||||
// RequestAuditConfig is the audit configuration that applies to the request
|
||||
RequestAuditConfig RequestAuditConfig
|
||||
// initialized indicates whether requestAuditConfig and sink have been populated and are safe to read unguarded.
|
||||
// This should only be set via Init().
|
||||
initialized atomic.Bool
|
||||
// requestAuditConfig is the audit configuration that applies to the request.
|
||||
// This should only be written via Init(RequestAuditConfig, Sink), and only read when initialized.Load() is true.
|
||||
requestAuditConfig RequestAuditConfig
|
||||
// sink is the sink to use when processing event stages.
|
||||
// This should only be written via Init(RequestAuditConfig, Sink), and only read when initialized.Load() is true.
|
||||
sink Sink
|
||||
|
||||
// Event is the audit Event object that is being captured to be written in
|
||||
// lock guards event
|
||||
lock sync.Mutex
|
||||
|
||||
// event is the audit Event object that is being captured to be written in
|
||||
// the API audit log.
|
||||
Event auditinternal.Event
|
||||
event auditinternal.Event
|
||||
|
||||
// annotationMutex guards event.Annotations
|
||||
annotationMutex sync.Mutex
|
||||
// unguarded copy of auditID from the event
|
||||
auditID atomic.Value
|
||||
}
|
||||
|
||||
// Enabled checks whether auditing is enabled for this audit context.
|
||||
func (ac *AuditContext) Enabled() bool {
|
||||
// Note: An unset Level should be considered Enabled, so that request data (e.g. annotations)
|
||||
// can still be captured before the audit policy is evaluated.
|
||||
return ac != nil && ac.RequestAuditConfig.Level != auditinternal.LevelNone
|
||||
if ac == nil {
|
||||
// protect against nil pointers
|
||||
return false
|
||||
}
|
||||
if !ac.initialized.Load() {
|
||||
// Note: An unset Level should be considered Enabled, so that request data (e.g. annotations)
|
||||
// can still be captured before the audit policy is evaluated.
|
||||
return true
|
||||
}
|
||||
return ac.requestAuditConfig.Level != auditinternal.LevelNone
|
||||
}
|
||||
|
||||
func (ac *AuditContext) Init(requestAuditConfig RequestAuditConfig, sink Sink) error {
|
||||
ac.lock.Lock()
|
||||
defer ac.lock.Unlock()
|
||||
if ac.initialized.Load() {
|
||||
return errors.New("audit context was already initialized")
|
||||
}
|
||||
ac.requestAuditConfig = requestAuditConfig
|
||||
ac.sink = sink
|
||||
ac.event.Level = requestAuditConfig.Level
|
||||
ac.initialized.Store(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac *AuditContext) AuditID() types.UID {
|
||||
// return the unguarded copy of the auditID
|
||||
id, _ := ac.auditID.Load().(types.UID)
|
||||
return id
|
||||
}
|
||||
|
||||
func (ac *AuditContext) visitEvent(f func(event *auditinternal.Event)) {
|
||||
ac.lock.Lock()
|
||||
defer ac.lock.Unlock()
|
||||
f(&ac.event)
|
||||
}
|
||||
|
||||
// ProcessEventStage returns true on success, false if there was an error processing the stage.
|
||||
func (ac *AuditContext) ProcessEventStage(ctx context.Context, stage auditinternal.Stage) bool {
|
||||
if ac == nil || !ac.initialized.Load() {
|
||||
return true
|
||||
}
|
||||
if ac.sink == nil {
|
||||
return true
|
||||
}
|
||||
for _, omitStage := range ac.requestAuditConfig.OmitStages {
|
||||
if stage == omitStage {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
processed := false
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
event.Stage = stage
|
||||
if stage == auditinternal.StageRequestReceived {
|
||||
event.StageTimestamp = event.RequestReceivedTimestamp
|
||||
} else {
|
||||
event.StageTimestamp = metav1.NewMicroTime(time.Now())
|
||||
}
|
||||
|
||||
ObserveEvent(ctx)
|
||||
processed = ac.sink.ProcessEvents(event)
|
||||
})
|
||||
return processed
|
||||
}
|
||||
|
||||
func (ac *AuditContext) LogImpersonatedUser(user user.Info) {
|
||||
ac.visitEvent(func(ev *auditinternal.Event) {
|
||||
if ev == nil || ev.Level.Less(auditinternal.LevelMetadata) {
|
||||
return
|
||||
}
|
||||
ev.ImpersonatedUser = &authnv1.UserInfo{
|
||||
Username: user.GetName(),
|
||||
}
|
||||
ev.ImpersonatedUser.Groups = user.GetGroups()
|
||||
ev.ImpersonatedUser.UID = user.GetUID()
|
||||
ev.ImpersonatedUser.Extra = map[string]authnv1.ExtraValue{}
|
||||
for k, v := range user.GetExtra() {
|
||||
ev.ImpersonatedUser.Extra[k] = authnv1.ExtraValue(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (ac *AuditContext) LogResponseObject(status *metav1.Status, obj *runtime.Unknown) {
|
||||
ac.visitEvent(func(ae *auditinternal.Event) {
|
||||
if status != nil {
|
||||
// selectively copy the bounded fields.
|
||||
ae.ResponseStatus = &metav1.Status{
|
||||
Status: status.Status,
|
||||
Message: status.Message,
|
||||
Reason: status.Reason,
|
||||
Details: status.Details,
|
||||
Code: status.Code,
|
||||
}
|
||||
}
|
||||
if ae.Level.Less(auditinternal.LevelRequestResponse) {
|
||||
return
|
||||
}
|
||||
ae.ResponseObject = obj
|
||||
})
|
||||
}
|
||||
|
||||
// LogRequestPatch fills in the given patch as the request object into an audit event.
|
||||
func (ac *AuditContext) LogRequestPatch(patch []byte) {
|
||||
ac.visitEvent(func(ae *auditinternal.Event) {
|
||||
ae.RequestObject = &runtime.Unknown{
|
||||
Raw: patch,
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventAnnotation(key string) (string, bool) {
|
||||
var val string
|
||||
var ok bool
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
val, ok = event.Annotations[key]
|
||||
})
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventLevel() auditinternal.Level {
|
||||
var level auditinternal.Level
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
level = event.Level
|
||||
})
|
||||
return level
|
||||
}
|
||||
|
||||
func (ac *AuditContext) SetEventStage(stage auditinternal.Stage) {
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
event.Stage = stage
|
||||
})
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventStage() auditinternal.Stage {
|
||||
var stage auditinternal.Stage
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
stage = event.Stage
|
||||
})
|
||||
return stage
|
||||
}
|
||||
|
||||
func (ac *AuditContext) SetEventStageTimestamp(timestamp metav1.MicroTime) {
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
event.StageTimestamp = timestamp
|
||||
})
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventResponseStatus() *metav1.Status {
|
||||
var status *metav1.Status
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
status = event.ResponseStatus
|
||||
})
|
||||
return status
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventRequestReceivedTimestamp() metav1.MicroTime {
|
||||
var timestamp metav1.MicroTime
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
timestamp = event.RequestReceivedTimestamp
|
||||
})
|
||||
return timestamp
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventStageTimestamp() metav1.MicroTime {
|
||||
var timestamp metav1.MicroTime
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
timestamp = event.StageTimestamp
|
||||
})
|
||||
return timestamp
|
||||
}
|
||||
|
||||
func (ac *AuditContext) SetEventResponseStatus(status *metav1.Status) {
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
event.ResponseStatus = status
|
||||
})
|
||||
}
|
||||
|
||||
func (ac *AuditContext) SetEventResponseStatusCode(statusCode int32) {
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
if event.ResponseStatus == nil {
|
||||
event.ResponseStatus = &metav1.Status{}
|
||||
}
|
||||
event.ResponseStatus.Code = statusCode
|
||||
})
|
||||
}
|
||||
|
||||
func (ac *AuditContext) GetEventAnnotations() map[string]string {
|
||||
var annotations map[string]string
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
annotations = maps.Clone(event.Annotations)
|
||||
})
|
||||
return annotations
|
||||
}
|
||||
|
||||
// AddAuditAnnotation sets the audit annotation for the given key, value pair.
|
||||
|
@ -66,8 +275,8 @@ func AddAuditAnnotation(ctx context.Context, key, value string) {
|
|||
return
|
||||
}
|
||||
|
||||
ac.annotationMutex.Lock()
|
||||
defer ac.annotationMutex.Unlock()
|
||||
ac.lock.Lock()
|
||||
defer ac.lock.Unlock()
|
||||
|
||||
addAuditAnnotationLocked(ac, key, value)
|
||||
}
|
||||
|
@ -81,8 +290,8 @@ func AddAuditAnnotations(ctx context.Context, keysAndValues ...string) {
|
|||
return
|
||||
}
|
||||
|
||||
ac.annotationMutex.Lock()
|
||||
defer ac.annotationMutex.Unlock()
|
||||
ac.lock.Lock()
|
||||
defer ac.lock.Unlock()
|
||||
|
||||
if len(keysAndValues)%2 != 0 {
|
||||
klog.Errorf("Dropping mismatched audit annotation %q", keysAndValues[len(keysAndValues)-1])
|
||||
|
@ -100,8 +309,8 @@ func AddAuditAnnotationsMap(ctx context.Context, annotations map[string]string)
|
|||
return
|
||||
}
|
||||
|
||||
ac.annotationMutex.Lock()
|
||||
defer ac.annotationMutex.Unlock()
|
||||
ac.lock.Lock()
|
||||
defer ac.lock.Unlock()
|
||||
|
||||
for k, v := range annotations {
|
||||
addAuditAnnotationLocked(ac, k, v)
|
||||
|
@ -110,8 +319,7 @@ func AddAuditAnnotationsMap(ctx context.Context, annotations map[string]string)
|
|||
|
||||
// addAuditAnnotationLocked records the audit annotation on the event.
|
||||
func addAuditAnnotationLocked(ac *AuditContext, key, value string) {
|
||||
ae := &ac.Event
|
||||
|
||||
ae := &ac.event
|
||||
if ae.Annotations == nil {
|
||||
ae.Annotations = make(map[string]string)
|
||||
}
|
||||
|
@ -128,15 +336,11 @@ func WithAuditContext(parent context.Context) context.Context {
|
|||
return parent // Avoid double registering.
|
||||
}
|
||||
|
||||
return genericapirequest.WithValue(parent, auditKey, &AuditContext{})
|
||||
}
|
||||
|
||||
// AuditEventFrom returns the audit event struct on the ctx
|
||||
func AuditEventFrom(ctx context.Context) *auditinternal.Event {
|
||||
if ac := AuditContextFrom(ctx); ac.Enabled() {
|
||||
return &ac.Event
|
||||
}
|
||||
return nil
|
||||
return genericapirequest.WithValue(parent, auditKey, &AuditContext{
|
||||
event: auditinternal.Event{
|
||||
Stage: auditinternal.StageResponseStarted,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AuditContextFrom returns the pair of the audit configuration object
|
||||
|
@ -154,7 +358,10 @@ func WithAuditID(ctx context.Context, auditID types.UID) {
|
|||
return
|
||||
}
|
||||
if ac := AuditContextFrom(ctx); ac != nil {
|
||||
ac.Event.AuditID = auditID
|
||||
ac.visitEvent(func(event *auditinternal.Event) {
|
||||
ac.auditID.Store(auditID)
|
||||
event.AuditID = auditID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +369,8 @@ func WithAuditID(ctx context.Context, auditID types.UID) {
|
|||
// auditing is enabled.
|
||||
func AuditIDFrom(ctx context.Context) (types.UID, bool) {
|
||||
if ac := AuditContextFrom(ctx); ac != nil {
|
||||
return ac.Event.AuditID, true
|
||||
id, _ := ac.auditID.Load().(types.UID)
|
||||
return id, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
|
|
@ -40,16 +40,34 @@ func TestEnabled(t *testing.T) {
|
|||
ctx: &AuditContext{},
|
||||
expectEnabled: true, // An AuditContext should be considered enabled before the level is set
|
||||
}, {
|
||||
name: "level None",
|
||||
ctx: &AuditContext{RequestAuditConfig: RequestAuditConfig{Level: auditinternal.LevelNone}},
|
||||
name: "level None",
|
||||
ctx: func() *AuditContext {
|
||||
ctx := &AuditContext{}
|
||||
if err := ctx.Init(RequestAuditConfig{Level: auditinternal.LevelNone}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ctx
|
||||
}(),
|
||||
expectEnabled: false,
|
||||
}, {
|
||||
name: "level Metadata",
|
||||
ctx: &AuditContext{RequestAuditConfig: RequestAuditConfig{Level: auditinternal.LevelMetadata}},
|
||||
name: "level Metadata",
|
||||
ctx: func() *AuditContext {
|
||||
ctx := &AuditContext{}
|
||||
if err := ctx.Init(RequestAuditConfig{Level: auditinternal.LevelMetadata}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ctx
|
||||
}(),
|
||||
expectEnabled: true,
|
||||
}, {
|
||||
name: "level RequestResponse",
|
||||
ctx: &AuditContext{RequestAuditConfig: RequestAuditConfig{Level: auditinternal.LevelRequestResponse}},
|
||||
name: "level RequestResponse",
|
||||
ctx: func() *AuditContext {
|
||||
ctx := &AuditContext{}
|
||||
if err := ctx.Init(RequestAuditConfig{Level: auditinternal.LevelRequestResponse}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ctx
|
||||
}(),
|
||||
expectEnabled: true,
|
||||
}}
|
||||
|
||||
|
@ -72,7 +90,7 @@ func TestAddAuditAnnotation(t *testing.T) {
|
|||
assert.Len(t, annotations, numAnnotations)
|
||||
}
|
||||
|
||||
ctxWithAnnotation := withAuditContextAndLevel(context.Background(), auditinternal.LevelMetadata)
|
||||
ctxWithAnnotation := withAuditContextAndLevel(context.Background(), t, auditinternal.LevelMetadata)
|
||||
AddAuditAnnotation(ctxWithAnnotation, fmt.Sprintf(annotationKeyTemplate, 0), annotationExtraValue)
|
||||
|
||||
tests := []struct {
|
||||
|
@ -89,28 +107,28 @@ func TestAddAuditAnnotation(t *testing.T) {
|
|||
// Annotations should be retained.
|
||||
ctx: WithAuditContext(context.Background()),
|
||||
validator: func(t *testing.T, ctx context.Context) {
|
||||
ev := AuditContextFrom(ctx).Event
|
||||
ev := AuditContextFrom(ctx).event
|
||||
expectAnnotations(t, ev.Annotations)
|
||||
},
|
||||
}, {
|
||||
description: "with metadata level",
|
||||
ctx: withAuditContextAndLevel(context.Background(), auditinternal.LevelMetadata),
|
||||
ctx: withAuditContextAndLevel(context.Background(), t, auditinternal.LevelMetadata),
|
||||
validator: func(t *testing.T, ctx context.Context) {
|
||||
ev := AuditContextFrom(ctx).Event
|
||||
ev := AuditContextFrom(ctx).event
|
||||
expectAnnotations(t, ev.Annotations)
|
||||
},
|
||||
}, {
|
||||
description: "with none level",
|
||||
ctx: withAuditContextAndLevel(context.Background(), auditinternal.LevelNone),
|
||||
ctx: withAuditContextAndLevel(context.Background(), t, auditinternal.LevelNone),
|
||||
validator: func(t *testing.T, ctx context.Context) {
|
||||
ev := AuditContextFrom(ctx).Event
|
||||
ev := AuditContextFrom(ctx).event
|
||||
assert.Empty(t, ev.Annotations)
|
||||
},
|
||||
}, {
|
||||
description: "with overlapping annotations",
|
||||
ctx: ctxWithAnnotation,
|
||||
validator: func(t *testing.T, ctx context.Context) {
|
||||
ev := AuditContextFrom(ctx).Event
|
||||
ev := AuditContextFrom(ctx).event
|
||||
expectAnnotations(t, ev.Annotations)
|
||||
// Verify that the pre-existing annotation is not overwritten.
|
||||
assert.Equal(t, annotationExtraValue, ev.Annotations[fmt.Sprintf(annotationKeyTemplate, 0)])
|
||||
|
@ -144,8 +162,8 @@ func TestAuditAnnotationsWithAuditLoggingSetup(t *testing.T) {
|
|||
AddAuditAnnotation(ctx, "before-evaluation", "1")
|
||||
|
||||
// policy evaluated, audit logging enabled
|
||||
if ac := AuditContextFrom(ctx); ac != nil {
|
||||
ac.RequestAuditConfig.Level = auditinternal.LevelMetadata
|
||||
if err := AuditContextFrom(ctx).Init(RequestAuditConfig{Level: auditinternal.LevelMetadata}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
AddAuditAnnotation(ctx, "after-evaluation", "2")
|
||||
|
||||
|
@ -153,13 +171,14 @@ func TestAuditAnnotationsWithAuditLoggingSetup(t *testing.T) {
|
|||
"before-evaluation": "1",
|
||||
"after-evaluation": "2",
|
||||
}
|
||||
actual := AuditContextFrom(ctx).Event.Annotations
|
||||
actual := AuditContextFrom(ctx).event.Annotations
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func withAuditContextAndLevel(ctx context.Context, l auditinternal.Level) context.Context {
|
||||
func withAuditContextAndLevel(ctx context.Context, t *testing.T, l auditinternal.Level) context.Context {
|
||||
ctx = WithAuditContext(ctx)
|
||||
ac := AuditContextFrom(ctx)
|
||||
ac.RequestAuditConfig.Level = l
|
||||
if err := AuditContextFrom(ctx).Init(RequestAuditConfig{Level: l}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -359,10 +360,6 @@ func TestOmitManagedFields(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
boolPtr := func(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
policy func() audit.Policy
|
||||
|
@ -395,7 +392,7 @@ func TestOmitManagedFields(t *testing.T) {
|
|||
name: "global policy default is true, rule overrides to false",
|
||||
policy: func() audit.Policy {
|
||||
rule := matchingPolicyRule.DeepCopy()
|
||||
rule.OmitManagedFields = boolPtr(false)
|
||||
rule.OmitManagedFields = ptr.To(false)
|
||||
return audit.Policy{
|
||||
OmitManagedFields: true,
|
||||
Rules: []audit.PolicyRule{*rule},
|
||||
|
@ -407,7 +404,7 @@ func TestOmitManagedFields(t *testing.T) {
|
|||
name: "global policy default is false, rule overrides to true",
|
||||
policy: func() audit.Policy {
|
||||
rule := matchingPolicyRule.DeepCopy()
|
||||
rule.OmitManagedFields = boolPtr(true)
|
||||
rule.OmitManagedFields = ptr.To(true)
|
||||
return audit.Policy{
|
||||
OmitManagedFields: false,
|
||||
Rules: []audit.PolicyRule{*rule},
|
||||
|
|
|
@ -18,7 +18,7 @@ package policy
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -47,7 +47,7 @@ func LoadPolicyFromFile(filePath string) (*auditinternal.Policy, error) {
|
|||
if filePath == "" {
|
||||
return nil, fmt.Errorf("file path not specified")
|
||||
}
|
||||
policyDef, err := ioutil.ReadFile(filePath)
|
||||
policyDef, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file path %q: %+v", filePath, err)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package policy
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -158,7 +157,7 @@ kind: Policy`,
|
|||
}
|
||||
|
||||
func writePolicy(t *testing.T, policy string) (string, error) {
|
||||
f, err := ioutil.TempFile("", "policy.yaml")
|
||||
f, err := os.CreateTemp("", "policy.yaml")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = f.WriteString(policy)
|
||||
|
|
|
@ -40,110 +40,73 @@ const (
|
|||
userAgentTruncateSuffix = "...TRUNCATED"
|
||||
)
|
||||
|
||||
func LogRequestMetadata(ctx context.Context, req *http.Request, requestReceivedTimestamp time.Time, level auditinternal.Level, attribs authorizer.Attributes) {
|
||||
func LogRequestMetadata(ctx context.Context, req *http.Request, requestReceivedTimestamp time.Time, attribs authorizer.Attributes) {
|
||||
ac := AuditContextFrom(ctx)
|
||||
if !ac.Enabled() {
|
||||
return
|
||||
}
|
||||
ev := &ac.Event
|
||||
|
||||
ev.RequestReceivedTimestamp = metav1.NewMicroTime(requestReceivedTimestamp)
|
||||
ev.Verb = attribs.GetVerb()
|
||||
ev.RequestURI = req.URL.RequestURI()
|
||||
ev.UserAgent = maybeTruncateUserAgent(req)
|
||||
ev.Level = level
|
||||
ac.visitEvent(func(ev *auditinternal.Event) {
|
||||
ev.RequestReceivedTimestamp = metav1.NewMicroTime(requestReceivedTimestamp)
|
||||
ev.Verb = attribs.GetVerb()
|
||||
ev.RequestURI = req.URL.RequestURI()
|
||||
ev.UserAgent = maybeTruncateUserAgent(req)
|
||||
|
||||
ips := utilnet.SourceIPs(req)
|
||||
ev.SourceIPs = make([]string, len(ips))
|
||||
for i := range ips {
|
||||
ev.SourceIPs[i] = ips[i].String()
|
||||
}
|
||||
|
||||
if user := attribs.GetUser(); user != nil {
|
||||
ev.User.Username = user.GetName()
|
||||
ev.User.Extra = map[string]authnv1.ExtraValue{}
|
||||
for k, v := range user.GetExtra() {
|
||||
ev.User.Extra[k] = authnv1.ExtraValue(v)
|
||||
ips := utilnet.SourceIPs(req)
|
||||
ev.SourceIPs = make([]string, len(ips))
|
||||
for i := range ips {
|
||||
ev.SourceIPs[i] = ips[i].String()
|
||||
}
|
||||
ev.User.Groups = user.GetGroups()
|
||||
ev.User.UID = user.GetUID()
|
||||
}
|
||||
|
||||
if attribs.IsResourceRequest() {
|
||||
ev.ObjectRef = &auditinternal.ObjectReference{
|
||||
Namespace: attribs.GetNamespace(),
|
||||
Name: attribs.GetName(),
|
||||
Resource: attribs.GetResource(),
|
||||
Subresource: attribs.GetSubresource(),
|
||||
APIGroup: attribs.GetAPIGroup(),
|
||||
APIVersion: attribs.GetAPIVersion(),
|
||||
if user := attribs.GetUser(); user != nil {
|
||||
ev.User.Username = user.GetName()
|
||||
ev.User.Extra = map[string]authnv1.ExtraValue{}
|
||||
for k, v := range user.GetExtra() {
|
||||
ev.User.Extra[k] = authnv1.ExtraValue(v)
|
||||
}
|
||||
ev.User.Groups = user.GetGroups()
|
||||
ev.User.UID = user.GetUID()
|
||||
}
|
||||
}
|
||||
|
||||
if attribs.IsResourceRequest() {
|
||||
ev.ObjectRef = &auditinternal.ObjectReference{
|
||||
Namespace: attribs.GetNamespace(),
|
||||
Name: attribs.GetName(),
|
||||
Resource: attribs.GetResource(),
|
||||
Subresource: attribs.GetSubresource(),
|
||||
APIGroup: attribs.GetAPIGroup(),
|
||||
APIVersion: attribs.GetAPIVersion(),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// LogImpersonatedUser fills in the impersonated user attributes into an audit event.
|
||||
func LogImpersonatedUser(ae *auditinternal.Event, user user.Info) {
|
||||
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
|
||||
func LogImpersonatedUser(ctx context.Context, user user.Info) {
|
||||
ac := AuditContextFrom(ctx)
|
||||
if !ac.Enabled() {
|
||||
return
|
||||
}
|
||||
ae.ImpersonatedUser = &authnv1.UserInfo{
|
||||
Username: user.GetName(),
|
||||
}
|
||||
ae.ImpersonatedUser.Groups = user.GetGroups()
|
||||
ae.ImpersonatedUser.UID = user.GetUID()
|
||||
ae.ImpersonatedUser.Extra = map[string]authnv1.ExtraValue{}
|
||||
for k, v := range user.GetExtra() {
|
||||
ae.ImpersonatedUser.Extra[k] = authnv1.ExtraValue(v)
|
||||
}
|
||||
ac.LogImpersonatedUser(user)
|
||||
}
|
||||
|
||||
// LogRequestObject fills in the request object into an audit event. The passed runtime.Object
|
||||
// will be converted to the given gv.
|
||||
func LogRequestObject(ctx context.Context, obj runtime.Object, objGV schema.GroupVersion, gvr schema.GroupVersionResource, subresource string, s runtime.NegotiatedSerializer) {
|
||||
ae := AuditEventFrom(ctx)
|
||||
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
|
||||
ac := AuditContextFrom(ctx)
|
||||
if !ac.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
// complete ObjectRef
|
||||
if ae.ObjectRef == nil {
|
||||
ae.ObjectRef = &auditinternal.ObjectReference{}
|
||||
if ac.GetEventLevel().Less(auditinternal.LevelMetadata) {
|
||||
return
|
||||
}
|
||||
|
||||
// meta.Accessor is more general than ObjectMetaAccessor, but if it fails, we can just skip setting these bits
|
||||
if meta, err := meta.Accessor(obj); err == nil {
|
||||
if len(ae.ObjectRef.Namespace) == 0 {
|
||||
ae.ObjectRef.Namespace = meta.GetNamespace()
|
||||
}
|
||||
if len(ae.ObjectRef.Name) == 0 {
|
||||
ae.ObjectRef.Name = meta.GetName()
|
||||
}
|
||||
if len(ae.ObjectRef.UID) == 0 {
|
||||
ae.ObjectRef.UID = meta.GetUID()
|
||||
}
|
||||
if len(ae.ObjectRef.ResourceVersion) == 0 {
|
||||
ae.ObjectRef.ResourceVersion = meta.GetResourceVersion()
|
||||
}
|
||||
}
|
||||
if len(ae.ObjectRef.APIVersion) == 0 {
|
||||
ae.ObjectRef.APIGroup = gvr.Group
|
||||
ae.ObjectRef.APIVersion = gvr.Version
|
||||
}
|
||||
if len(ae.ObjectRef.Resource) == 0 {
|
||||
ae.ObjectRef.Resource = gvr.Resource
|
||||
}
|
||||
if len(ae.ObjectRef.Subresource) == 0 {
|
||||
ae.ObjectRef.Subresource = subresource
|
||||
}
|
||||
|
||||
if ae.Level.Less(auditinternal.LevelRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
if shouldOmitManagedFields(ctx) {
|
||||
objMeta, _ := meta.Accessor(obj)
|
||||
if shouldOmitManagedFields(ac) {
|
||||
copy, ok, err := copyWithoutManagedFields(obj)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Error while dropping managed fields from the request", "auditID", ae.AuditID)
|
||||
klog.ErrorS(err, "Error while dropping managed fields from the request", "auditID", ac.AuditID())
|
||||
}
|
||||
if ok {
|
||||
obj = copy
|
||||
|
@ -151,54 +114,75 @@ func LogRequestObject(ctx context.Context, obj runtime.Object, objGV schema.Grou
|
|||
}
|
||||
|
||||
// TODO(audit): hook into the serializer to avoid double conversion
|
||||
var err error
|
||||
ae.RequestObject, err = encodeObject(obj, objGV, s)
|
||||
requestObject, err := encodeObject(obj, objGV, s)
|
||||
if err != nil {
|
||||
// TODO(audit): add error slice to audit event struct
|
||||
klog.ErrorS(err, "Encoding failed of request object", "auditID", ae.AuditID, "gvr", gvr.String(), "obj", obj)
|
||||
klog.ErrorS(err, "Encoding failed of request object", "auditID", ac.AuditID(), "gvr", gvr.String(), "obj", obj)
|
||||
return
|
||||
}
|
||||
|
||||
ac.visitEvent(func(ae *auditinternal.Event) {
|
||||
if ae.ObjectRef == nil {
|
||||
ae.ObjectRef = &auditinternal.ObjectReference{}
|
||||
}
|
||||
|
||||
if objMeta != nil {
|
||||
if len(ae.ObjectRef.Namespace) == 0 {
|
||||
ae.ObjectRef.Namespace = objMeta.GetNamespace()
|
||||
}
|
||||
if len(ae.ObjectRef.Name) == 0 {
|
||||
ae.ObjectRef.Name = objMeta.GetName()
|
||||
}
|
||||
if len(ae.ObjectRef.UID) == 0 {
|
||||
ae.ObjectRef.UID = objMeta.GetUID()
|
||||
}
|
||||
if len(ae.ObjectRef.ResourceVersion) == 0 {
|
||||
ae.ObjectRef.ResourceVersion = objMeta.GetResourceVersion()
|
||||
}
|
||||
}
|
||||
if len(ae.ObjectRef.APIVersion) == 0 {
|
||||
ae.ObjectRef.APIGroup = gvr.Group
|
||||
ae.ObjectRef.APIVersion = gvr.Version
|
||||
}
|
||||
if len(ae.ObjectRef.Resource) == 0 {
|
||||
ae.ObjectRef.Resource = gvr.Resource
|
||||
}
|
||||
if len(ae.ObjectRef.Subresource) == 0 {
|
||||
ae.ObjectRef.Subresource = subresource
|
||||
}
|
||||
|
||||
if ae.Level.Less(auditinternal.LevelRequest) {
|
||||
return
|
||||
}
|
||||
ae.RequestObject = requestObject
|
||||
})
|
||||
}
|
||||
|
||||
// LogRequestPatch fills in the given patch as the request object into an audit event.
|
||||
func LogRequestPatch(ctx context.Context, patch []byte) {
|
||||
ae := AuditEventFrom(ctx)
|
||||
if ae == nil || ae.Level.Less(auditinternal.LevelRequest) {
|
||||
ac := AuditContextFrom(ctx)
|
||||
if ac.GetEventLevel().Less(auditinternal.LevelRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
ae.RequestObject = &runtime.Unknown{
|
||||
Raw: patch,
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
}
|
||||
ac.LogRequestPatch(patch)
|
||||
}
|
||||
|
||||
// LogResponseObject fills in the response object into an audit event. The passed runtime.Object
|
||||
// will be converted to the given gv.
|
||||
func LogResponseObject(ctx context.Context, obj runtime.Object, gv schema.GroupVersion, s runtime.NegotiatedSerializer) {
|
||||
ae := AuditEventFrom(ctx)
|
||||
if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
|
||||
ac := AuditContextFrom(WithAuditContext(ctx))
|
||||
status, _ := obj.(*metav1.Status)
|
||||
if ac.GetEventLevel().Less(auditinternal.LevelMetadata) {
|
||||
return
|
||||
}
|
||||
if status, ok := obj.(*metav1.Status); ok {
|
||||
// selectively copy the bounded fields.
|
||||
ae.ResponseStatus = &metav1.Status{
|
||||
Status: status.Status,
|
||||
Message: status.Message,
|
||||
Reason: status.Reason,
|
||||
Details: status.Details,
|
||||
Code: status.Code,
|
||||
}
|
||||
}
|
||||
|
||||
if ae.Level.Less(auditinternal.LevelRequestResponse) {
|
||||
} else if ac.GetEventLevel().Less(auditinternal.LevelRequestResponse) {
|
||||
ac.LogResponseObject(status, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if shouldOmitManagedFields(ctx) {
|
||||
if shouldOmitManagedFields(ac) {
|
||||
copy, ok, err := copyWithoutManagedFields(obj)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Error while dropping managed fields from the response", "auditID", ae.AuditID)
|
||||
klog.ErrorS(err, "Error while dropping managed fields from the response", "auditID", ac.AuditID())
|
||||
}
|
||||
if ok {
|
||||
obj = copy
|
||||
|
@ -207,10 +191,11 @@ func LogResponseObject(ctx context.Context, obj runtime.Object, gv schema.GroupV
|
|||
|
||||
// TODO(audit): hook into the serializer to avoid double conversion
|
||||
var err error
|
||||
ae.ResponseObject, err = encodeObject(obj, gv, s)
|
||||
responseObject, err := encodeObject(obj, gv, s)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Encoding failed of response object", "auditID", ae.AuditID, "obj", obj)
|
||||
klog.ErrorS(err, "Encoding failed of response object", "auditID", ac.AuditID(), "obj", obj)
|
||||
}
|
||||
ac.LogResponseObject(status, responseObject)
|
||||
}
|
||||
|
||||
func encodeObject(obj runtime.Object, gv schema.GroupVersion, serializer runtime.NegotiatedSerializer) (*runtime.Unknown, error) {
|
||||
|
@ -301,9 +286,9 @@ func removeManagedFields(obj runtime.Object) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func shouldOmitManagedFields(ctx context.Context) bool {
|
||||
if auditContext := AuditContextFrom(ctx); auditContext != nil {
|
||||
return auditContext.RequestAuditConfig.OmitManagedFields
|
||||
func shouldOmitManagedFields(ac *AuditContext) bool {
|
||||
if ac != nil && ac.initialized.Load() && ac.requestAuditConfig.OmitManagedFields {
|
||||
return true
|
||||
}
|
||||
|
||||
// If we can't decide, return false to maintain current behavior which is
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
Copyright 2025 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 audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
)
|
||||
|
||||
func TestLogResponseObjectWithPod(t *testing.T) {
|
||||
testPod := &corev1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pod",
|
||||
Namespace: "test-namespace",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "test-container",
|
||||
Image: "test-image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
if err := corev1.AddToScheme(scheme); err != nil {
|
||||
t.Fatalf("Failed to add core/v1 to scheme: %v", err)
|
||||
}
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
negotiatedSerializer := codecs.WithoutConversion()
|
||||
|
||||
// Create audit context with RequestResponse level
|
||||
ctx := WithAuditContext(context.Background())
|
||||
ac := AuditContextFrom(ctx)
|
||||
|
||||
captureSink := &capturingAuditSink{}
|
||||
if err := ac.Init(RequestAuditConfig{Level: auditinternal.LevelRequestResponse}, captureSink); err != nil {
|
||||
t.Fatalf("Failed to initialize audit context: %v", err)
|
||||
}
|
||||
|
||||
LogResponseObject(ctx, testPod, schema.GroupVersion{Group: "", Version: "v1"}, negotiatedSerializer)
|
||||
ac.ProcessEventStage(ctx, auditinternal.StageResponseComplete)
|
||||
|
||||
if len(captureSink.events) != 1 {
|
||||
t.Fatalf("Expected one audit event to be captured, got %d", len(captureSink.events))
|
||||
}
|
||||
event := captureSink.events[0]
|
||||
if event.ResponseObject == nil {
|
||||
t.Fatal("Expected ResponseObject to be set, but it was nil")
|
||||
}
|
||||
if event.ResponseObject.ContentType != runtime.ContentTypeJSON {
|
||||
t.Errorf("Expected ContentType to be %q, got %q", runtime.ContentTypeJSON, event.ResponseObject.ContentType)
|
||||
}
|
||||
if len(event.ResponseObject.Raw) == 0 {
|
||||
t.Error("Expected ResponseObject.Raw to contain data, but it was empty")
|
||||
}
|
||||
|
||||
responseJSON := string(event.ResponseObject.Raw)
|
||||
expectedFields := []string{"test-pod", "test-namespace", "test-container", "test-image"}
|
||||
for _, field := range expectedFields {
|
||||
if !strings.Contains(responseJSON, field) {
|
||||
t.Errorf("Response should contain %q but didn't. Response: %s", field, responseJSON)
|
||||
}
|
||||
}
|
||||
|
||||
if event.ResponseStatus != nil {
|
||||
t.Errorf("Expected ResponseStatus to be nil for regular object, got: %+v", event.ResponseStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogResponseObjectWithStatus(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
level auditinternal.Level
|
||||
status *metav1.Status
|
||||
shouldEncode bool
|
||||
expectResponseObj bool
|
||||
expectStatusFields bool
|
||||
}{
|
||||
{
|
||||
name: "RequestResponse level should encode and log status fields",
|
||||
level: auditinternal.LevelRequestResponse,
|
||||
status: &metav1.Status{Status: "Success", Message: "Test message", Code: 200},
|
||||
shouldEncode: true,
|
||||
expectResponseObj: true,
|
||||
expectStatusFields: true,
|
||||
},
|
||||
{
|
||||
name: "Metadata level should log status fields without encoding",
|
||||
level: auditinternal.LevelMetadata,
|
||||
status: &metav1.Status{Status: "Success", Message: "Test message", Code: 200},
|
||||
shouldEncode: false,
|
||||
expectResponseObj: false,
|
||||
expectStatusFields: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
if err := metav1.AddMetaToScheme(scheme); err != nil {
|
||||
t.Fatalf("Failed to add meta to scheme: %v", err)
|
||||
}
|
||||
scheme.AddKnownTypes(schema.GroupVersion{Version: "v1"}, &metav1.Status{})
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
negotiatedSerializer := codecs.WithoutConversion()
|
||||
|
||||
ctx := WithAuditContext(context.Background())
|
||||
ac := AuditContextFrom(ctx)
|
||||
|
||||
captureSink := &capturingAuditSink{}
|
||||
if err := ac.Init(RequestAuditConfig{Level: tc.level}, captureSink); err != nil {
|
||||
t.Fatalf("Failed to initialize audit context: %v", err)
|
||||
}
|
||||
|
||||
LogResponseObject(ctx, tc.status, schema.GroupVersion{Group: "", Version: "v1"}, negotiatedSerializer)
|
||||
ac.ProcessEventStage(ctx, auditinternal.StageResponseComplete)
|
||||
|
||||
if len(captureSink.events) != 1 {
|
||||
t.Fatalf("Expected one audit event to be captured, got %d", len(captureSink.events))
|
||||
}
|
||||
event := captureSink.events[0]
|
||||
|
||||
if tc.expectResponseObj {
|
||||
if event.ResponseObject == nil {
|
||||
t.Error("Expected ResponseObject to be set, but it was nil")
|
||||
}
|
||||
} else {
|
||||
if event.ResponseObject != nil {
|
||||
t.Error("Expected ResponseObject to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
if tc.expectStatusFields {
|
||||
if event.ResponseStatus == nil {
|
||||
t.Fatal("Expected ResponseStatus to be set, but it was nil")
|
||||
}
|
||||
if event.ResponseStatus.Status != tc.status.Status {
|
||||
t.Errorf("Expected ResponseStatus.Status to be %q, got %q", tc.status.Status, event.ResponseStatus.Status)
|
||||
}
|
||||
if event.ResponseStatus.Message != tc.status.Message {
|
||||
t.Errorf("Expected ResponseStatus.Message to be %q, got %q", tc.status.Message, event.ResponseStatus.Message)
|
||||
}
|
||||
if event.ResponseStatus.Code != tc.status.Code {
|
||||
t.Errorf("Expected ResponseStatus.Code to be %d, got %d", tc.status.Code, event.ResponseStatus.Code)
|
||||
}
|
||||
} else if event.ResponseStatus != nil {
|
||||
t.Error("Expected ResponseStatus to be nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogResponseObjectLevelCheck(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
level auditinternal.Level
|
||||
obj runtime.Object
|
||||
shouldEncode bool
|
||||
expectResponseObj bool
|
||||
expectStatusFields bool
|
||||
}{
|
||||
{
|
||||
name: "None level should not encode or log anything",
|
||||
level: auditinternal.LevelNone,
|
||||
obj: &corev1.Pod{},
|
||||
shouldEncode: false,
|
||||
expectResponseObj: false,
|
||||
expectStatusFields: false,
|
||||
},
|
||||
{
|
||||
name: "Metadata level should not encode or log anything",
|
||||
level: auditinternal.LevelMetadata,
|
||||
obj: &corev1.Pod{},
|
||||
shouldEncode: false,
|
||||
expectResponseObj: false,
|
||||
expectStatusFields: false,
|
||||
},
|
||||
{
|
||||
name: "Metadata level with Status should log status fields without encoding",
|
||||
level: auditinternal.LevelMetadata,
|
||||
obj: &metav1.Status{
|
||||
Status: "Success",
|
||||
Message: "Test message",
|
||||
Code: 200,
|
||||
},
|
||||
shouldEncode: false,
|
||||
expectResponseObj: false,
|
||||
expectStatusFields: true,
|
||||
},
|
||||
{
|
||||
name: "Request level with Pod should not encode or log",
|
||||
level: auditinternal.LevelRequest,
|
||||
obj: &corev1.Pod{},
|
||||
shouldEncode: false,
|
||||
expectResponseObj: false,
|
||||
expectStatusFields: false,
|
||||
},
|
||||
{
|
||||
name: "Request level with Status should log status fields without encoding",
|
||||
level: auditinternal.LevelRequest,
|
||||
obj: &metav1.Status{
|
||||
Status: "Success",
|
||||
Message: "Test message",
|
||||
Code: 200,
|
||||
},
|
||||
shouldEncode: false,
|
||||
expectResponseObj: false,
|
||||
expectStatusFields: true,
|
||||
},
|
||||
{
|
||||
name: "RequestResponse level with Pod should encode",
|
||||
level: auditinternal.LevelRequestResponse,
|
||||
obj: &corev1.Pod{},
|
||||
shouldEncode: true,
|
||||
expectResponseObj: true,
|
||||
expectStatusFields: false,
|
||||
},
|
||||
{
|
||||
name: "RequestResponse level with Status should encode and log status fields",
|
||||
level: auditinternal.LevelRequestResponse,
|
||||
obj: &metav1.Status{
|
||||
Status: "Success",
|
||||
Message: "Test message",
|
||||
Code: 200,
|
||||
},
|
||||
shouldEncode: true,
|
||||
expectResponseObj: true,
|
||||
expectStatusFields: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := WithAuditContext(context.Background())
|
||||
ac := AuditContextFrom(ctx)
|
||||
|
||||
captureSink := &capturingAuditSink{}
|
||||
if err := ac.Init(RequestAuditConfig{Level: tc.level}, captureSink); err != nil {
|
||||
t.Fatalf("Failed to initialize audit context: %v", err)
|
||||
}
|
||||
|
||||
mockSerializer := &mockNegotiatedSerializer{}
|
||||
LogResponseObject(ctx, tc.obj, schema.GroupVersion{Group: "", Version: "v1"}, mockSerializer)
|
||||
ac.ProcessEventStage(ctx, auditinternal.StageResponseComplete)
|
||||
|
||||
if mockSerializer.encodeCalled != tc.shouldEncode {
|
||||
t.Errorf("Expected encoding to be called: %v, but got: %v", tc.shouldEncode, mockSerializer.encodeCalled)
|
||||
}
|
||||
|
||||
if len(captureSink.events) != 1 {
|
||||
t.Fatalf("Expected one audit event to be captured, got %d", len(captureSink.events))
|
||||
}
|
||||
event := captureSink.events[0]
|
||||
|
||||
if tc.expectResponseObj {
|
||||
if event.ResponseObject == nil {
|
||||
t.Error("Expected ResponseObject to be set, but it was nil")
|
||||
}
|
||||
} else {
|
||||
if event.ResponseObject != nil {
|
||||
t.Error("Expected ResponseObject to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
// Check ResponseStatus for Status objects
|
||||
status, isStatus := tc.obj.(*metav1.Status)
|
||||
if isStatus && tc.expectStatusFields {
|
||||
if event.ResponseStatus == nil {
|
||||
t.Error("Expected ResponseStatus to be set for Status object, but it was nil")
|
||||
} else {
|
||||
if event.ResponseStatus.Status != status.Status {
|
||||
t.Errorf("Expected ResponseStatus.Status to be %q, got %q", status.Status, event.ResponseStatus.Status)
|
||||
}
|
||||
if event.ResponseStatus.Message != status.Message {
|
||||
t.Errorf("Expected ResponseStatus.Message to be %q, got %q", status.Message, event.ResponseStatus.Message)
|
||||
}
|
||||
if event.ResponseStatus.Code != status.Code {
|
||||
t.Errorf("Expected ResponseStatus.Code to be %d, got %d", status.Code, event.ResponseStatus.Code)
|
||||
}
|
||||
}
|
||||
} else if event.ResponseStatus != nil {
|
||||
t.Error("Expected ResponseStatus to be nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockNegotiatedSerializer struct {
|
||||
encodeCalled bool
|
||||
}
|
||||
|
||||
func (m *mockNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||
return []runtime.SerializerInfo{
|
||||
{
|
||||
MediaType: runtime.ContentTypeJSON,
|
||||
EncodesAsText: true,
|
||||
Serializer: nil,
|
||||
PrettySerializer: nil,
|
||||
StreamSerializer: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockNegotiatedSerializer) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||
m.encodeCalled = true
|
||||
return &mockEncoder{}
|
||||
}
|
||||
|
||||
func (m *mockNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockEncoder struct{}
|
||||
|
||||
func (e *mockEncoder) Encode(obj runtime.Object, w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *mockEncoder) Identifier() runtime.Identifier {
|
||||
return runtime.Identifier("mock")
|
||||
}
|
||||
|
||||
type capturingAuditSink struct {
|
||||
events []*auditinternal.Event
|
||||
}
|
||||
|
||||
func (s *capturingAuditSink) ProcessEvents(events ...*auditinternal.Event) bool {
|
||||
for _, event := range events {
|
||||
eventCopy := event.DeepCopy()
|
||||
s.events = append(s.events, eventCopy)
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -22,8 +22,7 @@ import (
|
|||
|
||||
celgo "github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
)
|
||||
|
||||
// ExpressionAccessor is an interface that provides access to a CEL expression.
|
||||
|
@ -55,17 +54,17 @@ type Compiler interface {
|
|||
type ClaimsMapper interface {
|
||||
// EvalClaimMapping evaluates the given claim mapping expression and returns a EvaluationResult.
|
||||
// This is used for username, groups and uid claim mapping that contains a single expression.
|
||||
EvalClaimMapping(ctx context.Context, claims *unstructured.Unstructured) (EvaluationResult, error)
|
||||
EvalClaimMapping(ctx context.Context, claims traits.Mapper) (EvaluationResult, error)
|
||||
// EvalClaimMappings evaluates the given expressions and returns a list of EvaluationResult.
|
||||
// This is used for extra claim mapping and claim validation that contains a list of expressions.
|
||||
EvalClaimMappings(ctx context.Context, claims *unstructured.Unstructured) ([]EvaluationResult, error)
|
||||
EvalClaimMappings(ctx context.Context, claims traits.Mapper) ([]EvaluationResult, error)
|
||||
}
|
||||
|
||||
// UserMapper provides a CEL expression mapper configured with the user CEL variable.
|
||||
type UserMapper interface {
|
||||
// EvalUser evaluates the given user expressions and returns a list of EvaluationResult.
|
||||
// This is used for user validation that contains a list of expressions.
|
||||
EvalUser(ctx context.Context, userInfo *unstructured.Unstructured) ([]EvaluationResult, error)
|
||||
EvalUser(ctx context.Context, userInfo traits.Mapper) ([]EvaluationResult, error)
|
||||
}
|
||||
|
||||
var _ ExpressionAccessor = &ClaimMappingExpression{}
|
||||
|
|
|
@ -20,7 +20,8 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
)
|
||||
|
||||
var _ ClaimsMapper = &mapper{}
|
||||
|
@ -57,8 +58,8 @@ func NewUserMapper(compilationResults []CompilationResult) UserMapper {
|
|||
}
|
||||
|
||||
// EvalClaimMapping evaluates the given claim mapping expression and returns a EvaluationResult.
|
||||
func (m *mapper) EvalClaimMapping(ctx context.Context, claims *unstructured.Unstructured) (EvaluationResult, error) {
|
||||
results, err := m.eval(ctx, map[string]interface{}{claimsVarName: claims.Object})
|
||||
func (m *mapper) EvalClaimMapping(ctx context.Context, claims traits.Mapper) (EvaluationResult, error) {
|
||||
results, err := m.eval(ctx, &varNameActivation{name: claimsVarName, value: claims})
|
||||
if err != nil {
|
||||
return EvaluationResult{}, err
|
||||
}
|
||||
|
@ -69,16 +70,16 @@ func (m *mapper) EvalClaimMapping(ctx context.Context, claims *unstructured.Unst
|
|||
}
|
||||
|
||||
// EvalClaimMappings evaluates the given expressions and returns a list of EvaluationResult.
|
||||
func (m *mapper) EvalClaimMappings(ctx context.Context, claims *unstructured.Unstructured) ([]EvaluationResult, error) {
|
||||
return m.eval(ctx, map[string]interface{}{claimsVarName: claims.Object})
|
||||
func (m *mapper) EvalClaimMappings(ctx context.Context, claims traits.Mapper) ([]EvaluationResult, error) {
|
||||
return m.eval(ctx, &varNameActivation{name: claimsVarName, value: claims})
|
||||
}
|
||||
|
||||
// EvalUser evaluates the given user expressions and returns a list of EvaluationResult.
|
||||
func (m *mapper) EvalUser(ctx context.Context, userInfo *unstructured.Unstructured) ([]EvaluationResult, error) {
|
||||
return m.eval(ctx, map[string]interface{}{userVarName: userInfo.Object})
|
||||
func (m *mapper) EvalUser(ctx context.Context, userInfo traits.Mapper) ([]EvaluationResult, error) {
|
||||
return m.eval(ctx, &varNameActivation{name: userVarName, value: userInfo})
|
||||
}
|
||||
|
||||
func (m *mapper) eval(ctx context.Context, input map[string]interface{}) ([]EvaluationResult, error) {
|
||||
func (m *mapper) eval(ctx context.Context, input *varNameActivation) ([]EvaluationResult, error) {
|
||||
evaluations := make([]EvaluationResult, len(m.compilationResults))
|
||||
|
||||
for i, compilationResult := range m.compilationResults {
|
||||
|
@ -95,3 +96,19 @@ func (m *mapper) eval(ctx context.Context, input map[string]interface{}) ([]Eval
|
|||
|
||||
return evaluations, nil
|
||||
}
|
||||
|
||||
var _ interpreter.Activation = &varNameActivation{}
|
||||
|
||||
type varNameActivation struct {
|
||||
name string
|
||||
value traits.Mapper
|
||||
}
|
||||
|
||||
func (v *varNameActivation) ResolveName(name string) (any, bool) {
|
||||
if v.name != name {
|
||||
return nil, false
|
||||
}
|
||||
return v.value, true
|
||||
}
|
||||
|
||||
func (v *varNameActivation) Parent() interpreter.Activation { return nil }
|
||||
|
|
|
@ -16,4 +16,4 @@ limitations under the License.
|
|||
|
||||
// Package x509 provides a request authenticator that validates and
|
||||
// extracts user information from client certificates
|
||||
package x509 // import "k8s.io/apiserver/pkg/authentication/request/x509"
|
||||
package x509
|
||||
|
|
|
@ -21,15 +21,19 @@ import (
|
|||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
asn1util "k8s.io/apimachinery/pkg/apis/asn1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
)
|
||||
|
@ -281,9 +285,14 @@ var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate
|
|||
fp := sha256.Sum256(chain[0].Raw)
|
||||
id := "X509SHA256=" + hex.EncodeToString(fp[:])
|
||||
|
||||
uid, err := parseUIDFromCert(chain[0])
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: chain[0].Subject.CommonName,
|
||||
UID: uid,
|
||||
Groups: chain[0].Subject.Organization,
|
||||
Extra: map[string][]string{
|
||||
user.CredentialIDKey: {id},
|
||||
|
@ -291,3 +300,33 @@ var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate
|
|||
},
|
||||
}, true, nil
|
||||
})
|
||||
|
||||
var uidOID = asn1util.X509UID()
|
||||
|
||||
func parseUIDFromCert(cert *x509.Certificate) (string, error) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.AllowParsingUserUIDFromCertAuth) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
uids := []string{}
|
||||
for _, name := range cert.Subject.Names {
|
||||
if !name.Type.Equal(uidOID) {
|
||||
continue
|
||||
}
|
||||
uid, ok := name.Value.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unable to parse UID into a string")
|
||||
}
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
if len(uids) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if len(uids) != 1 {
|
||||
return "", fmt.Errorf("expected 1 UID, but found multiple: %v", uids)
|
||||
}
|
||||
if uids[0] == "" {
|
||||
return "", errors.New("UID cannot be an empty string")
|
||||
}
|
||||
return uids[0], nil
|
||||
}
|
||||
|
|
|
@ -19,18 +19,26 @@ package x509
|
|||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
asn1util "k8s.io/apimachinery/pkg/apis/asn1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -124,21 +132,21 @@ const (
|
|||
*/
|
||||
|
||||
rootCACert = `-----BEGIN CERTIFICATE-----
|
||||
MIICtjCCAh+gAwIBAgIULXMaLteLiSCDnEKabvf19qHsr4wwDQYJKoZIhvcNAQEF
|
||||
MIICtjCCAh+gAwIBAgIUXipc16GmHC8Q64wKx+gegIcA0wAwDQYJKoZIhvcNAQEF
|
||||
BQAwZzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE15IFN0YXRlMRAwDgYDVQQHDAdN
|
||||
eSBDaXR5MQ8wDQYDVQQKDAZNeSBPcmcxEDAOBgNVBAsMB015IFVuaXQxEDAOBgNV
|
||||
BAMMB1JPT1QgQ0EwIBcNMjQwNTAyMDU0MzUxWhgPMjEyNDA0MDgwNTQzNTFaMGcx
|
||||
BAMMB1JPT1QgQ0EwIBcNMjQxMDA2MjAzNTIwWhgPMjEyNDA5MTIyMDM1MjBaMGcx
|
||||
CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNeSBTdGF0ZTEQMA4GA1UEBwwHTXkgQ2l0
|
||||
eTEPMA0GA1UECgwGTXkgT3JnMRAwDgYDVQQLDAdNeSBVbml0MRAwDgYDVQQDDAdS
|
||||
T09UIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCow9zeGvY+lZcq1b+L
|
||||
cpMGhXJLNirZY6ic+4A+my+ExlfS/zMTvzLpkGbbCpoFwePBCbsldbLX/JwJhoAV
|
||||
sGxnxRrpdgEyQCJY7E6ht8UFAUlV2E9LiB2/ZtPeWErnJra/rzPYV0LxvDRnRIi0
|
||||
MfZKSrMewsprSy5aMiObGz+XNQIDAQABo10wWzAdBgNVHQ4EFgQU0wfNcua+ClrY
|
||||
6WAgr8LyNn4zYgswHwYDVR0jBBgwFoAU0wfNcua+ClrY6WAgr8LyNn4zYgswDAYD
|
||||
VR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEFBQADgYEASlQHRnHB
|
||||
sqLTMuffSYyvh0argRHGxUu+Cwzqfl84FHlDkvm7gm/2BqZDGeJ8UmY2E28PcxY9
|
||||
eV/5pshMGPn/ICvefxXgq65E+mV6horf0GOCsVzz+FwFl04fCdbZVec2/Ag+P2aZ
|
||||
aLYxRA9jIGqygVA5GdBH3iCU8KIs62mTk6M=
|
||||
T09UIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCt8DHt/ni9y/6lqWss
|
||||
uv2eFvW6N9RvYhxRmuuxQK74F5/VRAfhEMvDOU+woG/HBXMyPOgLL1uWt4dk3DGu
|
||||
WYNwYP2oN6D04KkWYgcxwYFjcduzWxynr5zT1T2B3bxZFMkvqshyrHWD38Vge080
|
||||
NU3Pns7Z53AZu673srH+OSU8WwIDAQABo10wWzAdBgNVHQ4EFgQUSHB11O1rSTtT
|
||||
2+mm+ZxVklG9luYwHwYDVR0jBBgwFoAUSHB11O1rSTtT2+mm+ZxVklG9luYwDAYD
|
||||
VR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEFBQADgYEAj/zGCbq+
|
||||
POo9thqGg2i2/bzHzAr4X9ylJaeM8oaBhk0pvliTcWGb/usjqwWpcXIqHY8jjBrN
|
||||
GFJEH6elL1Q63W+JCwWS14i2jQExjPk7/AWLBv/J7XqgiUhPfF/P9iQp+lGcInNR
|
||||
6TGXeFKLtsrySVfQ4TvEW1zNJj9qJ819YwU=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
|
@ -246,9 +254,13 @@ u7ESEaSlma8XEeex
|
|||
openssl genrsa -out client.key 1024 && \
|
||||
openssl rsa -in ./client.key -outform PEM \
|
||||
-pubout -out ./client.pub && \
|
||||
OID_CONF="oid_section = my_oids\n\n" && \
|
||||
OID_CONF="${OID_CONF}[ my_oids ]\n" && \
|
||||
OID_CONF="${OID_CONF}kube_uid=1.3.6.1.4.1.57683.2\n" && \
|
||||
openssl req -key ./client.key -new\
|
||||
-config <(printf "${OID_CONF}") \
|
||||
-sha1 -out ./client.csr \
|
||||
-subj "/C=US/ST=My State/L=My City/O=My Org/OU=My Unit/CN=client_cn" \
|
||||
-subj "/C=US/ST=My State/L=My City/O=My Org/OU=My Unit/CN=client_cn2/CN=client_cn/kube_uid=client_id" \
|
||||
&& \
|
||||
EXTFILE="subjectKeyIdentifier=hash\n" && \
|
||||
EXTFILE="${EXTFILE}authorityKeyIdentifier=keyid,issuer\n" && \
|
||||
|
@ -277,63 +289,64 @@ u7ESEaSlma8XEeex
|
|||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C = US, ST = My State, L = My City, O = My Org, OU = My Unit, CN = ROOT CA
|
||||
Issuer: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=ROOT CA
|
||||
Validity
|
||||
Not Before: May 2 05:46:24 2024 GMT
|
||||
Not After : Apr 8 05:46:24 2124 GMT
|
||||
Subject: C = US, ST = My State, L = My City, O = My Org, OU = My Unit, CN = client_cn
|
||||
Not Before: Oct 27 00:43:31 2024 GMT
|
||||
Not After : Oct 3 00:43:31 2124 GMT
|
||||
Subject: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=client_cn2, CN=client_cn, 1.3.6.1.4.1.57683.2=client_id
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:bd:3f:2d:d1:86:73:6d:b5:09:9c:ff:42:fb:27:
|
||||
8e:07:69:a3:b6:d1:c7:72:d1:de:98:14:a5:61:9b:
|
||||
83:03:1d:da:54:d1:d4:0d:7f:de:98:2e:cc:db:6f:
|
||||
e4:19:c7:41:43:59:ff:34:7b:82:06:80:01:ab:79:
|
||||
b3:40:d3:45:1f:52:2d:10:f9:55:40:a7:7a:61:f7:
|
||||
fd:9c:41:eb:d1:ec:7e:30:ca:1a:fa:0e:9e:0f:1e:
|
||||
50:93:9a:ca:55:ea:64:80:6e:bb:49:7d:12:15:d8:
|
||||
6f:a8:aa:3f:b9:10:24:6f:72:22:e9:4f:f3:a4:29:
|
||||
1e:4e:71:a6:82:af:39:78:a9
|
||||
00:d1:61:6d:94:0e:a2:7b:3e:ae:2c:d4:39:66:a5:
|
||||
ec:3a:d1:90:d1:85:fd:de:3c:1d:7d:cc:cd:fd:93:
|
||||
50:06:26:02:a7:89:e4:92:45:d5:96:ba:b0:04:6b:
|
||||
29:a9:93:ff:c9:d5:f2:5c:50:b5:1c:5a:1d:48:4f:
|
||||
eb:a9:bf:f9:28:24:a2:5e:da:08:d1:01:1a:1a:c8:
|
||||
00:35:d0:4a:51:46:f0:02:2b:89:3b:b2:aa:a9:68:
|
||||
33:ee:08:d4:61:06:62:e6:ea:53:f6:4a:13:49:66:
|
||||
67:03:82:22:08:28:2e:be:dd:81:91:28:a5:aa:89:
|
||||
78:41:33:3b:5d:65:b2:f7:0b
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
FB:77:D6:D0:84:A8:10:DF:FA:4E:A4:E0:F1:2A:BB:B4:80:FD:4F:3F
|
||||
X509v3 Authority Key Identifier:
|
||||
D3:07:CD:72:E6:BE:0A:5A:D8:E9:60:20:AF:C2:F2:36:7E:33:62:0B
|
||||
X509v3 Basic Constraints:
|
||||
X509v3 Subject Key Identifier:
|
||||
64:84:5E:B7:37:A2:82:F9:62:1A:01:00:FE:1B:B4:4B:F4:18:92:F6
|
||||
X509v3 Authority Key Identifier:
|
||||
48:70:75:D4:ED:6B:49:3B:53:DB:E9:A6:F9:9C:55:92:51:BD:96:E6
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
X509v3 Subject Alternative Name:
|
||||
X509v3 Subject Alternative Name:
|
||||
<EMPTY>
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Signature Value:
|
||||
6b:24:0f:2f:81:46:32:c4:c1:57:09:cd:64:6d:9f:50:ee:29:
|
||||
4d:a7:14:d0:a0:0c:ea:a6:dc:e5:15:52:9a:42:08:eb:a2:91:
|
||||
3c:ce:94:0e:f0:82:bc:fd:d7:23:d1:ad:d1:98:07:94:05:fa:
|
||||
ca:37:45:d7:f0:7d:aa:d2:ec:94:2b:8b:03:85:00:fb:af:1d:
|
||||
35:28:53:a8:1d:f8:44:e1:ea:48:3f:a4:2a:46:3b:f6:19:bf:
|
||||
30:df:b2:0e:8d:79:b0:0a:f5:34:c7:8a:6d:bf:58:39:9d:5d:
|
||||
a1:f5:35:a0:54:87:98:c6:5d:bf:ea:4e:46:f9:47:6d:d7:e6:
|
||||
5a:f3
|
||||
59:a5:81:1c:61:12:ad:1e:b8:3d:a5:e6:c2:dd:dd:8f:09:3c:
|
||||
8f:61:fc:96:e6:ff:70:d1:77:b0:b8:18:7f:f5:9e:e7:61:a1:
|
||||
cc:b6:53:75:d4:b3:a7:cb:77:1c:7f:e2:01:22:6b:30:44:df:
|
||||
e0:c2:9e:f6:56:a8:1e:13:0b:02:a7:fa:25:cb:f8:6c:0b:85:
|
||||
32:be:a7:1d:50:07:5d:76:0c:e5:ec:58:88:3e:ab:21:09:58:
|
||||
1f:af:06:26:80:77:48:1a:a4:37:50:35:e5:b3:d0:d0:4c:d7:
|
||||
ad:bb:29:2b:f5:eb:56:94:c0:8b:4d:69:37:f6:1c:d2:fd:87:
|
||||
d5:af
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICtTCCAh6gAwIBAgIBATANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MIIC5TCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECAwITXkgU3RhdGUxEDAOBgNVBAcMB015IENpdHkxDzANBgNVBAoMBk15
|
||||
IE9yZzEQMA4GA1UECwwHTXkgVW5pdDEQMA4GA1UEAwwHUk9PVCBDQTAgFw0yNDA1
|
||||
MDIwNTQ2MjRaGA8yMTI0MDQwODA1NDYyNFowaTELMAkGA1UEBhMCVVMxETAPBgNV
|
||||
BAgMCE15IFN0YXRlMRAwDgYDVQQHDAdNeSBDaXR5MQ8wDQYDVQQKDAZNeSBPcmcx
|
||||
EDAOBgNVBAsMB015IFVuaXQxEjAQBgNVBAMMCWNsaWVudF9jbjCBnzANBgkqhkiG
|
||||
9w0BAQEFAAOBjQAwgYkCgYEAvT8t0YZzbbUJnP9C+yeOB2mjttHHctHemBSlYZuD
|
||||
Ax3aVNHUDX/emC7M22/kGcdBQ1n/NHuCBoABq3mzQNNFH1ItEPlVQKd6Yff9nEHr
|
||||
0ex+MMoa+g6eDx5Qk5rKVepkgG67SX0SFdhvqKo/uRAkb3Ii6U/zpCkeTnGmgq85
|
||||
eKkCAwEAAaNtMGswHQYDVR0OBBYEFPt31tCEqBDf+k6k4PEqu7SA/U8/MB8GA1Ud
|
||||
IwQYMBaAFNMHzXLmvgpa2OlgIK/C8jZ+M2ILMAkGA1UdEwQCMAAwCQYDVR0RBAIw
|
||||
ADATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOBgQBrJA8vgUYy
|
||||
xMFXCc1kbZ9Q7ilNpxTQoAzqptzlFVKaQgjropE8zpQO8IK8/dcj0a3RmAeUBfrK
|
||||
N0XX8H2q0uyUK4sDhQD7rx01KFOoHfhE4epIP6QqRjv2Gb8w37IOjXmwCvU0x4pt
|
||||
v1g5nV2h9TWgVIeYxl2/6k5G+Udt1+Za8w==
|
||||
IE9yZzEQMA4GA1UECwwHTXkgVW5pdDEQMA4GA1UEAwwHUk9PVCBDQTAgFw0yNDEw
|
||||
MjcwMDQzMzFaGA8yMTI0MTAwMzAwNDMzMVowgZgxCzAJBgNVBAYTAlVTMREwDwYD
|
||||
VQQIDAhNeSBTdGF0ZTEQMA4GA1UEBwwHTXkgQ2l0eTEPMA0GA1UECgwGTXkgT3Jn
|
||||
MRAwDgYDVQQLDAdNeSBVbml0MRMwEQYDVQQDDApjbGllbnRfY24yMRIwEAYDVQQD
|
||||
DAljbGllbnRfY24xGDAWBgkrBgEEAYPCUwIMCWNsaWVudF9pZDCBnzANBgkqhkiG
|
||||
9w0BAQEFAAOBjQAwgYkCgYEA0WFtlA6iez6uLNQ5ZqXsOtGQ0YX93jwdfczN/ZNQ
|
||||
BiYCp4nkkkXVlrqwBGspqZP/ydXyXFC1HFodSE/rqb/5KCSiXtoI0QEaGsgANdBK
|
||||
UUbwAiuJO7KqqWgz7gjUYQZi5upT9koTSWZnA4IiCCguvt2BkSilqol4QTM7XWWy
|
||||
9wsCAwEAAaNtMGswHQYDVR0OBBYEFGSEXrc3ooL5YhoBAP4btEv0GJL2MB8GA1Ud
|
||||
IwQYMBaAFEhwddTta0k7U9vppvmcVZJRvZbmMAkGA1UdEwQCMAAwCQYDVR0RBAIw
|
||||
ADATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOBgQBZpYEcYRKt
|
||||
Hrg9pebC3d2PCTyPYfyW5v9w0XewuBh/9Z7nYaHMtlN11LOny3ccf+IBImswRN/g
|
||||
wp72VqgeEwsCp/oly/hsC4UyvqcdUAdddgzl7FiIPqshCVgfrwYmgHdIGqQ3UDXl
|
||||
s9DQTNetuykr9etWlMCLTWk39hzS/YfVrw==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
|
@ -374,66 +387,199 @@ v1g5nV2h9TWgVIeYxl2/6k5G+Udt1+Za8w==
|
|||
Version: 3 (0x2)
|
||||
Serial Number: 7 (0x7)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C = US, ST = My State, L = My City, O = My Org, OU = My Unit, CN = ROOT CA
|
||||
Issuer: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=ROOT CA
|
||||
Validity
|
||||
Not Before: May 2 05:47:31 2024 GMT
|
||||
Not After : Apr 8 05:47:31 2124 GMT
|
||||
Subject: C = US, ST = My State, L = My City, O = My Org, OU = My Unit, CN = 127.0.0.1
|
||||
Not Before: Oct 6 20:38:02 2024 GMT
|
||||
Not After : Sep 12 20:38:02 2124 GMT
|
||||
Subject: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=127.0.0.1
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:9d:1f:c3:9e:ac:51:92:27:df:2a:3a:48:b7:59:
|
||||
40:23:a5:c3:a1:61:71:7a:00:df:d5:8b:a2:8a:7c:
|
||||
54:f0:19:69:fe:ae:19:a3:e1:eb:1e:1b:39:2c:61:
|
||||
fb:7b:21:10:81:b2:ef:29:94:b6:14:6f:ca:eb:4d:
|
||||
f3:f6:84:93:5f:51:2c:7a:ab:9f:34:05:15:62:c4:
|
||||
55:54:2e:75:b9:26:d1:0e:c5:63:41:e5:36:02:3f:
|
||||
1c:5f:fc:1b:07:20:d2:1c:70:a5:a1:e8:08:1d:8f:
|
||||
4c:c3:57:e0:54:72:a6:c9:24:1b:b0:fa:0d:86:f5:
|
||||
26:1f:20:e5:1c:1c:c3:8f:d3
|
||||
00:b6:d5:2f:a6:7a:78:5d:40:a6:0d:76:6f:e9:9d:
|
||||
54:6d:d9:e9:d6:32:00:f2:8a:fb:da:87:be:05:07:
|
||||
b4:58:ab:88:25:f8:38:e7:50:25:23:47:99:8f:3c:
|
||||
ff:8a:cc:61:7c:21:db:39:c9:81:f6:0c:f2:22:a8:
|
||||
19:65:7a:ae:c6:32:74:63:4d:a5:14:fa:b5:04:ab:
|
||||
a4:83:c5:0f:26:38:b3:65:9d:68:bb:4f:55:e4:0b:
|
||||
e5:71:49:dd:5b:b8:a0:ed:7d:13:6f:29:03:44:20:
|
||||
d0:2d:9c:44:e4:0e:8b:d7:71:79:fe:35:cd:6c:7c:
|
||||
79:a4:01:08:ae:9e:95:46:d9
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
F2:AE:B7:50:D5:02:C1:E9:8D:38:0E:76:A5:D8:24:0B:1C:DB:08:0E
|
||||
X509v3 Authority Key Identifier:
|
||||
D3:07:CD:72:E6:BE:0A:5A:D8:E9:60:20:AF:C2:F2:36:7E:33:62:0B
|
||||
X509v3 Basic Constraints:
|
||||
X509v3 Subject Key Identifier:
|
||||
CA:72:DA:A3:17:BB:56:CC:14:A9:BA:12:F2:88:7F:F4:15:69:33:CB
|
||||
X509v3 Authority Key Identifier:
|
||||
48:70:75:D4:ED:6B:49:3B:53:DB:E9:A6:F9:9C:55:92:51:BD:96:E6
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
X509v3 Subject Alternative Name:
|
||||
X509v3 Subject Alternative Name:
|
||||
<EMPTY>
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Signature Value:
|
||||
3f:3d:d1:5d:d5:9f:c1:ab:6e:ba:c1:c2:1b:63:1a:a8:4f:d9:
|
||||
df:03:13:ff:6d:a8:ed:c9:8d:19:a6:8f:a6:e2:a8:23:a0:f7:
|
||||
5d:5e:22:01:d1:29:9b:d0:95:75:66:46:f2:51:a7:08:1c:8c:
|
||||
aa:ca:4a:57:d8:ab:ed:1b:b3:77:25:58:38:1f:89:e0:a4:13:
|
||||
0a:f2:99:d5:3d:24:00:08:06:7e:b3:1a:b0:0b:07:33:a7:c7:
|
||||
ff:f8:ef:bc:7c:c9:2e:aa:3f:7a:3e:8e:8a:49:cf:a4:5a:b5:
|
||||
41:07:57:f1:36:f4:57:dc:6e:3f:70:38:0d:4e:71:9c:24:20:
|
||||
b4:36
|
||||
08:14:37:cd:ec:d6:4e:81:d2:d7:09:ba:5a:50:84:6a:1b:f2:
|
||||
02:49:44:94:5d:e3:41:48:09:dc:88:0b:37:d6:e9:c7:b6:4b:
|
||||
42:58:b3:cb:81:5b:a6:0d:78:47:1b:4a:5a:5f:d5:14:4c:37:
|
||||
bd:b6:64:c4:d5:ac:17:d0:6c:2d:f5:1b:aa:d8:de:27:f1:1e:
|
||||
26:42:dd:45:90:ef:97:0b:e6:c9:01:c5:4b:7c:c3:81:18:c6:
|
||||
28:d9:8a:f5:a5:8c:b4:ec:75:c2:b8:43:83:d0:db:09:e1:58:
|
||||
a6:2a:65:52:97:0b:d0:d6:c7:43:8f:10:63:23:b4:ce:c9:15:
|
||||
4d:4a
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICtTCCAh6gAwIBAgIBBzANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECAwITXkgU3RhdGUxEDAOBgNVBAcMB015IENpdHkxDzANBgNVBAoMBk15
|
||||
IE9yZzEQMA4GA1UECwwHTXkgVW5pdDEQMA4GA1UEAwwHUk9PVCBDQTAgFw0yNDA1
|
||||
MDIwNTQ3MzFaGA8yMTI0MDQwODA1NDczMVowaTELMAkGA1UEBhMCVVMxETAPBgNV
|
||||
IE9yZzEQMA4GA1UECwwHTXkgVW5pdDEQMA4GA1UEAwwHUk9PVCBDQTAgFw0yNDEw
|
||||
MDYyMDM4MDJaGA8yMTI0MDkxMjIwMzgwMlowaTELMAkGA1UEBhMCVVMxETAPBgNV
|
||||
BAgMCE15IFN0YXRlMRAwDgYDVQQHDAdNeSBDaXR5MQ8wDQYDVQQKDAZNeSBPcmcx
|
||||
EDAOBgNVBAsMB015IFVuaXQxEjAQBgNVBAMMCTEyNy4wLjAuMTCBnzANBgkqhkiG
|
||||
9w0BAQEFAAOBjQAwgYkCgYEAnR/DnqxRkiffKjpIt1lAI6XDoWFxegDf1YuiinxU
|
||||
8Blp/q4Zo+HrHhs5LGH7eyEQgbLvKZS2FG/K603z9oSTX1EsequfNAUVYsRVVC51
|
||||
uSbRDsVjQeU2Aj8cX/wbByDSHHCloegIHY9Mw1fgVHKmySQbsPoNhvUmHyDlHBzD
|
||||
j9MCAwEAAaNtMGswHQYDVR0OBBYEFPKut1DVAsHpjTgOdqXYJAsc2wgOMB8GA1Ud
|
||||
IwQYMBaAFNMHzXLmvgpa2OlgIK/C8jZ+M2ILMAkGA1UdEwQCMAAwCQYDVR0RBAIw
|
||||
ADATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOBgQA/PdFd1Z/B
|
||||
q266wcIbYxqoT9nfAxP/bajtyY0Zpo+m4qgjoPddXiIB0Smb0JV1ZkbyUacIHIyq
|
||||
ykpX2KvtG7N3JVg4H4ngpBMK8pnVPSQACAZ+sxqwCwczp8f/+O+8fMkuqj96Po6K
|
||||
Sc+kWrVBB1fxNvRX3G4/cDgNTnGcJCC0Ng==
|
||||
9w0BAQEFAAOBjQAwgYkCgYEAttUvpnp4XUCmDXZv6Z1Ubdnp1jIA8or72oe+BQe0
|
||||
WKuIJfg451AlI0eZjzz/isxhfCHbOcmB9gzyIqgZZXquxjJ0Y02lFPq1BKukg8UP
|
||||
JjizZZ1ou09V5AvlcUndW7ig7X0TbykDRCDQLZxE5A6L13F5/jXNbHx5pAEIrp6V
|
||||
RtkCAwEAAaNtMGswHQYDVR0OBBYEFMpy2qMXu1bMFKm6EvKIf/QVaTPLMB8GA1Ud
|
||||
IwQYMBaAFEhwddTta0k7U9vppvmcVZJRvZbmMAkGA1UdEwQCMAAwCQYDVR0RBAIw
|
||||
ADATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOBgQAIFDfN7NZO
|
||||
gdLXCbpaUIRqG/ICSUSUXeNBSAnciAs31unHtktCWLPLgVumDXhHG0paX9UUTDe9
|
||||
tmTE1awX0Gwt9Ruq2N4n8R4mQt1FkO+XC+bJAcVLfMOBGMYo2Yr1pYy07HXCuEOD
|
||||
0NsJ4VimKmVSlwvQ1sdDjxBjI7TOyRVNSg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
/*
|
||||
openssl genrsa -out ca.key 4096 && \
|
||||
OID_CONF="oid_section = my_oids\n\n" && \
|
||||
OID_CONF="${OID_CONF}[ my_oids ]\n" && \
|
||||
OID_CONF="${OID_CONF}kube_uid=1.3.6.1.4.1.57683.2\n" && \
|
||||
openssl req -new -x509 -days 36500 \
|
||||
-config <(printf "${OID_CONF}") \
|
||||
-sha256 -key ca.key \
|
||||
-out ca.crt \
|
||||
-subj "/C=US/ST=My State/L=My City/CN=caWithMultiUIDs"/kube_uid=client_uid1/kube_uid=client_uid2 && \
|
||||
openssl x509 -in ca.crt -text
|
||||
*/
|
||||
|
||||
// A certificate with multiple UIDs.
|
||||
caWithMultiUIDs = `Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number:
|
||||
2d:92:d9:46:70:49:59:58:3c:d0:12:06:ed:3e:ee:15:f3:17:62:d4
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=My State, L=My City, CN=caWithMultiUIDs, 1.3.6.1.4.1.57683.2=client_uid1, 1.3.6.1.4.1.57683.2=client_uid2
|
||||
Validity
|
||||
Not Before: Nov 2 19:44:52 2024 GMT
|
||||
Not After : Oct 9 19:44:52 2124 GMT
|
||||
Subject: C=US, ST=My State, L=My City, CN=caWithMultiUIDs, 1.3.6.1.4.1.57683.2=client_uid1, 1.3.6.1.4.1.57683.2=client_uid2
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (4096 bit)
|
||||
Modulus:
|
||||
00:a4:c1:9d:6e:a2:e7:af:07:14:7f:5f:00:60:92:
|
||||
1b:ec:51:77:3d:ac:93:4d:a8:ac:ee:15:93:9d:5f:
|
||||
62:44:5d:97:70:a2:c9:63:59:79:79:84:86:98:2e:
|
||||
4b:cb:fd:99:2e:e7:0c:7d:e2:c3:65:f5:80:5d:bb:
|
||||
38:3f:0e:09:11:40:9f:56:b0:91:04:2b:66:02:a4:
|
||||
28:9a:fc:a4:e9:0d:6b:f0:31:41:90:95:f2:4a:d7:
|
||||
af:5d:50:f8:28:1d:6d:2b:01:e4:38:bf:15:9c:9e:
|
||||
93:2b:44:7e:29:33:c0:96:66:4f:5f:43:74:c2:eb:
|
||||
c9:45:e1:16:22:33:b7:d9:93:5f:1c:af:bb:95:f5:
|
||||
64:15:53:15:c3:a6:4c:0e:2c:6e:f7:45:c1:c5:4a:
|
||||
c3:8c:29:c3:42:aa:e1:eb:53:da:c2:0d:9c:dc:5a:
|
||||
e1:01:9c:59:b4:43:1c:2b:c5:ff:d7:cf:cf:4c:76:
|
||||
a4:7b:ce:00:a1:78:4a:38:0f:f3:ab:48:0f:5e:86:
|
||||
49:7f:24:85:71:db:c8:3c:7e:dc:f5:26:5f:54:aa:
|
||||
a6:e2:41:83:3c:5b:eb:e4:f0:e4:76:78:a3:82:68:
|
||||
46:b1:50:54:a7:d0:c4:aa:12:4d:fd:7f:b4:c2:92:
|
||||
f1:d0:2d:0a:e9:df:9b:0f:95:88:94:3f:77:35:57:
|
||||
e6:8e:a7:b1:50:9a:80:51:62:19:49:9b:2c:81:f5:
|
||||
97:b3:f4:23:b7:94:9e:96:2e:22:d3:6e:6a:56:50:
|
||||
77:1c:ad:3a:60:52:eb:b6:ba:34:fe:f5:1e:ba:fd:
|
||||
e3:dc:b8:9d:c1:59:b2:42:fa:5e:88:d3:fe:4a:1a:
|
||||
3d:1d:a6:55:ce:af:dc:71:e7:8a:4a:dd:37:00:0a:
|
||||
64:79:14:b3:29:ed:7c:4a:42:c6:f1:38:72:e5:36:
|
||||
19:64:9f:3c:23:8a:b1:ee:18:a7:7e:cf:12:48:53:
|
||||
0c:27:fb:12:82:62:bc:9a:7f:fc:5d:97:ae:2d:38:
|
||||
bd:ff:74:23:1b:62:1c:2e:4a:26:7e:85:6c:6d:82:
|
||||
01:96:95:86:15:1c:db:40:d9:01:d6:df:68:6d:e9:
|
||||
5b:0b:6d:cc:6f:40:95:34:f8:b4:1c:13:ab:95:0f:
|
||||
5b:0b:dc:65:93:87:a0:4c:0d:e6:b0:0a:a6:7e:ac:
|
||||
0b:04:6d:f9:ee:42:7b:14:0f:b4:22:53:e2:58:bc:
|
||||
6f:05:41:f0:d3:3a:98:1a:c4:3a:6e:0b:a9:85:fe:
|
||||
e9:4d:7a:50:b4:4d:28:bd:fc:6a:78:e2:b8:9d:cd:
|
||||
15:a3:ac:03:a9:94:38:8e:94:b1:00:12:fc:1f:70:
|
||||
1b:b8:f4:1a:7b:a9:cc:17:c5:2a:42:c6:40:c7:b4:
|
||||
40:ba:fd
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
87:40:3F:E7:DD:73:C8:A6:62:6A:B2:8E:AF:82:52:F6:8D:26:C1:68
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Signature Value:
|
||||
3c:9f:c8:86:2b:97:45:ab:10:44:a2:f1:b7:06:d1:2d:54:a3:
|
||||
34:85:40:e2:6f:8b:6f:7f:84:a4:e5:e5:23:6d:f9:e3:b5:63:
|
||||
55:23:f5:14:7f:c1:b9:b4:68:86:c4:75:d9:fa:03:fc:c8:aa:
|
||||
26:a7:38:44:be:7d:c6:3c:1d:60:7b:31:83:6b:76:43:95:f3:
|
||||
2a:9a:2d:71:86:ea:fb:8a:2c:2a:f2:7e:79:a5:78:cf:cf:aa:
|
||||
92:67:1a:01:2b:4f:32:a9:2e:48:10:27:89:77:67:1c:ba:97:
|
||||
3d:05:2e:38:ff:6c:a6:9b:13:2a:20:9c:8e:b3:32:3f:11:51:
|
||||
5f:28:3d:c3:21:64:8f:7a:0b:df:62:8e:7a:27:57:86:90:cd:
|
||||
58:69:4f:51:9d:b3:0e:cb:47:68:1e:2f:8e:a4:58:9a:5b:f2:
|
||||
a9:51:0b:2f:22:8a:14:b7:69:d1:22:bf:10:a8:59:ca:0e:7f:
|
||||
16:18:80:0f:e6:42:5a:7d:2f:b0:2f:c5:c1:35:9d:99:75:57:
|
||||
c4:0d:0d:be:da:23:9e:82:d0:14:c7:12:07:1d:b7:9d:44:09:
|
||||
84:83:d0:31:fc:aa:c5:bb:f3:ba:e9:a0:60:01:df:5d:4b:f6:
|
||||
73:5b:98:62:a0:82:ae:5c:8d:41:6b:e7:d0:62:c2:70:80:51:
|
||||
43:8f:6d:f5:52:3e:1c:a3:18:9b:c1:12:eb:f3:f0:89:59:3b:
|
||||
44:c1:3c:33:fa:30:99:86:7a:1a:01:e7:8a:1e:41:04:7f:96:
|
||||
9d:63:c8:93:ee:76:05:15:8d:16:59:45:e0:99:36:e2:16:68:
|
||||
ea:54:13:3b:98:12:2e:30:84:c5:0f:c1:63:10:0c:a6:d0:93:
|
||||
73:54:c7:5d:10:aa:3b:9a:4d:0a:82:e8:e2:0f:3a:cb:93:a1:
|
||||
97:1c:d7:51:eb:ba:be:ed:84:cc:76:a7:73:e0:9a:18:b5:9b:
|
||||
eb:d4:fc:a7:b2:3a:90:fa:71:d8:c8:e3:88:f9:25:44:a4:63:
|
||||
f1:4d:ab:d5:1e:04:62:d9:40:a0:ea:e7:f7:78:03:12:90:c4:
|
||||
02:58:fd:ef:62:2d:78:85:e5:f6:25:20:85:8e:e5:52:a2:0d:
|
||||
4e:9d:a1:4a:1b:4b:17:9a:ba:9c:42:08:0b:f3:85:8c:8d:00:
|
||||
76:b9:48:4f:11:cb:d2:42:03:06:a5:2c:38:15:40:39:ec:3b:
|
||||
c4:ca:bf:07:c0:33:54:6e:6f:7a:21:f6:47:1f:95:3a:24:56:
|
||||
06:73:c2:84:1e:c1:9d:6c:02:81:61:87:3a:58:f7:62:fb:55:
|
||||
c9:34:9c:c2:52:dd:8c:3a:51:a0:1b:d2:ab:5e:d3:50:a2:e5:
|
||||
2a:ab:85:95:de:a0:a2:fc
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFuzCCA6OgAwIBAgIULZLZRnBJWVg80BIG7T7uFfMXYtQwDQYJKoZIhvcNAQEL
|
||||
BQAwgYQxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNeSBTdGF0ZTEQMA4GA1UEBwwH
|
||||
TXkgQ2l0eTEYMBYGA1UEAwwPY2FXaXRoTXVsdGlVSURzMRowGAYJKwYBBAGDwlMC
|
||||
DAtjbGllbnRfdWlkMTEaMBgGCSsGAQQBg8JTAgwLY2xpZW50X3VpZDIwIBcNMjQx
|
||||
MTAyMTk0NDUyWhgPMjEyNDEwMDkxOTQ0NTJaMIGEMQswCQYDVQQGEwJVUzERMA8G
|
||||
A1UECAwITXkgU3RhdGUxEDAOBgNVBAcMB015IENpdHkxGDAWBgNVBAMMD2NhV2l0
|
||||
aE11bHRpVUlEczEaMBgGCSsGAQQBg8JTAgwLY2xpZW50X3VpZDExGjAYBgkrBgEE
|
||||
AYPCUwIMC2NsaWVudF91aWQyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
|
||||
AgEApMGdbqLnrwcUf18AYJIb7FF3PayTTais7hWTnV9iRF2XcKLJY1l5eYSGmC5L
|
||||
y/2ZLucMfeLDZfWAXbs4Pw4JEUCfVrCRBCtmAqQomvyk6Q1r8DFBkJXyStevXVD4
|
||||
KB1tKwHkOL8VnJ6TK0R+KTPAlmZPX0N0wuvJReEWIjO32ZNfHK+7lfVkFVMVw6ZM
|
||||
Dixu90XBxUrDjCnDQqrh61Pawg2c3FrhAZxZtEMcK8X/18/PTHake84AoXhKOA/z
|
||||
q0gPXoZJfySFcdvIPH7c9SZfVKqm4kGDPFvr5PDkdnijgmhGsVBUp9DEqhJN/X+0
|
||||
wpLx0C0K6d+bD5WIlD93NVfmjqexUJqAUWIZSZssgfWXs/Qjt5Seli4i025qVlB3
|
||||
HK06YFLrtro0/vUeuv3j3LidwVmyQvpeiNP+Sho9HaZVzq/cceeKSt03AApkeRSz
|
||||
Ke18SkLG8Thy5TYZZJ88I4qx7hinfs8SSFMMJ/sSgmK8mn/8XZeuLTi9/3QjG2Ic
|
||||
LkomfoVsbYIBlpWGFRzbQNkB1t9obelbC23Mb0CVNPi0HBOrlQ9bC9xlk4egTA3m
|
||||
sAqmfqwLBG357kJ7FA+0IlPiWLxvBUHw0zqYGsQ6bguphf7pTXpQtE0ovfxqeOK4
|
||||
nc0Vo6wDqZQ4jpSxABL8H3AbuPQae6nMF8UqQsZAx7RAuv0CAwEAAaMhMB8wHQYD
|
||||
VR0OBBYEFIdAP+fdc8imYmqyjq+CUvaNJsFoMA0GCSqGSIb3DQEBCwUAA4ICAQA8
|
||||
n8iGK5dFqxBEovG3BtEtVKM0hUDib4tvf4Sk5eUjbfnjtWNVI/UUf8G5tGiGxHXZ
|
||||
+gP8yKompzhEvn3GPB1gezGDa3ZDlfMqmi1xhur7iiwq8n55pXjPz6qSZxoBK08y
|
||||
qS5IECeJd2ccupc9BS44/2ymmxMqIJyOszI/EVFfKD3DIWSPegvfYo56J1eGkM1Y
|
||||
aU9RnbMOy0doHi+OpFiaW/KpUQsvIooUt2nRIr8QqFnKDn8WGIAP5kJafS+wL8XB
|
||||
NZ2ZdVfEDQ2+2iOegtAUxxIHHbedRAmEg9Ax/KrFu/O66aBgAd9dS/ZzW5hioIKu
|
||||
XI1Ba+fQYsJwgFFDj231Uj4coxibwRLr8/CJWTtEwTwz+jCZhnoaAeeKHkEEf5ad
|
||||
Y8iT7nYFFY0WWUXgmTbiFmjqVBM7mBIuMITFD8FjEAym0JNzVMddEKo7mk0Kguji
|
||||
DzrLk6GXHNdR67q+7YTMdqdz4JoYtZvr1PynsjqQ+nHYyOOI+SVEpGPxTavVHgRi
|
||||
2UCg6uf3eAMSkMQCWP3vYi14heX2JSCFjuVSog1OnaFKG0sXmrqcQggL84WMjQB2
|
||||
uUhPEcvSQgMGpSw4FUA57DvEyr8HwDNUbm96IfZHH5U6JFYGc8KEHsGdbAKBYYc6
|
||||
WPdi+1XJNJzCUt2MOlGgG9KrXtNQouUqq4WV3qCi/A==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
/*
|
||||
openssl genrsa -out ca.key 4096
|
||||
openssl req -new -x509 -days 36500 \
|
||||
|
@ -584,6 +730,9 @@ func TestX509(t *testing.T) {
|
|||
ExpectOK bool
|
||||
ExpectResponse *authenticator.Response
|
||||
ExpectErr bool
|
||||
ExpectErrMsg *regexp.Regexp
|
||||
|
||||
setupFunc func(t *testing.T)
|
||||
}{
|
||||
"non-tls": {
|
||||
Insecure: true,
|
||||
|
@ -623,14 +772,35 @@ func TestX509(t *testing.T) {
|
|||
Name: "127.0.0.1",
|
||||
Groups: []string{"My Org"},
|
||||
Extra: map[string][]string{
|
||||
user.CredentialIDKey: {"X509SHA256=92209d1e0dd36a018f244f5e1b88e2d47b049e9cfcd4b7c87c65875866872230"},
|
||||
user.CredentialIDKey: {"X509SHA256=04adf2b65e6325a8c467256eb3a9a373d818398d9a1f1d9eca1cbc2c237fe75f"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"common name": {
|
||||
"common name and UID": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectOK: true,
|
||||
ExpectResponse: &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "client_cn",
|
||||
Groups: []string{"My Org"},
|
||||
UID: "client_id",
|
||||
Extra: map[string][]string{
|
||||
user.CredentialIDKey: {"X509SHA256=0a016b6c2ff14c5431e4a2b448e941fcaa21fb3d7ad105e9a53d4e8ce12824f0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectErr: false,
|
||||
setupFunc: func(t *testing.T) {
|
||||
t.Helper()
|
||||
},
|
||||
},
|
||||
"common name and empty UID with feature gate disabled": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
@ -641,11 +811,50 @@ func TestX509(t *testing.T) {
|
|||
Name: "client_cn",
|
||||
Groups: []string{"My Org"},
|
||||
Extra: map[string][]string{
|
||||
user.CredentialIDKey: {"X509SHA256=dd0a6a295055fa94455c522b0d54ef0499186f454a7cf978b8b346dc35b254f7"},
|
||||
user.CredentialIDKey: {"X509SHA256=0a016b6c2ff14c5431e4a2b448e941fcaa21fb3d7ad105e9a53d4e8ce12824f0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectErr: false,
|
||||
setupFunc: func(t *testing.T) {
|
||||
t.Helper()
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.AllowParsingUserUIDFromCertAuth, false)
|
||||
},
|
||||
},
|
||||
"ca with empty UID": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: toCertWithUIDValue(t, clientCNCert, ""),
|
||||
User: CommonNameUserConversion,
|
||||
ExpectOK: false,
|
||||
ExpectErr: true,
|
||||
ExpectErrMsg: regexp.MustCompile("UID cannot be an empty string"),
|
||||
setupFunc: func(t *testing.T) {
|
||||
t.Helper()
|
||||
},
|
||||
},
|
||||
"ca with non-string UID": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: toCertWithUIDValue(t, clientCNCert, asn1.RawValue{Tag: 16, Bytes: []byte("custom_value")}),
|
||||
User: CommonNameUserConversion,
|
||||
ExpectOK: false,
|
||||
ExpectErr: true,
|
||||
ExpectErrMsg: regexp.MustCompile("unable to parse UID into a string"),
|
||||
setupFunc: func(t *testing.T) {
|
||||
t.Helper()
|
||||
},
|
||||
},
|
||||
"ca with multiple UIDs": {
|
||||
Opts: x509.VerifyOptions{
|
||||
Roots: getRootCertPoolFor(t, caWithMultiUIDs),
|
||||
},
|
||||
Certs: getCerts(t, caWithMultiUIDs),
|
||||
User: CommonNameUserConversion,
|
||||
ExpectOK: false,
|
||||
ExpectErr: true,
|
||||
ExpectErrMsg: regexp.MustCompile("expected 1 UID, but found multiple"),
|
||||
setupFunc: func(t *testing.T) {
|
||||
t.Helper()
|
||||
},
|
||||
},
|
||||
"ca with multiple organizations": {
|
||||
Opts: x509.VerifyOptions{
|
||||
|
@ -744,6 +953,10 @@ func TestX509(t *testing.T) {
|
|||
|
||||
for k, testCase := range testCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
if testCase.setupFunc != nil {
|
||||
testCase.setupFunc(t)
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
if !testCase.Insecure {
|
||||
req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
|
||||
|
@ -760,6 +973,9 @@ func TestX509(t *testing.T) {
|
|||
if !testCase.ExpectErr && err != nil {
|
||||
t.Fatalf("Got unexpected error: %v", err)
|
||||
}
|
||||
if testCase.ExpectErrMsg != nil && err != nil {
|
||||
assert.Regexp(t, testCase.ExpectErrMsg, err.Error())
|
||||
}
|
||||
|
||||
if testCase.ExpectOK != ok {
|
||||
t.Fatalf("Expected ok=%v, got %v", testCase.ExpectOK, ok)
|
||||
|
@ -956,7 +1172,7 @@ func getCertsFromFile(t *testing.T, names ...string) []*x509.Certificate {
|
|||
certs := []*x509.Certificate{}
|
||||
for _, name := range names {
|
||||
filename := "testdata/" + name + ".pem"
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading %s: %v", filename, err)
|
||||
}
|
||||
|
@ -985,6 +1201,29 @@ func getCerts(t *testing.T, pemData ...string) []*x509.Certificate {
|
|||
return certs
|
||||
}
|
||||
|
||||
// Modifies the cert's Subject to use custom value for the UID.
|
||||
func toCertWithUIDValue(t *testing.T, pemData string, val any) []*x509.Certificate {
|
||||
cert := getCert(t, pemData)
|
||||
|
||||
oid := asn1util.X509UID()
|
||||
attrs := []pkix.AttributeTypeAndValue{}
|
||||
for _, attr := range cert.Subject.Names {
|
||||
if !attr.Type.Equal(oid) {
|
||||
attrs = append(attrs, attr)
|
||||
}
|
||||
}
|
||||
cert.Subject.Names = attrs
|
||||
cert.Subject.Names = append(
|
||||
cert.Subject.Names,
|
||||
pkix.AttributeTypeAndValue{
|
||||
Type: asn1util.X509UID(),
|
||||
Value: val,
|
||||
},
|
||||
)
|
||||
|
||||
return []*x509.Certificate{cert}
|
||||
}
|
||||
|
||||
func TestCertificateIdentifier(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
|
@ -994,7 +1233,7 @@ func TestCertificateIdentifier(t *testing.T) {
|
|||
{
|
||||
name: "client cert",
|
||||
cert: getCert(t, clientCNCert),
|
||||
expectedIdentifier: "SN=1, SKID=FB:77:D6:D0:84:A8:10:DF:FA:4E:A4:E0:F1:2A:BB:B4:80:FD:4F:3F, AKID=D3:07:CD:72:E6:BE:0A:5A:D8:E9:60:20:AF:C2:F2:36:7E:33:62:0B",
|
||||
expectedIdentifier: "SN=1, SKID=64:84:5E:B7:37:A2:82:F9:62:1A:01:00:FE:1B:B4:4B:F4:18:92:F6, AKID=48:70:75:D4:ED:6B:49:3B:53:DB:E9:A6:F9:9C:55:92:51:BD:96:E6",
|
||||
},
|
||||
{
|
||||
name: "nil serial",
|
||||
|
@ -1003,7 +1242,7 @@ func TestCertificateIdentifier(t *testing.T) {
|
|||
c.SerialNumber = nil
|
||||
return c
|
||||
}(),
|
||||
expectedIdentifier: "SN=<nil>, SKID=FB:77:D6:D0:84:A8:10:DF:FA:4E:A4:E0:F1:2A:BB:B4:80:FD:4F:3F, AKID=D3:07:CD:72:E6:BE:0A:5A:D8:E9:60:20:AF:C2:F2:36:7E:33:62:0B",
|
||||
expectedIdentifier: "SN=<nil>, SKID=64:84:5E:B7:37:A2:82:F9:62:1A:01:00:FE:1B:B4:4B:F4:18:92:F6, AKID=48:70:75:D4:ED:6B:49:3B:53:DB:E9:A6:F9:9C:55:92:51:BD:96:E6",
|
||||
},
|
||||
{
|
||||
name: "empty SKID",
|
||||
|
@ -1012,7 +1251,7 @@ func TestCertificateIdentifier(t *testing.T) {
|
|||
c.SubjectKeyId = nil
|
||||
return c
|
||||
}(),
|
||||
expectedIdentifier: "SN=1, SKID=, AKID=D3:07:CD:72:E6:BE:0A:5A:D8:E9:60:20:AF:C2:F2:36:7E:33:62:0B",
|
||||
expectedIdentifier: "SN=1, SKID=, AKID=48:70:75:D4:ED:6B:49:3B:53:DB:E9:A6:F9:9C:55:92:51:BD:96:E6",
|
||||
},
|
||||
{
|
||||
name: "empty AKID",
|
||||
|
@ -1021,7 +1260,7 @@ func TestCertificateIdentifier(t *testing.T) {
|
|||
c.AuthorityKeyId = nil
|
||||
return c
|
||||
}(),
|
||||
expectedIdentifier: "SN=1, SKID=FB:77:D6:D0:84:A8:10:DF:FA:4E:A4:E0:F1:2A:BB:B4:80:FD:4F:3F, AKID=",
|
||||
expectedIdentifier: "SN=1, SKID=64:84:5E:B7:37:A2:82:F9:62:1A:01:00:FE:1B:B4:4B:F4:18:92:F6, AKID=",
|
||||
},
|
||||
{
|
||||
name: "self-signed",
|
||||
|
|
|
@ -33,7 +33,6 @@ import (
|
|||
"golang.org/x/sync/singleflight"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/warning"
|
||||
|
@ -199,12 +198,9 @@ func (a *cachedTokenAuthenticator) doAuthenticateToken(ctx context.Context, toke
|
|||
|
||||
ctx = audit.WithAuditContext(ctx)
|
||||
ac := audit.AuditContextFrom(ctx)
|
||||
// since this is shared work between multiple requests, we have no way of knowing if any
|
||||
// particular request supports audit annotations. thus we always attempt to record them.
|
||||
ac.Event.Level = auditinternal.LevelMetadata
|
||||
|
||||
record.resp, record.ok, record.err = a.authenticator.AuthenticateToken(ctx, token)
|
||||
record.annotations = ac.Event.Annotations
|
||||
record.annotations = ac.GetEventAnnotations()
|
||||
record.warnings = recorder.extractWarnings()
|
||||
|
||||
if !a.cacheErrs && record.err != nil {
|
||||
|
|
|
@ -35,7 +35,6 @@ import (
|
|||
|
||||
utilrand "k8s.io/apimachinery/pkg/util/rand"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
|
@ -306,7 +305,7 @@ func TestCachedAuditAnnotations(t *testing.T) {
|
|||
ctx := withAudit(context.Background())
|
||||
_, _, _ = a.AuthenticateToken(ctx, "token")
|
||||
|
||||
allAnnotations <- audit.AuditEventFrom(ctx).Annotations
|
||||
allAnnotations <- audit.AuditContextFrom(ctx).GetEventAnnotations()
|
||||
}()
|
||||
}
|
||||
|
||||
|
@ -343,7 +342,7 @@ func TestCachedAuditAnnotations(t *testing.T) {
|
|||
for i := 0; i < cap(allAnnotations); i++ {
|
||||
ctx := withAudit(context.Background())
|
||||
_, _, _ = a.AuthenticateToken(ctx, "token")
|
||||
allAnnotations = append(allAnnotations, audit.AuditEventFrom(ctx).Annotations)
|
||||
allAnnotations = append(allAnnotations, audit.AuditContextFrom(ctx).GetEventAnnotations())
|
||||
}
|
||||
|
||||
if len(allAnnotations) != cap(allAnnotations) {
|
||||
|
@ -370,14 +369,14 @@ func TestCachedAuditAnnotations(t *testing.T) {
|
|||
|
||||
ctx1 := withAudit(context.Background())
|
||||
_, _, _ = a.AuthenticateToken(ctx1, "token1")
|
||||
annotations1 := audit.AuditEventFrom(ctx1).Annotations
|
||||
annotations1 := audit.AuditContextFrom(ctx1).GetEventAnnotations()
|
||||
|
||||
// guarantee different now times
|
||||
time.Sleep(time.Second)
|
||||
|
||||
ctx2 := withAudit(context.Background())
|
||||
_, _, _ = a.AuthenticateToken(ctx2, "token2")
|
||||
annotations2 := audit.AuditEventFrom(ctx2).Annotations
|
||||
annotations2 := audit.AuditContextFrom(ctx2).GetEventAnnotations()
|
||||
|
||||
if ok := len(annotations1) == 1 && len(annotations1["timestamp"]) > 0; !ok {
|
||||
t.Errorf("invalid annotations 1: %v", annotations1)
|
||||
|
@ -546,13 +545,10 @@ func (s *singleBenchmark) bench(b *testing.B) {
|
|||
// extraction.
|
||||
func withAudit(ctx context.Context) context.Context {
|
||||
ctx = audit.WithAuditContext(ctx)
|
||||
ac := audit.AuditContextFrom(ctx)
|
||||
ac.Event.Level = auditinternal.LevelMetadata
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestUnsafeConversions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// needs to be large to force allocations so we pick a random value between [1024, 2048]
|
||||
size := utilrand.IntnRange(1024, 2048+1)
|
||||
|
@ -574,7 +570,6 @@ func TestUnsafeConversions(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("toBytes allocations", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := utilrand.String(size)
|
||||
f := func() {
|
||||
|
@ -606,7 +601,6 @@ func TestUnsafeConversions(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("toString allocations", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
b := make([]byte, size)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
|
|
|
@ -18,7 +18,6 @@ package tokenfile
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
@ -137,14 +136,14 @@ func TestEmptyTokenTokenFile(t *testing.T) {
|
|||
}
|
||||
|
||||
func newWithContents(t *testing.T, contents string) (auth *TokenAuthenticator, err error) {
|
||||
f, err := ioutil.TempFile("", "tokenfile_test")
|
||||
f, err := os.CreateTemp("", "tokenfile_test")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating tokenfile: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(contents), 0700); err != nil {
|
||||
if err := os.WriteFile(f.Name(), []byte(contents), 0700); err != nil {
|
||||
t.Fatalf("unexpected error writing tokenfile: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,4 +16,4 @@ limitations under the License.
|
|||
|
||||
// Package user contains utilities for dealing with simple user exchange in the auth
|
||||
// packages. The user.Info interface defines an interface for exchanging that info.
|
||||
package user // import "k8s.io/apiserver/pkg/authentication/user"
|
||||
package user
|
||||
|
|
|
@ -15,4 +15,4 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
// Package path contains an authorizer that allows certain paths and path prefixes.
|
||||
package path // import "k8s.io/apiserver/pkg/authorization/path"
|
||||
package path
|
||||
|
|
|
@ -105,23 +105,13 @@ func (p *ResolverTypeProvider) FindIdent(identName string) (ref.Val, bool) {
|
|||
return p.underlyingTypeProvider.FindIdent(identName)
|
||||
}
|
||||
|
||||
// ResolverEnvOption creates the ResolverTypeProvider with a given DynamicTypeResolver,
|
||||
// and also returns the CEL ResolverEnvOption to apply it to the env.
|
||||
// ResolverEnvOption creates the ResolverTypeProvider with a given DynamicTypeResolver
|
||||
// and returns the CEL ResolverEnvOption to apply it to the env.
|
||||
func ResolverEnvOption(resolver TypeResolver) cel.EnvOption {
|
||||
_, envOpt := NewResolverTypeProviderAndEnvOption(resolver)
|
||||
return envOpt
|
||||
}
|
||||
|
||||
// NewResolverTypeProviderAndEnvOption creates the ResolverTypeProvider with a given DynamicTypeResolver,
|
||||
// and also returns the CEL ResolverEnvOption to apply it to the env.
|
||||
func NewResolverTypeProviderAndEnvOption(resolver TypeResolver) (*ResolverTypeProvider, cel.EnvOption) {
|
||||
tp := &ResolverTypeProvider{typeResolver: resolver}
|
||||
var envOption cel.EnvOption = func(e *cel.Env) (*cel.Env, error) {
|
||||
// wrap the existing type provider (acquired from the env)
|
||||
// and set new type provider for the env.
|
||||
tp.underlyingTypeProvider = e.CELTypeProvider()
|
||||
return func(e *cel.Env) (*cel.Env, error) {
|
||||
tp := &ResolverTypeProvider{typeResolver: resolver, underlyingTypeProvider: e.CELTypeProvider()}
|
||||
// set new type provider for the env.
|
||||
typeProviderOption := cel.CustomTypeProvider(tp)
|
||||
return typeProviderOption(e)
|
||||
}
|
||||
return tp, envOption
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ func TestTypeProvider(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, option := NewResolverTypeProviderAndEnvOption(&mockTypeResolver{})
|
||||
option := ResolverEnvOption(&mockTypeResolver{})
|
||||
env := mustCreateEnv(t, option)
|
||||
ast, issues := env.Compile(tc.expression)
|
||||
if len(tc.expectCompileError) > 0 {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
Copyright 2025 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 common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"reflect"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/value"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
)
|
||||
|
||||
// TypedToVal wraps "typed" Go value as CEL ref.Val types using reflection.
|
||||
// "typed" values must be values declared by Kubernetes API types.go definitions.
|
||||
func TypedToVal(val interface{}, schema Schema) ref.Val {
|
||||
if val == nil {
|
||||
return types.NullValue
|
||||
}
|
||||
v := reflect.ValueOf(val)
|
||||
if !v.IsValid() {
|
||||
return types.NewErr("invalid data, got invalid reflect value: %v", v)
|
||||
}
|
||||
for v.Kind() == reflect.Pointer {
|
||||
if v.IsNil() {
|
||||
return types.NullValue
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
val = v.Interface()
|
||||
|
||||
switch typedVal := val.(type) {
|
||||
case bool:
|
||||
return types.Bool(typedVal)
|
||||
case int:
|
||||
return types.Int(typedVal)
|
||||
case int32:
|
||||
return types.Int(typedVal)
|
||||
case int64:
|
||||
return types.Int(typedVal)
|
||||
case float32:
|
||||
return types.Double(typedVal)
|
||||
case float64:
|
||||
return types.Double(typedVal)
|
||||
case string:
|
||||
return types.String(typedVal)
|
||||
case []byte:
|
||||
if typedVal == nil {
|
||||
return types.NullValue
|
||||
}
|
||||
return types.Bytes(typedVal)
|
||||
case metav1.Time:
|
||||
return types.Timestamp{Time: typedVal.Time}
|
||||
case metav1.MicroTime:
|
||||
return types.Timestamp{Time: typedVal.Time}
|
||||
case metav1.Duration:
|
||||
return types.Duration{Duration: typedVal.Duration}
|
||||
case intstr.IntOrString:
|
||||
switch typedVal.Type {
|
||||
case intstr.Int:
|
||||
return types.Int(typedVal.IntVal)
|
||||
case intstr.String:
|
||||
return types.String(typedVal.StrVal)
|
||||
}
|
||||
case resource.Quantity:
|
||||
// For compatibility with CRD Validation rules, represent quantity as a plain string.
|
||||
return types.String(typedVal.String())
|
||||
case json.Marshaler:
|
||||
// All JSON marshaled types must be mapped to a CEL type in the above switch.
|
||||
// This ensures that all types are purposefully mapped to CEL types.
|
||||
return types.NewErr("unsupported Go type for CEL: %T", typedVal)
|
||||
default:
|
||||
// continue on to the next switch
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
if schema.Items() == nil {
|
||||
return types.NewErr("invalid schema for slice type: %v", schema)
|
||||
}
|
||||
typedList := typedList{value: v, itemsSchema: schema.Items()}
|
||||
listType := schema.XListType()
|
||||
if listType != "" {
|
||||
switch listType {
|
||||
case "map":
|
||||
mapKeys := schema.XListMapKeys()
|
||||
return &typedMapList{typedList: typedList, escapedKeyProps: escapeKeyProps(mapKeys)}
|
||||
case "set":
|
||||
return &typedSetList{typedList: typedList}
|
||||
case "atomic":
|
||||
return &typedList
|
||||
default:
|
||||
return types.NewErr("invalid x-kubernetes-list-type, expected 'map', 'set' or 'atomic' but got %s", listType)
|
||||
}
|
||||
}
|
||||
return &typedList
|
||||
case reflect.Map:
|
||||
if schema.AdditionalProperties() == nil || schema.AdditionalProperties().Schema() == nil {
|
||||
return types.NewErr("invalid schema for map type: %v", schema)
|
||||
}
|
||||
return &typedMap{value: v, valuesSchema: schema.AdditionalProperties().Schema()}
|
||||
case reflect.Struct:
|
||||
if schema.Properties() == nil {
|
||||
return types.NewErr("invalid schema for struct type: %v", schema)
|
||||
}
|
||||
return &typedStruct{
|
||||
value: v,
|
||||
propSchema: func(key string) (Schema, bool) {
|
||||
if schema, ok := schema.Properties()[key]; ok {
|
||||
return schema, true
|
||||
}
|
||||
return nil, false
|
||||
},
|
||||
}
|
||||
// Match type aliases to primitives by kind
|
||||
case reflect.Bool:
|
||||
return types.Bool(v.Bool())
|
||||
case reflect.String:
|
||||
return types.String(v.String())
|
||||
case reflect.Int, reflect.Int32, reflect.Int64:
|
||||
return types.Int(v.Int())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return types.Double(v.Float())
|
||||
default:
|
||||
return types.NewErr("unsupported Go type for CEL: %v", v.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// typedStruct wraps a struct as a CEL ref.Val and provides lazy access to fields via reflection.
|
||||
type typedStruct struct {
|
||||
value reflect.Value // Kind is required to be: reflect.Struct
|
||||
|
||||
// propSchema finds the schema to use for a particular map key.
|
||||
propSchema func(key string) (Schema, bool)
|
||||
}
|
||||
|
||||
func (s *typedStruct) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
|
||||
if s.value.Type().AssignableTo(typeDesc) {
|
||||
return s.value.Interface(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("type conversion error from struct type %v to %v", s.value.Type(), typeDesc)
|
||||
}
|
||||
|
||||
func (s *typedStruct) ConvertToType(typeValue ref.Type) ref.Val {
|
||||
switch typeValue {
|
||||
case s.Type():
|
||||
return s
|
||||
case types.MapType:
|
||||
return s
|
||||
case types.TypeType:
|
||||
return s.objType()
|
||||
}
|
||||
return types.NewErr("type conversion error from struct %s to %s", s.Type().TypeName(), typeValue.TypeName())
|
||||
}
|
||||
|
||||
func (s *typedStruct) Equal(other ref.Val) ref.Val {
|
||||
otherStruct, ok := other.(*typedStruct)
|
||||
if ok {
|
||||
return types.Bool(apiequality.Semantic.DeepEqual(s.value.Interface(), otherStruct.value.Interface()))
|
||||
}
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
|
||||
func (s *typedStruct) Type() ref.Type {
|
||||
return s.objType()
|
||||
}
|
||||
|
||||
func (s *typedStruct) objType() *types.Type {
|
||||
typeName := s.value.Type().Name()
|
||||
if pkgPath := s.value.Type().PkgPath(); pkgPath != "" {
|
||||
typeName = pkgPath + "." + typeName
|
||||
}
|
||||
return types.NewObjectType(typeName)
|
||||
}
|
||||
|
||||
func (s *typedStruct) Value() interface{} {
|
||||
return s.value.Interface()
|
||||
}
|
||||
|
||||
func (s *typedStruct) IsSet(field ref.Val) ref.Val {
|
||||
v, found := s.lookupField(field)
|
||||
if v != nil && types.IsUnknownOrError(v) {
|
||||
return v
|
||||
}
|
||||
return types.Bool(found)
|
||||
}
|
||||
|
||||
func (s *typedStruct) Get(key ref.Val) ref.Val {
|
||||
v, found := s.lookupField(key)
|
||||
if !found {
|
||||
return types.NewErr("no such key: %v", key)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *typedStruct) lookupField(key ref.Val) (ref.Val, bool) {
|
||||
keyStr, ok := key.(types.String)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(key), true
|
||||
}
|
||||
fieldName := keyStr.Value().(string)
|
||||
|
||||
cacheEntry := value.TypeReflectEntryOf(s.value.Type())
|
||||
fieldCache, ok := cacheEntry.Fields()[fieldName]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if e := fieldCache.GetFrom(s.value); !fieldCache.CanOmit(e) {
|
||||
if propSchema, ok := s.propSchema(fieldName); ok {
|
||||
v := TypedToVal(e.Interface(), propSchema)
|
||||
if v == types.NullValue {
|
||||
return nil, false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type typedList struct {
|
||||
value reflect.Value // Kind is required to be: reflect.Slice
|
||||
|
||||
itemsSchema Schema
|
||||
}
|
||||
|
||||
func (t *typedList) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
|
||||
switch typeDesc.Kind() {
|
||||
case reflect.Slice:
|
||||
return t.value.Interface(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("type conversion error from '%s' to '%s'", t.Type(), typeDesc)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *typedList) ConvertToType(typeValue ref.Type) ref.Val {
|
||||
switch typeValue {
|
||||
case types.ListType:
|
||||
return t
|
||||
case types.TypeType:
|
||||
return types.ListType
|
||||
}
|
||||
return types.NewErr("type conversion error from '%s' to '%s'", t.Type(), typeValue.TypeName())
|
||||
}
|
||||
|
||||
func (t *typedList) Equal(other ref.Val) ref.Val {
|
||||
oList, ok := other.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
sz := types.Int(t.value.Len())
|
||||
if sz != oList.Size() {
|
||||
return types.False
|
||||
}
|
||||
for i := types.Int(0); i < sz; i++ {
|
||||
eq := t.Get(i).Equal(oList.Get(i))
|
||||
if eq != types.True {
|
||||
return eq // either false or error
|
||||
}
|
||||
}
|
||||
return types.True
|
||||
}
|
||||
|
||||
func (t *typedList) Type() ref.Type {
|
||||
return types.ListType
|
||||
}
|
||||
|
||||
func (t *typedList) Value() interface{} {
|
||||
return t.value
|
||||
}
|
||||
|
||||
func (t *typedList) Add(other ref.Val) ref.Val {
|
||||
oList, ok := other.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
resultValue := t.value
|
||||
for it := oList.Iterator(); it.HasNext() == types.True; {
|
||||
next := it.Next().Value()
|
||||
resultValue = reflect.Append(resultValue, reflect.ValueOf(next))
|
||||
}
|
||||
|
||||
return &typedList{value: resultValue, itemsSchema: t.itemsSchema}
|
||||
}
|
||||
|
||||
func (t *typedList) Contains(val ref.Val) ref.Val {
|
||||
if types.IsUnknownOrError(val) {
|
||||
return val
|
||||
}
|
||||
var err ref.Val
|
||||
sz := t.value.Len()
|
||||
for i := 0; i < sz; i++ {
|
||||
elem := TypedToVal(t.value.Index(i).Interface(), t.itemsSchema)
|
||||
cmp := elem.Equal(val)
|
||||
b, ok := cmp.(types.Bool)
|
||||
if !ok && err == nil {
|
||||
err = types.MaybeNoSuchOverloadErr(cmp)
|
||||
}
|
||||
if b == types.True {
|
||||
return types.True
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return types.False
|
||||
}
|
||||
|
||||
func (t *typedList) Get(idx ref.Val) ref.Val {
|
||||
iv, isInt := idx.(types.Int)
|
||||
if !isInt {
|
||||
return types.ValOrErr(idx, "unsupported index: %v", idx)
|
||||
}
|
||||
i := int(iv)
|
||||
if i < 0 || i >= t.value.Len() {
|
||||
return types.NewErr("index out of bounds: %v", idx)
|
||||
}
|
||||
return TypedToVal(t.value.Index(i).Interface(), t.itemsSchema)
|
||||
}
|
||||
|
||||
func (t *typedList) Iterator() traits.Iterator {
|
||||
elements := make([]ref.Val, t.value.Len())
|
||||
sz := t.value.Len()
|
||||
for i := 0; i < sz; i++ {
|
||||
elements[i] = TypedToVal(t.value.Index(i).Interface(), t.itemsSchema)
|
||||
}
|
||||
return &sliceIter{typedList: t, elements: elements}
|
||||
}
|
||||
|
||||
func (t *typedList) Size() ref.Val {
|
||||
return types.Int(t.value.Len())
|
||||
}
|
||||
|
||||
type sliceIter struct {
|
||||
*typedList
|
||||
elements []ref.Val
|
||||
idx int
|
||||
}
|
||||
|
||||
func (it *sliceIter) HasNext() ref.Val {
|
||||
return types.Bool(it.idx < len(it.elements))
|
||||
}
|
||||
|
||||
func (it *sliceIter) Next() ref.Val {
|
||||
if it.idx >= len(it.elements) {
|
||||
return types.NewErr("iterator exhausted")
|
||||
}
|
||||
elem := it.elements[it.idx]
|
||||
it.idx++
|
||||
return elem
|
||||
}
|
||||
|
||||
type typedMapList struct {
|
||||
typedList
|
||||
escapedKeyProps []string
|
||||
|
||||
sync.Once // for lazy load of mapOfList since it is only needed if Equals is called
|
||||
mapOfList map[interface{}]interface{}
|
||||
}
|
||||
|
||||
func (t *typedMapList) getMap() map[interface{}]interface{} {
|
||||
t.Do(func() {
|
||||
sz := t.value.Len()
|
||||
t.mapOfList = make(map[interface{}]interface{}, sz)
|
||||
for i := types.Int(0); i < types.Int(sz); i++ {
|
||||
v := t.Get(i)
|
||||
e := reflect.ValueOf(v.Value())
|
||||
t.mapOfList[t.toMapKey(e)] = e.Interface()
|
||||
}
|
||||
})
|
||||
return t.mapOfList
|
||||
}
|
||||
|
||||
// toMapKey returns a valid golang map key for the given element of the map list.
|
||||
// element must be a valid map list entry where all map key props are scalar types (which are comparable in go
|
||||
// and valid for use in a golang map key).
|
||||
func (t *typedMapList) toMapKey(element reflect.Value) interface{} {
|
||||
if element.Kind() != reflect.Struct {
|
||||
return types.NewErr("unexpected data format for element of array with x-kubernetes-list-type=map: %T", element)
|
||||
}
|
||||
cacheEntry := value.TypeReflectEntryOf(element.Type())
|
||||
var fieldEntries []*value.FieldCacheEntry
|
||||
for i := 0; i < len(t.escapedKeyProps); i++ {
|
||||
if ce, ok := cacheEntry.Fields()[t.escapedKeyProps[i]]; !ok {
|
||||
return types.NewErr("unexpected data format for element of array with x-kubernetes-list-type=map: %T", element)
|
||||
} else {
|
||||
fieldEntries = append(fieldEntries, ce)
|
||||
}
|
||||
}
|
||||
|
||||
// Arrays are comparable in go and may be used as map keys, but maps and slices are not.
|
||||
// So we can special case small numbers of key props as arrays and fall back to serialization
|
||||
// for larger numbers of key props
|
||||
if len(fieldEntries) == 1 {
|
||||
return fieldEntries[0].GetFrom(element).Interface()
|
||||
}
|
||||
if len(fieldEntries) == 2 {
|
||||
return [2]interface{}{fieldEntries[0].GetFrom(element).Interface(), fieldEntries[1].GetFrom(element).Interface()}
|
||||
}
|
||||
if len(fieldEntries) == 3 {
|
||||
return [3]interface{}{fieldEntries[0].GetFrom(element).Interface(), fieldEntries[1].GetFrom(element).Interface(), fieldEntries[3].GetFrom(element).Interface()}
|
||||
}
|
||||
|
||||
key := make([]interface{}, len(fieldEntries))
|
||||
for i := range fieldEntries {
|
||||
key[i] = fieldEntries[i].GetFrom(element).Interface()
|
||||
}
|
||||
return fmt.Sprintf("%v", key)
|
||||
}
|
||||
|
||||
// Equal on a map list ignores list element order.
|
||||
func (t *typedMapList) Equal(other ref.Val) ref.Val {
|
||||
oMapList, ok := other.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
sz := types.Int(t.value.Len())
|
||||
if sz != oMapList.Size() {
|
||||
return types.False
|
||||
}
|
||||
tMap := t.getMap()
|
||||
for it := oMapList.Iterator(); it.HasNext() == types.True; {
|
||||
v := it.Next()
|
||||
k := t.toMapKey(reflect.ValueOf(v.Value()))
|
||||
tVal, ok := tMap[k]
|
||||
if !ok {
|
||||
return types.False
|
||||
}
|
||||
eq := TypedToVal(tVal, t.itemsSchema).Equal(v)
|
||||
if eq != types.True {
|
||||
return eq // either false or error
|
||||
}
|
||||
}
|
||||
return types.True
|
||||
}
|
||||
|
||||
// Add for a map list `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values
|
||||
// are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with
|
||||
// non-intersecting keys are appended, retaining their partial order.
|
||||
func (t *typedMapList) Add(other ref.Val) ref.Val {
|
||||
sliceType := t.value.Type()
|
||||
elementType := sliceType.Elem()
|
||||
oMapList, ok := other.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
sz := t.value.Len()
|
||||
elements := reflect.MakeSlice(sliceType, sz, sz)
|
||||
keyToIdx := map[interface{}]int{}
|
||||
for i := 0; i < sz; i++ {
|
||||
e := t.Get(types.Int(i)).Value()
|
||||
re := reflect.ValueOf(e)
|
||||
k := t.toMapKey(re)
|
||||
keyToIdx[k] = i
|
||||
elements.Index(i).Set(re.Convert(elementType))
|
||||
}
|
||||
for it := oMapList.Iterator(); it.HasNext() == types.True; {
|
||||
e := it.Next()
|
||||
re := reflect.ValueOf(e.Value())
|
||||
k := t.toMapKey(re)
|
||||
if overwritePosition, ok := keyToIdx[k]; ok {
|
||||
elements.Index(overwritePosition).Set(re)
|
||||
} else {
|
||||
elements = reflect.Append(elements, re.Convert(elementType))
|
||||
}
|
||||
}
|
||||
return &typedMapList{
|
||||
typedList: typedList{value: elements, itemsSchema: t.itemsSchema},
|
||||
escapedKeyProps: t.escapedKeyProps,
|
||||
}
|
||||
}
|
||||
|
||||
type typedSetList struct {
|
||||
typedList
|
||||
|
||||
sync.Once // for lazy load of setOfList since it is only needed if Equals is called
|
||||
set map[interface{}]struct{}
|
||||
}
|
||||
|
||||
func (t *typedSetList) getSet() map[interface{}]struct{} {
|
||||
// sets are only allowed to contain scalar elements, which are comparable in go, and can safely be used as
|
||||
// golang map keys
|
||||
t.Do(func() {
|
||||
sz := t.value.Len()
|
||||
t.set = make(map[interface{}]struct{}, sz)
|
||||
for i := types.Int(0); i < types.Int(sz); i++ {
|
||||
e := t.Get(i).Value()
|
||||
t.set[e] = struct{}{}
|
||||
}
|
||||
})
|
||||
return t.set
|
||||
}
|
||||
|
||||
// Equal on a map list ignores list element order.
|
||||
func (t *typedSetList) Equal(other ref.Val) ref.Val {
|
||||
oSetList, ok := other.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
sz := types.Int(t.value.Len())
|
||||
if sz != oSetList.Size() {
|
||||
return types.False
|
||||
}
|
||||
tSet := t.getSet()
|
||||
for it := oSetList.Iterator(); it.HasNext() == types.True; {
|
||||
next := it.Next().Value()
|
||||
_, ok := tSet[next]
|
||||
if !ok {
|
||||
return types.False
|
||||
}
|
||||
}
|
||||
return types.True
|
||||
}
|
||||
|
||||
// Add for a set list `X + Y` performs a union where the array positions of all elements in `X` are preserved and
|
||||
// non-intersecting elements in `Y` are appended, retaining their partial order.
|
||||
func (t *typedSetList) Add(other ref.Val) ref.Val {
|
||||
setType := t.value.Type()
|
||||
elementType := setType.Elem()
|
||||
oSetList, ok := other.(traits.Lister)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
sz := t.value.Len()
|
||||
elements := reflect.MakeSlice(setType, sz, sz)
|
||||
for i := 0; i < sz; i++ {
|
||||
e := t.Get(types.Int(i)).Value()
|
||||
re := reflect.ValueOf(e)
|
||||
elements.Index(i).Set(re.Convert(elementType))
|
||||
}
|
||||
set := t.getSet()
|
||||
for it := oSetList.Iterator(); it.HasNext() == types.True; {
|
||||
e := it.Next().Value()
|
||||
re := reflect.ValueOf(e)
|
||||
if _, ok := set[e]; !ok {
|
||||
set[e] = struct{}{}
|
||||
elements = reflect.Append(elements, re.Convert(elementType))
|
||||
}
|
||||
}
|
||||
return &typedSetList{
|
||||
typedList: typedList{value: elements, itemsSchema: t.itemsSchema},
|
||||
}
|
||||
}
|
||||
|
||||
type typedMap struct {
|
||||
value reflect.Value // Kind is required to be: reflect.Map
|
||||
|
||||
valuesSchema Schema
|
||||
}
|
||||
|
||||
func (t *typedMap) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
|
||||
switch typeDesc.Kind() {
|
||||
case reflect.Map:
|
||||
return t.value, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("type conversion error from '%s' to '%s'", t.Type(), typeDesc)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *typedMap) ConvertToType(typeValue ref.Type) ref.Val {
|
||||
switch typeValue {
|
||||
case types.MapType:
|
||||
return t
|
||||
case types.TypeType:
|
||||
return types.MapType
|
||||
}
|
||||
return types.NewErr("type conversion error from '%s' to '%s'", t.Type(), typeValue.TypeName())
|
||||
}
|
||||
|
||||
func (t *typedMap) Equal(other ref.Val) ref.Val {
|
||||
oMap, isMap := other.(traits.Mapper)
|
||||
if !isMap {
|
||||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
if types.Int(t.value.Len()) != oMap.Size() {
|
||||
return types.False
|
||||
}
|
||||
for it := t.value.MapRange(); it.Next(); {
|
||||
key := it.Key()
|
||||
value := it.Value()
|
||||
ov, found := oMap.Find(types.String(key.String()))
|
||||
if !found {
|
||||
return types.False
|
||||
}
|
||||
v := TypedToVal(value.Interface(), t.valuesSchema)
|
||||
vEq := v.Equal(ov)
|
||||
if vEq != types.True {
|
||||
return vEq // either false or error
|
||||
}
|
||||
}
|
||||
return types.True
|
||||
}
|
||||
|
||||
func (t *typedMap) Type() ref.Type {
|
||||
return types.MapType
|
||||
}
|
||||
|
||||
func (t *typedMap) Value() interface{} {
|
||||
return t.value
|
||||
}
|
||||
|
||||
func (t *typedMap) Contains(key ref.Val) ref.Val {
|
||||
v, found := t.Find(key)
|
||||
if v != nil && types.IsUnknownOrError(v) {
|
||||
return v
|
||||
}
|
||||
|
||||
return types.Bool(found)
|
||||
}
|
||||
|
||||
func (t *typedMap) Get(key ref.Val) ref.Val {
|
||||
v, found := t.Find(key)
|
||||
if found {
|
||||
return v
|
||||
}
|
||||
return types.ValOrErr(key, "no such key: %v", key)
|
||||
}
|
||||
|
||||
func (t *typedMap) Size() ref.Val {
|
||||
return types.Int(t.value.Len())
|
||||
}
|
||||
|
||||
func (t *typedMap) Find(key ref.Val) (ref.Val, bool) {
|
||||
keyStr, ok := key.(types.String)
|
||||
if !ok {
|
||||
return types.MaybeNoSuchOverloadErr(key), true
|
||||
}
|
||||
k := keyStr.Value().(string)
|
||||
if v := t.value.MapIndex(reflect.ValueOf(k)); v.IsValid() {
|
||||
return TypedToVal(v.Interface(), t.valuesSchema), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (t *typedMap) Iterator() traits.Iterator {
|
||||
keys := make([]ref.Val, t.value.Len())
|
||||
for i, k := range t.value.MapKeys() {
|
||||
keys[i] = types.String(k.String())
|
||||
}
|
||||
return &mapIter{typedMap: t, keys: keys}
|
||||
}
|
||||
|
||||
type mapIter struct {
|
||||
*typedMap
|
||||
keys []ref.Val
|
||||
idx int
|
||||
}
|
||||
|
||||
func (it *mapIter) HasNext() ref.Val {
|
||||
return types.Bool(it.idx < len(it.keys))
|
||||
}
|
||||
|
||||
func (it *mapIter) Next() ref.Val {
|
||||
key := it.keys[it.idx]
|
||||
it.idx++
|
||||
return key
|
||||
}
|
|
@ -33,7 +33,7 @@ import (
|
|||
)
|
||||
|
||||
// UnstructuredToVal converts a Kubernetes unstructured data element to a CEL Val.
|
||||
// The root schema of custom resource schema is expected contain type meta and object meta schemas.
|
||||
// The root schema of custom resource schemas is expected to contain type meta and object meta schemas.
|
||||
// If Embedded resources do not contain type meta and object meta schemas, they will be added automatically.
|
||||
func UnstructuredToVal(unstructured interface{}, schema Schema) ref.Val {
|
||||
if unstructured == nil {
|
||||
|
@ -358,9 +358,8 @@ func escapeKeyProps(idents []string) []string {
|
|||
// unstructuredSetList represents an unstructured data instance of an OpenAPI array with x-kubernetes-list-type=set.
|
||||
type unstructuredSetList struct {
|
||||
unstructuredList
|
||||
escapedKeyProps []string
|
||||
|
||||
sync.Once // for for lazy load of setOfList since it is only needed if Equals is called
|
||||
sync.Once // for lazy load of setOfList since it is only needed if Equals is called
|
||||
set map[interface{}]struct{}
|
||||
}
|
||||
|
||||
|
@ -415,7 +414,6 @@ func (t *unstructuredSetList) Add(other ref.Val) ref.Val {
|
|||
}
|
||||
return &unstructuredSetList{
|
||||
unstructuredList: unstructuredList{elements: elements, itemsSchema: t.itemsSchema},
|
||||
escapedKeyProps: t.escapedKeyProps,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,13 +598,17 @@ func (t *unstructuredMap) Equal(other ref.Val) ref.Val {
|
|||
if t.Size() != oMap.Size() {
|
||||
return types.False
|
||||
}
|
||||
|
||||
for key, value := range t.value {
|
||||
if propSchema, ok := t.propSchema(key); ok {
|
||||
ov, found := oMap.Find(types.String(key))
|
||||
if !found {
|
||||
if _, ok := t.propSchema(key); ok {
|
||||
v, found := t.Find(types.String(key))
|
||||
ov, oFound := oMap.Find(types.String(key))
|
||||
if found != oFound {
|
||||
return types.False
|
||||
}
|
||||
v := UnstructuredToVal(value, propSchema)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
vEq := v.Equal(ov)
|
||||
if vEq != types.True {
|
||||
return vEq // either false or error
|
|
@ -32,9 +32,9 @@ import (
|
|||
celconfig "k8s.io/apiserver/pkg/apis/cel"
|
||||
"k8s.io/apiserver/pkg/cel/library"
|
||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/util/compatibility"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/component-base/featuregate"
|
||||
utilversion "k8s.io/component-base/version"
|
||||
basecompatibility "k8s.io/component-base/compatibility"
|
||||
)
|
||||
|
||||
// DefaultCompatibilityVersion returns a default compatibility version for use with EnvSet
|
||||
|
@ -50,9 +50,9 @@ import (
|
|||
// A default version number equal to the current Kubernetes major.minor version
|
||||
// indicates fast forward CEL features that can be used when rollback is no longer needed.
|
||||
func DefaultCompatibilityVersion() *version.Version {
|
||||
effectiveVer := featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent)
|
||||
effectiveVer := compatibility.DefaultComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent)
|
||||
if effectiveVer == nil {
|
||||
effectiveVer = utilversion.DefaultKubeEffectiveVersion()
|
||||
effectiveVer = compatibility.DefaultBuildEffectiveVersion()
|
||||
}
|
||||
return effectiveVer.MinCompatibilityVersion()
|
||||
}
|
||||
|
@ -173,7 +173,14 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
|
|||
{
|
||||
IntroducedVersion: version.MajorMinor(1, 32),
|
||||
EnvOptions: []cel.EnvOption{
|
||||
UnversionedLib(ext.TwoVarComprehensions),
|
||||
ext.TwoVarComprehensions(),
|
||||
},
|
||||
},
|
||||
// Semver
|
||||
{
|
||||
IntroducedVersion: version.MajorMinor(1, 33),
|
||||
EnvOptions: []cel.EnvOption{
|
||||
library.SemverLib(library.SemverVersion(1)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ package cel
|
|||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
"sigs.k8s.io/randfill"
|
||||
)
|
||||
|
||||
// TestEscaping tests that property names are escaped as expected.
|
||||
|
@ -142,10 +143,10 @@ func TestUnescapeMalformed(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEscapingFuzz(t *testing.T) {
|
||||
fuzzer := fuzz.New()
|
||||
fuzzer := randfill.New()
|
||||
for i := 0; i < 1000; i++ {
|
||||
var unescaped string
|
||||
fuzzer.Fuzz(&unescaped)
|
||||
fuzzer.Fill(&unescaped)
|
||||
t.Run(fmt.Sprintf("%d - '%s'", i, unescaped), func(t *testing.T) {
|
||||
if len(unescaped) == 0 {
|
||||
return
|
||||
|
@ -204,3 +205,11 @@ func TestCanSkipRegex(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCELReservedSymbolsNoDoubleUnderscore(t *testing.T) {
|
||||
for symbol := range celReservedSymbols {
|
||||
if strings.Contains(symbol, "__") {
|
||||
t.Errorf("CEL reserved symbol '%s' contains '__', which is not allowed as it would interfere with escaping", symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,9 +160,7 @@ var cidrLibraryDecls = map[string][]cel.FunctionOpt{
|
|||
}
|
||||
|
||||
func (*cidrs) CompileOptions() []cel.EnvOption {
|
||||
options := []cel.EnvOption{cel.Types(apiservercel.CIDRType),
|
||||
cel.Variable(apiservercel.CIDRType.TypeName(), types.NewTypeTypeWithParam(apiservercel.CIDRType)),
|
||||
}
|
||||
options := []cel.EnvOption{cel.Types(apiservercel.CIDRType)}
|
||||
for name, overloads := range cidrLibraryDecls {
|
||||
options = append(options, cel.Function(name, overloads...))
|
||||
}
|
||||
|
@ -231,8 +229,7 @@ func cidrContainsCIDR(arg ref.Val, other ref.Val) ref.Val {
|
|||
return types.MaybeNoSuchOverloadErr(other)
|
||||
}
|
||||
|
||||
equalMasked := cidr.Prefix.Masked() == netip.PrefixFrom(containsCIDR.Prefix.Addr(), cidr.Prefix.Bits())
|
||||
return types.Bool(equalMasked && cidr.Prefix.Bits() <= containsCIDR.Prefix.Bits())
|
||||
return types.Bool(cidr.Overlaps(containsCIDR.Prefix) && cidr.Prefix.Bits() <= containsCIDR.Prefix.Bits())
|
||||
}
|
||||
|
||||
func prefixLength(arg ref.Val) ref.Val {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue