From dde74b314d73788781eb33dc7cb6f0a6352a3903 Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Wed, 3 Dec 2025 14:15:22 +0100 Subject: [PATCH 1/8] update codebase to kcp 0.29 On-behalf-of: @SAP christoph.mewes@sap.com --- cmd/api-syncagent/kcp.go | 6 +-- cmd/api-syncagent/main.go | 2 +- go.mod | 35 ++++++------ go.sum | 54 ++++++++++--------- hack/reconciling.yaml | 4 +- internal/controller/apiexport/controller.go | 2 +- internal/controller/apiexport/reconciler.go | 2 +- .../apiresourceschema/controller.go | 2 +- internal/controller/sync/controller.go | 4 +- internal/controller/syncmanager/controller.go | 4 +- internal/kcp/apiresourceschema.go | 2 +- .../reconciling/zz_generated_reconcile.go | 2 +- test/e2e/apiexport/apiexport_test.go | 2 +- .../apiresourceschema_test.go | 2 +- test/e2e/sync/apiexportendpointslice_test.go | 2 +- test/e2e/sync/related_test.go | 2 +- test/utils/fixtures.go | 10 ++-- test/utils/utils.go | 4 +- test/utils/wait.go | 2 +- 19 files changed, 71 insertions(+), 72 deletions(-) diff --git a/cmd/api-syncagent/kcp.go b/cmd/api-syncagent/kcp.go index 3d376dd..5a1f054 100644 --- a/cmd/api-syncagent/kcp.go +++ b/cmd/api-syncagent/kcp.go @@ -26,9 +26,9 @@ import ( "github.com/kcp-dev/api-syncagent/internal/kcp" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" - kcpdevcore "github.com/kcp-dev/kcp/sdk/apis/core" - kcpdevcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpdevcore "github.com/kcp-dev/sdk/apis/core" + kcpdevcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" diff --git a/cmd/api-syncagent/main.go b/cmd/api-syncagent/main.go index 708449a..9deb511 100644 --- a/cmd/api-syncagent/main.go +++ b/cmd/api-syncagent/main.go @@ -36,7 +36,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/version" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" diff --git a/go.mod b/go.mod index 847ef22..dfb85b3 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.24.0 replace github.com/kcp-dev/api-syncagent/sdk => ./sdk replace ( - k8s.io/apiextensions-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20250821202322-e42d885de52b - k8s.io/apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20250821202322-e42d885de52b + k8s.io/apiextensions-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251113163256-f038ec6bf609 + k8s.io/apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251113163256-f038ec6bf609 ) require ( @@ -17,10 +17,10 @@ require ( github.com/google/cel-go v0.23.2 github.com/google/go-cmp v0.7.0 github.com/kcp-dev/api-syncagent/sdk v0.0.0-00010101000000-000000000000 - github.com/kcp-dev/kcp v0.28.1 - github.com/kcp-dev/kcp/sdk v0.28.1 + github.com/kcp-dev/kcp v0.29.0 github.com/kcp-dev/logicalcluster/v3 v3.0.5 github.com/kcp-dev/multicluster-provider v0.2.1-0.20250901093233-9ef571c8bd47 + github.com/kcp-dev/sdk v0.29.0 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 github.com/spf13/cobra v1.10.1 github.com/spf13/pflag v1.0.10 @@ -28,16 +28,12 @@ require ( github.com/tidwall/sjson v1.2.5 go.uber.org/zap v1.27.0 k8c.io/reconciler v0.5.0 - // Deviating from kcp's kube version (0.32) because more recent multicluster-runtime - // versions are built ontop of controller-runtime 0.21 with Kube 0.33. Ideally in - // the future we should go back to having kcp, controller-runtime and multicluster-runtime - // be in-sync again. - k8s.io/api v0.33.4 - k8s.io/apiextensions-apiserver v0.33.4 - k8s.io/apimachinery v0.33.4 - k8s.io/apiserver v0.33.4 - k8s.io/client-go v0.33.4 - k8s.io/component-base v0.33.4 + k8s.io/api v0.33.5 + k8s.io/apiextensions-apiserver v0.33.5 + k8s.io/apimachinery v0.33.5 + k8s.io/apiserver v0.33.5 + k8s.io/client-go v0.33.5 + k8s.io/component-base v0.33.5 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 sigs.k8s.io/controller-runtime v0.21.0 @@ -91,8 +87,9 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20250728122101-adbf20db3e51 // indirect - github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b // indirect + github.com/kcp-dev/apimachinery/v2 v2.29.0 // indirect + github.com/kcp-dev/client-go v0.29.0 // indirect + github.com/kcp-dev/kcp/sdk v0.28.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.9.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -144,10 +141,10 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-helpers v0.32.3 // indirect - k8s.io/controller-manager v0.32.3 // indirect + k8s.io/component-helpers v0.33.5 // indirect + k8s.io/controller-manager v0.33.5 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kubernetes v1.33.3 // indirect + k8s.io/kubernetes v1.33.5 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/go.sum b/go.sum index 093b263..985ebd9 100644 --- a/go.sum +++ b/go.sum @@ -129,22 +129,24 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20250728122101-adbf20db3e51 h1:l38RDS+VUMx9etvyaCgJIZa4nM7FaNevNubWN0kDZY4= -github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20250728122101-adbf20db3e51/go.mod h1:rF1jfvUfPjFXs+HV/LN1BtPzAz1bfjJOwVa+hAVfroQ= -github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b h1:2LGrXvY9sc4l5yjKIbMZ86GEou7NyrHhA4qBPaeFfxs= -github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b/go.mod h1:QdO8AaGAZPr/rIZ1iVanCM3tUOiiuX897GWv7WTByLE= -github.com/kcp-dev/kcp v0.28.1 h1:T7Ky7u9hvprkGrBnKuw0QZoP8O6TCbXqJz2Kwt6Tx+o= -github.com/kcp-dev/kcp v0.28.1/go.mod h1:q28Fx8sU/KA8kz8HGwtaqA7Iom8oR90ydoPK39jMaxo= +github.com/kcp-dev/apimachinery/v2 v2.29.0 h1:hoIomHs0v8N9+kvKYfjM/tVh5l27NH1zCDUV0kRLxn4= +github.com/kcp-dev/apimachinery/v2 v2.29.0/go.mod h1:KizREtsxF5malriCj+Cr87TghwHQIMZYXbVycNBbGHs= +github.com/kcp-dev/client-go v0.29.0 h1:F5pX3Ohy2jUjPZVHNkLCxtFIwvdB8rB/iu3Ops8Yw8w= +github.com/kcp-dev/client-go v0.29.0/go.mod h1:Kl+d0bVdvyLULfZLjWA2Zfem8iOJRC0YbK6PpCOwUZg= +github.com/kcp-dev/kcp v0.29.0 h1:FAfbOEJ6jH0YxhCnjMSQndFCr3ORZKO1wrLZTqV0MYo= +github.com/kcp-dev/kcp v0.29.0/go.mod h1:4+DL+QaHRyb8lQjTWR27KtQ8NBvfrXNI9GIuk1kXkuQ= github.com/kcp-dev/kcp/sdk v0.28.1 h1:bTtuHVjFRjbwFEqXTPxc1J1JP2Hc3mTYqQ2xfJsi16M= github.com/kcp-dev/kcp/sdk v0.28.1/go.mod h1:8oZpWxkoMu2TDpx5DgdIGDigByKHKkeqVMA4GiWneoI= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20250821202322-e42d885de52b h1:PsIDMrXs6N6TqdEhjoEQC2xnj6CUV9yOdc5jmlt1PPg= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20250821202322-e42d885de52b/go.mod h1:YS4BozVhJubIHAN+HRLFrXAAPd+vctFRS0u1d6DRUIM= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20250821202322-e42d885de52b h1:G8dAytU6qgg9jTqeYJcsTiJkndOeOsS4/fub7JOu2A0= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20250821202322-e42d885de52b/go.mod h1:STCgTiD+xCCHsfLOPHn5sNVsyktakX/ctW3dMv3erh0= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251113163256-f038ec6bf609 h1:2eF4Jq/1bTL2OObwStwOOik1WdLVPOSwHyuiRXjWWbg= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251113163256-f038ec6bf609/go.mod h1:7YRhmmIzd7elyfRQ96hbOfFq7+OvGNZljO5Z/E+mLto= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251113163256-f038ec6bf609 h1:yMWrq/DpozipIHUNrjnqOHr/55rl8eMFlfJJFYSjaTk= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251113163256-f038ec6bf609/go.mod h1:nG4FME1ik2lKNo+jGhrGsZRiI/+1r8Nuldah1bPqqg8= github.com/kcp-dev/logicalcluster/v3 v3.0.5 h1:JbYakokb+5Uinz09oTXomSUJVQsqfxEvU4RyHUYxHOU= github.com/kcp-dev/logicalcluster/v3 v3.0.5/go.mod h1:EWBUBxdr49fUB1cLMO4nOdBWmYifLbP1LfoL20KkXYY= github.com/kcp-dev/multicluster-provider v0.2.1-0.20250901093233-9ef571c8bd47 h1:CPrrjZevZqUHrW/BBVvSjzSFrFnASm7srHznhTBhJ0Y= github.com/kcp-dev/multicluster-provider v0.2.1-0.20250901093233-9ef571c8bd47/go.mod h1:RhSDOKAn/ROpSIFt140Tk2LsM7PXa3a3rov4qbqFERc= +github.com/kcp-dev/sdk v0.29.0 h1:zXJjtcKNduy8MntSdf2CoXSMVWybptPJwq0zKBkf0Iw= +github.com/kcp-dev/sdk v0.29.0/go.mod h1:iOA3cEMbI7jeLHbfc0GlHSXRHfyhOWC0NDEJRdNcBY8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -351,26 +353,26 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8c.io/reconciler v0.5.0 h1:BHpelg1UfI/7oBFctqOq8sX6qzflXpl3SlvHe7e8wak= k8c.io/reconciler v0.5.0/go.mod h1:pT1+SVcVXJQeBJhpJBXQ5XW64QnKKeYTnVlQf0dGE0k= -k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk= -k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc= -k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s= -k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/client-go v0.33.4 h1:TNH+CSu8EmXfitntjUPwaKVPN0AYMbc9F1bBS8/ABpw= -k8s.io/client-go v0.33.4/go.mod h1:LsA0+hBG2DPwovjd931L/AoaezMPX9CmBgyVyBZmbCY= -k8s.io/component-base v0.33.4 h1:Jvb/aw/tl3pfgnJ0E0qPuYLT0NwdYs1VXXYQmSuxJGY= -k8s.io/component-base v0.33.4/go.mod h1:567TeSdixWW2Xb1yYUQ7qk5Docp2kNznKL87eygY8Rc= -k8s.io/component-helpers v0.32.3 h1:9veHpOGTPLluqU4hAu5IPOwkOIZiGAJUhHndfVc5FT4= -k8s.io/component-helpers v0.32.3/go.mod h1:utTBXk8lhkJewBKNuNf32Xl3KT/0VV19DmiXU/SV4Ao= -k8s.io/controller-manager v0.32.3 h1:jBxZnQ24k6IMeWLyxWZmpa3QVS7ww+osAIzaUY/jqyc= -k8s.io/controller-manager v0.32.3/go.mod h1:out1L3DZjE/p7JG0MoMMIaQGWIkt3c+pKaswqSHgKsI= +k8s.io/api v0.33.5 h1:YR+uhYj05jdRpcksv8kjSliW+v9hwXxn6Cv10aR8Juw= +k8s.io/api v0.33.5/go.mod h1:2gzShdwXKT5yPGiqrTrn/U/nLZ7ZyT4WuAj3XGDVgVs= +k8s.io/apimachinery v0.33.5 h1:NiT64hln4TQXeYR18/ES39OrNsjGz8NguxsBgp+6QIo= +k8s.io/apimachinery v0.33.5/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.5 h1:I8BdmQGxInpkMEnJvV6iG7dqzP3JRlpZZlib3OMFc3o= +k8s.io/client-go v0.33.5/go.mod h1:W8PQP4MxbM4ypgagVE65mUUqK1/ByQkSALF9tzuQ6u0= +k8s.io/component-base v0.33.5 h1:4D3kxjEx1pJRy3WHAZsmX3+LCpmd4ftE+2J4v6naTnQ= +k8s.io/component-base v0.33.5/go.mod h1:Zma1YjBVuuGxIbspj1vGR3/5blzo2ARf1v0QTtog1to= +k8s.io/component-helpers v0.33.5 h1:1LDSMzn7YTreVLPaOBJK36ase/FWi2sDpeJJvbEBO2s= +k8s.io/component-helpers v0.33.5/go.mod h1:C3HsDU2lANSLgTTgMJ0TFnG5xZrVrxR3Ss9n7Wrsw4s= +k8s.io/controller-manager v0.33.5 h1:abmssknXnhOhW533583v2SYQObD5RhYiSL7Za1rezGM= +k8s.io/controller-manager v0.33.5/go.mod h1:KuQeAlf4vI2+qj5fwPVLaDlbtrTBA/8L/LqQvI74Ow0= 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.32.3 h1:HhHw5+pRCzEJp3oFFJ1q5W2N6gAI7YkUg4ay4Z0dgwM= -k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= +k8s.io/kms v0.33.3 h1:7cQWC+GSH211NgY8LRKjBXNtkzra5SkpYzeZrOt5D+8= +k8s.io/kms v0.33.3/go.mod h1:C1I8mjFFBNzfUZXYt9FZVJ8MJl7ynFbGgZFbBzkBJ3E= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/kubernetes v1.33.3 h1:dBx5Z2ZhR8kNzAwCoCz4j1niUbUrNUDVxeSj4/Ienu0= -k8s.io/kubernetes v1.33.3/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00= +k8s.io/kubernetes v1.33.5 h1:uf0/5VVQodYf61FudCbwtWjg5ApQcKKA7mwanTLjxyw= +k8s.io/kubernetes v1.33.5/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00= 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= diff --git a/hack/reconciling.yaml b/hack/reconciling.yaml index 321400d..930f81f 100644 --- a/hack/reconciling.yaml +++ b/hack/reconciling.yaml @@ -19,5 +19,5 @@ package: reconciling boilerplate: hack/boilerplate/generated/boilerplate.go.txt resourceTypes: # kcp-dev/v1alpha1 - - { package: github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1, importAlias: kcpdevv1alpha1, resourceName: APIExport } - - { package: github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1, importAlias: kcpdevv1alpha1, resourceName: APIResourceSchema } + - { package: github.com/kcp-dev/sdk/apis/apis/v1alpha1, importAlias: kcpdevv1alpha1, resourceName: APIExport } + - { package: github.com/kcp-dev/sdk/apis/apis/v1alpha1, importAlias: kcpdevv1alpha1, resourceName: APIResourceSchema } diff --git a/internal/controller/apiexport/controller.go b/internal/controller/apiexport/controller.go index eda3a92..a1654b7 100644 --- a/internal/controller/apiexport/controller.go +++ b/internal/controller/apiexport/controller.go @@ -31,7 +31,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/resources/reconciling" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/internal/controller/apiexport/reconciler.go b/internal/controller/apiexport/reconciler.go index abad6ab..6be8793 100644 --- a/internal/controller/apiexport/reconciler.go +++ b/internal/controller/apiexport/reconciler.go @@ -24,7 +24,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/resources/reconciling" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/internal/controller/apiresourceschema/controller.go b/internal/controller/apiresourceschema/controller.go index bacb4ce..d5dea39 100644 --- a/internal/controller/apiresourceschema/controller.go +++ b/internal/controller/apiresourceschema/controller.go @@ -30,7 +30,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/projection" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" diff --git a/internal/controller/sync/controller.go b/internal/controller/sync/controller.go index 86eae42..1622d80 100644 --- a/internal/controller/sync/controller.go +++ b/internal/controller/sync/controller.go @@ -31,8 +31,8 @@ import ( "github.com/kcp-dev/api-syncagent/internal/sync" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpcore "github.com/kcp-dev/kcp/sdk/apis/core" - kcpdevcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + kcpcore "github.com/kcp-dev/sdk/apis/core" + kcpdevcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" mccontroller "sigs.k8s.io/multicluster-runtime/pkg/controller" mchandler "sigs.k8s.io/multicluster-runtime/pkg/handler" mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" diff --git a/internal/controller/syncmanager/controller.go b/internal/controller/syncmanager/controller.go index 8f5f966..ddccdb0 100644 --- a/internal/controller/syncmanager/controller.go +++ b/internal/controller/syncmanager/controller.go @@ -31,9 +31,9 @@ import ( "github.com/kcp-dev/api-syncagent/internal/discovery" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" - kcpcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" apiexportprovider "github.com/kcp-dev/multicluster-provider/apiexport" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" mccontroller "sigs.k8s.io/multicluster-runtime/pkg/controller" mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" diff --git a/internal/kcp/apiresourceschema.go b/internal/kcp/apiresourceschema.go index 465f080..c2ea80b 100644 --- a/internal/kcp/apiresourceschema.go +++ b/internal/kcp/apiresourceschema.go @@ -22,7 +22,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/crypto" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/internal/resources/reconciling/zz_generated_reconcile.go b/internal/resources/reconciling/zz_generated_reconcile.go index 0a22759..f19e52c 100644 --- a/internal/resources/reconciling/zz_generated_reconcile.go +++ b/internal/resources/reconciling/zz_generated_reconcile.go @@ -24,7 +24,7 @@ import ( "k8s.io/apimachinery/pkg/types" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" ) // APIExportReconciler defines an interface to create/update APIExports. diff --git a/test/e2e/apiexport/apiexport_test.go b/test/e2e/apiexport/apiexport_test.go index 06371a7..f637ff3 100644 --- a/test/e2e/apiexport/apiexport_test.go +++ b/test/e2e/apiexport/apiexport_test.go @@ -30,7 +30,7 @@ import ( syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" "github.com/kcp-dev/api-syncagent/test/utils" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/test/e2e/apiresourceschema/apiresourceschema_test.go b/test/e2e/apiresourceschema/apiresourceschema_test.go index 136c88d..edfbb5c 100644 --- a/test/e2e/apiresourceschema/apiresourceschema_test.go +++ b/test/e2e/apiresourceschema/apiresourceschema_test.go @@ -29,7 +29,7 @@ import ( syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" "github.com/kcp-dev/api-syncagent/test/utils" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/test/e2e/sync/apiexportendpointslice_test.go b/test/e2e/sync/apiexportendpointslice_test.go index b8930dd..3e928b7 100644 --- a/test/e2e/sync/apiexportendpointslice_test.go +++ b/test/e2e/sync/apiexportendpointslice_test.go @@ -30,7 +30,7 @@ import ( syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" "github.com/kcp-dev/api-syncagent/test/utils" - kcpdevv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/test/e2e/sync/related_test.go b/test/e2e/sync/related_test.go index 03843a0..29646c0 100644 --- a/test/e2e/sync/related_test.go +++ b/test/e2e/sync/related_test.go @@ -36,7 +36,7 @@ import ( crds "github.com/kcp-dev/api-syncagent/test/crds/example/v1" "github.com/kcp-dev/api-syncagent/test/utils" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/test/utils/fixtures.go b/test/utils/fixtures.go index e9abfe7..ff1f6aa 100644 --- a/test/utils/fixtures.go +++ b/test/utils/fixtures.go @@ -27,11 +27,11 @@ import ( "github.com/kcp-dev/logicalcluster/v3" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" - kcpcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" - kcptenancyv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/tenancy/v1alpha1" - conditionsv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/third_party/conditions/apis/conditions/v1alpha1" - "github.com/kcp-dev/kcp/sdk/apis/third_party/conditions/util/conditions" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" + kcptenancyv1alpha1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1" + conditionsv1alpha1 "github.com/kcp-dev/sdk/apis/third_party/conditions/apis/conditions/v1alpha1" + "github.com/kcp-dev/sdk/apis/third_party/conditions/util/conditions" rbacv1 "k8s.io/api/rbac/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" diff --git a/test/utils/utils.go b/test/utils/utils.go index 5102d92..a79d38e 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -27,9 +27,9 @@ import ( syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" - kcptenancyv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/tenancy/v1alpha1" mcclient "github.com/kcp-dev/multicluster-provider/client" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcptenancyv1alpha1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" diff --git a/test/utils/wait.go b/test/utils/wait.go index b8fe446..c2ea631 100644 --- a/test/utils/wait.go +++ b/test/utils/wait.go @@ -22,7 +22,7 @@ import ( "testing" "time" - kcpapisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" From 89aa356316db1fad5965df6ca4bba4950504de2f Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Wed, 3 Dec 2025 14:25:37 +0100 Subject: [PATCH 2/8] bump all golang.org/x/ modules On-behalf-of: @SAP christoph.mewes@sap.com --- go.mod | 18 +++++++++--------- go.sum | 40 ++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index dfb85b3..b34ca02 100644 --- a/go.mod +++ b/go.mod @@ -124,15 +124,15 @@ require ( go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.41.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/time v0.11.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.14.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect diff --git a/go.sum b/go.sum index 985ebd9..27bc056 100644 --- a/go.sum +++ b/go.sum @@ -285,44 +285,44 @@ 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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= +golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= 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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 4555e2f99d5835d0aa014eafa23ff507a538e5c9 Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Fri, 12 Dec 2025 17:21:18 +0100 Subject: [PATCH 3/8] fix make accidentally creating a false file when installing kcp On-behalf-of: @SAP christoph.mewes@sap.com --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4535b1c..9a7eba5 100644 --- a/Makefile +++ b/Makefile @@ -174,7 +174,7 @@ install-yq: @UNCOMPRESSED=true hack/uget.sh https://github.com/mikefarah/yq/releases/download/v{VERSION}/yq_{GOOS}_{GOARCH} yq $(YQ_VERSION) yq_* .PHONY: install-kcp -install-kcp: UGET_CHECKSUMS=false # do not checksum because the version regularly gets overwritten in CI jobs +install-kcp: UGET_CHECKSUMS= # do not checksum because the version regularly gets overwritten in CI jobs install-kcp: @hack/uget.sh https://github.com/kcp-dev/kcp/releases/download/v{VERSION}/kcp_{VERSION}_{GOOS}_{GOARCH}.tar.gz kcp $(KCP_VERSION) From cb23876d9afbbdbf35b54fc844cc58d5ecfdcae3 Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Fri, 12 Dec 2025 17:32:30 +0100 Subject: [PATCH 4/8] improve import aliases and grouping On-behalf-of: @SAP christoph.mewes@sap.com --- .gimps.yaml | 6 +- .golangci.yml | 3 + cmd/api-syncagent/kcp.go | 109 +++++++----------- cmd/api-syncagent/main.go | 2 +- internal/controller/apiexport/controller.go | 10 +- internal/controller/apiexport/reconciler.go | 10 +- .../apiresourceschema/controller.go | 6 +- internal/controller/sync/controller.go | 18 +-- internal/kcp/apiresourceschema.go | 14 +-- internal/sync/meta.go | 3 +- internal/sync/metadata.go | 3 +- internal/sync/object_syncer.go | 3 +- internal/sync/state_store.go | 4 +- internal/sync/syncer.go | 3 +- internal/sync/syncer_test.go | 3 +- internal/sync/templating/naming.go | 4 +- internal/sync/templating/naming_test.go | 4 +- internal/sync/templating/related.go | 4 +- internal/sync/templating/templating_test.go | 4 +- test/e2e/sync/apiexportendpointslice_test.go | 2 +- test/e2e/sync/primary_test.go | 3 +- test/e2e/sync/related_test.go | 2 +- test/utils/fixtures.go | 1 - 23 files changed, 104 insertions(+), 117 deletions(-) diff --git a/.gimps.yaml b/.gimps.yaml index 6276ebe..d939b9f 100644 --- a/.gimps.yaml +++ b/.gimps.yaml @@ -21,12 +21,14 @@ sets: - 'k8s.io/**' - 'sigs.k8s.io/controller-runtime/**' - 'sigs.k8s.io/controller-tools/**' + - 'sigs.k8s.io/multicluster-runtime/**' - 'sigs.k8s.io/yaml/**' - 'github.com/kcp-dev/client-go/**' - 'github.com/kcp-dev/kubernetes/**' - name: kcp patterns: - 'github.com/kcp-dev/kcp/**' - - 'github.com/kcp-dev/multicluster-provider/**' + - 'github.com/kcp-dev/sdk/**' + - 'github.com/kcp-dev/logicalcluster/**' - 'github.com/kcp-dev/code-generator/**' - - 'sigs.k8s.io/multicluster-runtime/**' + - 'github.com/kcp-dev/multicluster-provider/**' diff --git a/.golangci.yml b/.golangci.yml index fd06ffa..03a4680 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -74,6 +74,9 @@ linters: # Controller Runtime - pkg: sigs.k8s.io/controller-runtime/pkg/client alias: ctrlruntimeclient + # kcp APIs + - pkg: github.com/kcp-dev/sdk/apis/(\w+)/(v[\w\d]+) + alias: kcp$1$2 no-unaliased: true exclusions: generated: lax diff --git a/cmd/api-syncagent/kcp.go b/cmd/api-syncagent/kcp.go index 5a1f054..ebdd520 100644 --- a/cmd/api-syncagent/kcp.go +++ b/cmd/api-syncagent/kcp.go @@ -22,13 +22,12 @@ import ( "fmt" "regexp" - "github.com/kcp-dev/logicalcluster/v3" - "github.com/kcp-dev/api-syncagent/internal/kcp" - kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" - kcpdevcore "github.com/kcp-dev/sdk/apis/core" - kcpdevcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpcore "github.com/kcp-dev/sdk/apis/core" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" @@ -60,18 +59,18 @@ func setupEndpointKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { scheme := runtime.NewScheme() - if err := kcpdevv1alpha1.AddToScheme(scheme); err != nil { - return nil, fmt.Errorf("failed to register scheme %s: %w", kcpdevv1alpha1.SchemeGroupVersion, err) + if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpapisv1alpha1.SchemeGroupVersion, err) } - if err := kcpdevcorev1alpha1.AddToScheme(scheme); err != nil { - return nil, fmt.Errorf("failed to register scheme %s: %w", kcpdevcorev1alpha1.SchemeGroupVersion, err) + if err := kcpcorev1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpcorev1alpha1.SchemeGroupVersion, err) } // RBAC in kcp might be very tight and might not allow to list/watch all objects; // restrict the cache's selectors accordingly so we can still make use of caching. byObject := map[ctrlruntimeclient.Object]cache.ByObject{ - &kcpdevv1alpha1.APIExportEndpointSlice{}: { + &kcpapisv1alpha1.APIExportEndpointSlice{}: { Field: fields.SelectorFromSet(fields.Set{"metadata.name": endpoint.EndpointSlice.Name}), }, } @@ -90,18 +89,18 @@ func setupEndpointKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { func setupManagedKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { scheme := runtime.NewScheme() - if err := kcpdevv1alpha1.AddToScheme(scheme); err != nil { - return nil, fmt.Errorf("failed to register scheme %s: %w", kcpdevv1alpha1.SchemeGroupVersion, err) + if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpapisv1alpha1.SchemeGroupVersion, err) } - if err := kcpdevcorev1alpha1.AddToScheme(scheme); err != nil { - return nil, fmt.Errorf("failed to register scheme %s: %w", kcpdevcorev1alpha1.SchemeGroupVersion, err) + if err := kcpcorev1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpcorev1alpha1.SchemeGroupVersion, err) } // RBAC in kcp might be very tight and might not allow to list/watch all objects; // restrict the cache's selectors accordingly so we can still make use of caching. byObject := map[ctrlruntimeclient.Object]cache.ByObject{ - &kcpdevv1alpha1.APIExport{}: { + &kcpapisv1alpha1.APIExport{}: { Field: fields.SelectorFromSet(fields.Set{"metadata.name": endpoint.APIExport.Name}), }, } @@ -145,11 +144,11 @@ type syncEndpoint struct { func resolveSyncEndpoint(ctx context.Context, initialRestConfig *rest.Config, endpointSliceRef string, apiExportRef string) (*syncEndpoint, error) { // construct temporary, uncached client scheme := runtime.NewScheme() - if err := kcpdevcorev1alpha1.AddToScheme(scheme); err != nil { - return nil, fmt.Errorf("failed to register scheme %s: %w", kcpdevcorev1alpha1.SchemeGroupVersion, err) + if err := kcpcorev1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpcorev1alpha1.SchemeGroupVersion, err) } - if err := kcpdevv1alpha1.AddToScheme(scheme); err != nil { - return nil, fmt.Errorf("failed to register scheme %s: %w", kcpdevv1alpha1.SchemeGroupVersion, err) + if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpapisv1alpha1.SchemeGroupVersion, err) } clientOpts := ctrlruntimeclient.Options{Scheme: scheme} @@ -160,58 +159,38 @@ func resolveSyncEndpoint(ctx context.Context, initialRestConfig *rest.Config, en se := &syncEndpoint{} - // When an endpoint ref is given, both the APIExportEndpointSlice and the APIExport must exist. - if endpointSliceRef != "" { - endpointSlice, err := resolveAPIExportEndpointSlice(ctx, client, endpointSliceRef) - if err != nil { - return nil, fmt.Errorf("failed to resolve APIExportEndpointSlice: %w", err) - } - endpointSlice.Config = initialRestConfig - - // find the APIExport referenced not by the user (can't: both ref parameters to this function - // are mutually exclusive), but in the APIExportEndpointSlice. - restConfig, err := retargetRestConfig(initialRestConfig, endpointSlice.Spec.APIExport.Path) - if err != nil { - return nil, fmt.Errorf("failed to re-target the given kubeconfig to cluster %q: %w", endpointSlice.Spec.APIExport.Path, err) - } - - client, err := ctrlruntimeclient.New(restConfig, clientOpts) - if err != nil { - return nil, fmt.Errorf("failed to create service reader: %w", err) - } + // First we find the APIExportEndpointSlice. + endpointSlice, err := resolveAPIExportEndpointSlice(ctx, client, endpointSliceRef) + if err != nil { + return nil, fmt.Errorf("failed to resolve APIExportEndpointSlice: %w", err) + } + endpointSlice.Config = initialRestConfig - apiExport, err := resolveAPIExport(ctx, client, endpointSlice.Spec.APIExport.Name) - if err != nil { - return nil, fmt.Errorf("failed to resolve APIExport: %w", err) - } - apiExport.Config = restConfig - - se.APIExport = apiExport - se.EndpointSlice = &endpointSlice - } else { // if an export ref is given, the endpoint slice is optional (for compat with kcp <0.28) - apiExport, err := resolveAPIExport(ctx, client, apiExportRef) - if err != nil { - return nil, fmt.Errorf("failed to resolve APIExport: %w", err) - } - apiExport.Config = initialRestConfig + // Now we find the APIExport referenced in the APIExportEndpointSlice. + restConfig, err := retargetRestConfig(initialRestConfig, endpointSlice.Spec.APIExport.Path) + if err != nil { + return nil, fmt.Errorf("failed to re-target the given kubeconfig to cluster %q: %w", endpointSlice.Spec.APIExport.Path, err) + } - se.APIExport = apiExport + client, err = ctrlruntimeclient.New(restConfig, clientOpts) + if err != nil { + return nil, fmt.Errorf("failed to create service reader: %w", err) + } - // try to find an endpoint slice in the same workspace with the same name as the APIExport - endpointSlice, err := resolveAPIExportEndpointSlice(ctx, client, apiExportRef) - if ctrlruntimeclient.IgnoreNotFound(err) != nil { - return nil, fmt.Errorf("failed to resolve APIExportEndpointSlice: %w", err) - } else if err == nil { - apiExport.Config = initialRestConfig - se.EndpointSlice = &endpointSlice - } + apiExport, err := resolveAPIExport(ctx, client, endpointSlice.Spec.APIExport.Name) + if err != nil { + return nil, fmt.Errorf("failed to resolve APIExport: %w", err) } + apiExport.Config = restConfig + + se.APIExport = apiExport + se.EndpointSlice = endpointSlice return se, nil } func resolveAPIExportEndpointSlice(ctx context.Context, client ctrlruntimeclient.Client, ref string) (qualifiedAPIExportEndpointSlice, error) { - endpointSlice := &kcpdevv1alpha1.APIExportEndpointSlice{} + endpointSlice := &kcpapisv1alpha1.APIExportEndpointSlice{} key := types.NamespacedName{Name: ref} if err := client.Get(ctx, key, endpointSlice); err != nil { return qualifiedAPIExportEndpointSlice{}, fmt.Errorf("failed to get APIExportEndpointSlice %q: %w", ref, err) @@ -232,7 +211,7 @@ func resolveAPIExportEndpointSlice(ctx context.Context, client ctrlruntimeclient } func resolveAPIExport(ctx context.Context, client ctrlruntimeclient.Client, ref string) (qualifiedAPIExport, error) { - apiExport := &kcpdevv1alpha1.APIExport{} + apiExport := &kcpapisv1alpha1.APIExport{} key := types.NamespacedName{Name: ref} if err := client.Get(ctx, key, apiExport); err != nil { return qualifiedAPIExport{}, fmt.Errorf("failed to get APIExport %q: %w", ref, err) @@ -253,13 +232,13 @@ func resolveAPIExport(ctx context.Context, client ctrlruntimeclient.Client, ref } func resolveCurrentCluster(ctx context.Context, client ctrlruntimeclient.Client) (logicalcluster.Name, logicalcluster.Path, error) { - lc := &kcpdevcorev1alpha1.LogicalCluster{} + lc := &kcpcorev1alpha1.LogicalCluster{} if err := client.Get(ctx, types.NamespacedName{Name: kcp.IdentityClusterName}, lc); err != nil { return "", logicalcluster.None, fmt.Errorf("failed to resolve current workspace: %w", err) } lcName := logicalcluster.From(lc) - lcPath := logicalcluster.NewPath(lc.Annotations[kcpdevcore.LogicalClusterPathAnnotationKey]) + lcPath := logicalcluster.NewPath(lc.Annotations[kcpcore.LogicalClusterPathAnnotationKey]) return lcName, lcPath, nil } diff --git a/cmd/api-syncagent/main.go b/cmd/api-syncagent/main.go index 9deb511..770217c 100644 --- a/cmd/api-syncagent/main.go +++ b/cmd/api-syncagent/main.go @@ -36,7 +36,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/version" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" diff --git a/internal/controller/apiexport/controller.go b/internal/controller/apiexport/controller.go index a1654b7..c4e581e 100644 --- a/internal/controller/apiexport/controller.go +++ b/internal/controller/apiexport/controller.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "github.com/kcp-dev/api-syncagent/internal/controllerutil" @@ -31,7 +30,8 @@ import ( "github.com/kcp-dev/api-syncagent/internal/resources/reconciling" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" @@ -109,7 +109,7 @@ func Add( // Watch for changes to APIExport on the kcp side to start/restart the actual syncing controllers; // the cache is already restricted by a fieldSelector in the main.go to respect the RBC restrictions, // so there is no need here to add an additional filter. - WatchesRawSource(source.Kind(kcpCluster.GetCache(), &kcpdevv1alpha1.APIExport{}, controllerutil.EnqueueConst[*kcpdevv1alpha1.APIExport]("dummy"))). + WatchesRawSource(source.Kind(kcpCluster.GetCache(), &kcpapisv1alpha1.APIExport{}, controllerutil.EnqueueConst[*kcpapisv1alpha1.APIExport]("dummy"))). // Watch for changes to PublishedResources on the local service cluster Watches(&syncagentv1alpha1.PublishedResource{}, controllerutil.EnqueueConst[ctrlruntimeclient.Object]("dummy"), builder.WithPredicates(predicateutil.ByLabels(prFilter), hasARS)). Build(reconciler) @@ -120,7 +120,7 @@ func Add( func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { r.log.Debug("Processing") - apiExport := &kcpdevv1alpha1.APIExport{} + apiExport := &kcpapisv1alpha1.APIExport{} if err := r.kcpClient.Get(ctx, types.NamespacedName{Name: r.apiExportName}, apiExport); err != nil { return reconcile.Result{}, ctrlruntimeclient.IgnoreNotFound(err) } @@ -128,7 +128,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) ( return reconcile.Result{}, r.reconcile(ctx, apiExport) } -func (r *Reconciler) reconcile(ctx context.Context, apiExport *kcpdevv1alpha1.APIExport) error { +func (r *Reconciler) reconcile(ctx context.Context, apiExport *kcpapisv1alpha1.APIExport) error { // find all PublishedResources; we keep those that are not yet converted into ARS, // just to reduce the amount of re-reconciles when the agent processes a number of PRs in a row // and would constantly update the APIExport; instead we rely on kcp to handle the eventual diff --git a/internal/controller/apiexport/reconciler.go b/internal/controller/apiexport/reconciler.go index 6be8793..f462341 100644 --- a/internal/controller/apiexport/reconciler.go +++ b/internal/controller/apiexport/reconciler.go @@ -24,7 +24,7 @@ import ( "github.com/kcp-dev/api-syncagent/internal/resources/reconciling" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -61,7 +61,7 @@ func (r *Reconciler) createAPIExportReconciler( recorder record.EventRecorder, ) reconciling.NamedAPIExportReconcilerFactory { return func() (string, reconciling.APIExportReconciler) { - return apiExportName, func(existing *kcpdevv1alpha1.APIExport) (*kcpdevv1alpha1.APIExport, error) { + return apiExportName, func(existing *kcpapisv1alpha1.APIExport) (*kcpapisv1alpha1.APIExport, error) { if existing.Annotations == nil { existing.Annotations = map[string]string{} } @@ -102,8 +102,8 @@ func (r *Reconciler) createAPIExportReconciler( // add our missing claims for _, claimed := range claimsToAdd { - existing.Spec.PermissionClaims = append(existing.Spec.PermissionClaims, kcpdevv1alpha1.PermissionClaim{ - GroupResource: kcpdevv1alpha1.GroupResource{ + existing.Spec.PermissionClaims = append(existing.Spec.PermissionClaims, kcpapisv1alpha1.PermissionClaim{ + GroupResource: kcpapisv1alpha1.GroupResource{ Group: claimed.Group, Resource: claimed.Resource, }, @@ -121,7 +121,7 @@ func (r *Reconciler) createAPIExportReconciler( } // prevent reconcile loops by ensuring a stable order - slices.SortFunc(existing.Spec.PermissionClaims, func(a, b kcpdevv1alpha1.PermissionClaim) int { + slices.SortFunc(existing.Spec.PermissionClaims, func(a, b kcpapisv1alpha1.PermissionClaim) int { if a.Group != b.Group { return strings.Compare(a.Group, b.Group) } diff --git a/internal/controller/apiresourceschema/controller.go b/internal/controller/apiresourceschema/controller.go index d5dea39..3b4c981 100644 --- a/internal/controller/apiresourceschema/controller.go +++ b/internal/controller/apiresourceschema/controller.go @@ -21,7 +21,6 @@ import ( "fmt" "reflect" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "github.com/kcp-dev/api-syncagent/internal/controllerutil/predicate" @@ -30,7 +29,8 @@ import ( "github.com/kcp-dev/api-syncagent/internal/projection" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -159,7 +159,7 @@ func (r *Reconciler) reconcile(ctx context.Context, log *zap.SugaredLogger, pubR arsName := kcp.GetAPIResourceSchemaName(projectedCRD) // ensure ARS exists (don't try to reconcile it, it's basically entirely immutable) - ars := &kcpdevv1alpha1.APIResourceSchema{} + ars := &kcpapisv1alpha1.APIResourceSchema{} err = r.kcpClient.Get(ctx, types.NamespacedName{Name: arsName}, ars, &ctrlruntimeclient.GetOptions{}) if apierrors.IsNotFound(err) { diff --git a/internal/controller/sync/controller.go b/internal/controller/sync/controller.go index 1622d80..dbd2a42 100644 --- a/internal/controller/sync/controller.go +++ b/internal/controller/sync/controller.go @@ -22,7 +22,6 @@ import ( "time" "github.com/go-logr/zapr" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "github.com/kcp-dev/api-syncagent/internal/discovery" @@ -31,13 +30,9 @@ import ( "github.com/kcp-dev/api-syncagent/internal/sync" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" kcpcore "github.com/kcp-dev/sdk/apis/core" - kcpdevcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" - mccontroller "sigs.k8s.io/multicluster-runtime/pkg/controller" - mchandler "sigs.k8s.io/multicluster-runtime/pkg/handler" - mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" - mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" - mcsource "sigs.k8s.io/multicluster-runtime/pkg/source" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -52,6 +47,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + mccontroller "sigs.k8s.io/multicluster-runtime/pkg/controller" + mchandler "sigs.k8s.io/multicluster-runtime/pkg/handler" + mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" + mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" + mcsource "sigs.k8s.io/multicluster-runtime/pkg/source" ) const ( @@ -215,8 +215,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, request mcreconcile.Request) // if desired, fetch the workspace path as well (some downstream service providers might make use of it, // but since it requires an additional permission claim, it's optional) if r.pubRes.Spec.EnableWorkspacePaths { - lc := &kcpdevcorev1alpha1.LogicalCluster{} - if err := vwClient.Get(ctx, types.NamespacedName{Name: kcpdevcorev1alpha1.LogicalClusterName}, lc); err != nil { + lc := &kcpcorev1alpha1.LogicalCluster{} + if err := vwClient.Get(ctx, types.NamespacedName{Name: kcpcorev1alpha1.LogicalClusterName}, lc); err != nil { recorder.Event(remoteObj, corev1.EventTypeWarning, "ReconcilingError", "Failed to retrieve workspace path, cannot process object.") return reconcile.Result{}, fmt.Errorf("failed to retrieve remote logicalcluster: %w", err) } diff --git a/internal/kcp/apiresourceschema.go b/internal/kcp/apiresourceschema.go index c2ea80b..8f5c2e9 100644 --- a/internal/kcp/apiresourceschema.go +++ b/internal/kcp/apiresourceschema.go @@ -22,22 +22,22 @@ import ( "github.com/kcp-dev/api-syncagent/internal/crypto" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func CreateAPIResourceSchema(crd *apiextensionsv1.CustomResourceDefinition, name string, agentName string) (*kcpdevv1alpha1.APIResourceSchema, error) { +func CreateAPIResourceSchema(crd *apiextensionsv1.CustomResourceDefinition, name string, agentName string) (*kcpapisv1alpha1.APIResourceSchema, error) { // prefix is irrelevant as the name is overridden later - converted, err := kcpdevv1alpha1.CRDToAPIResourceSchema(crd, "irrelevant") + converted, err := kcpapisv1alpha1.CRDToAPIResourceSchema(crd, "irrelevant") if err != nil { return nil, fmt.Errorf("failed to convert CRD: %w", err) } - ars := &kcpdevv1alpha1.APIResourceSchema{} + ars := &kcpapisv1alpha1.APIResourceSchema{} ars.TypeMeta = metav1.TypeMeta{ - APIVersion: kcpdevv1alpha1.SchemeGroupVersion.String(), + APIVersion: kcpapisv1alpha1.SchemeGroupVersion.String(), Kind: "APIResourceSchema", } @@ -55,9 +55,9 @@ func CreateAPIResourceSchema(crd *apiextensionsv1.CustomResourceDefinition, name ars.Spec.Versions = converted.Spec.Versions if len(converted.Spec.Versions) > 1 { - ars.Spec.Conversion = &kcpdevv1alpha1.CustomResourceConversion{ + ars.Spec.Conversion = &kcpapisv1alpha1.CustomResourceConversion{ // as of kcp 0.27, there is no constant for this - Strategy: kcpdevv1alpha1.ConversionStrategyType("None"), + Strategy: kcpapisv1alpha1.ConversionStrategyType("None"), } } diff --git a/internal/sync/meta.go b/internal/sync/meta.go index 69ca51d..c5bf76f 100644 --- a/internal/sync/meta.go +++ b/internal/sync/meta.go @@ -19,11 +19,12 @@ package sync import ( "context" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "github.com/kcp-dev/api-syncagent/internal/crypto" + "github.com/kcp-dev/logicalcluster/v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" diff --git a/internal/sync/metadata.go b/internal/sync/metadata.go index afcb848..d42d5c0 100644 --- a/internal/sync/metadata.go +++ b/internal/sync/metadata.go @@ -21,8 +21,6 @@ import ( "maps" "strings" - mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" @@ -30,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" ) func stripMetadata(obj *unstructured.Unstructured) error { diff --git a/internal/sync/object_syncer.go b/internal/sync/object_syncer.go index eb4db73..a514e96 100644 --- a/internal/sync/object_syncer.go +++ b/internal/sync/object_syncer.go @@ -22,12 +22,13 @@ import ( "slices" jsonpatch "github.com/evanphx/json-patch/v5" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "k8c.io/reconciler/pkg/equality" "github.com/kcp-dev/api-syncagent/internal/mutation" + "github.com/kcp-dev/logicalcluster/v3" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/internal/sync/state_store.go b/internal/sync/state_store.go index 47fab7e..6cd8641 100644 --- a/internal/sync/state_store.go +++ b/internal/sync/state_store.go @@ -21,10 +21,10 @@ import ( "fmt" "strings" - "github.com/kcp-dev/logicalcluster/v3" - "github.com/kcp-dev/api-syncagent/internal/crypto" + "github.com/kcp-dev/logicalcluster/v3" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" diff --git a/internal/sync/syncer.go b/internal/sync/syncer.go index a741ed5..7aad7f2 100644 --- a/internal/sync/syncer.go +++ b/internal/sync/syncer.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "github.com/kcp-dev/api-syncagent/internal/mutation" @@ -28,6 +27,8 @@ import ( "github.com/kcp-dev/api-syncagent/internal/sync/templating" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" diff --git a/internal/sync/syncer_test.go b/internal/sync/syncer_test.go index 0b21532..75055e8 100644 --- a/internal/sync/syncer_test.go +++ b/internal/sync/syncer_test.go @@ -23,7 +23,6 @@ import ( "os" "testing" - "github.com/kcp-dev/logicalcluster/v3" "go.uber.org/zap" "github.com/kcp-dev/api-syncagent/internal/mutation" @@ -31,6 +30,8 @@ import ( "github.com/kcp-dev/api-syncagent/internal/test/diff" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" diff --git a/internal/sync/templating/naming.go b/internal/sync/templating/naming.go index e3f76a9..32058b3 100644 --- a/internal/sync/templating/naming.go +++ b/internal/sync/templating/naming.go @@ -20,11 +20,11 @@ import ( "fmt" "strings" - "github.com/kcp-dev/logicalcluster/v3" - "github.com/kcp-dev/api-syncagent/internal/crypto" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" ) diff --git a/internal/sync/templating/naming_test.go b/internal/sync/templating/naming_test.go index a72541c..471ae63 100644 --- a/internal/sync/templating/naming_test.go +++ b/internal/sync/templating/naming_test.go @@ -19,10 +19,10 @@ package templating import ( "testing" - "github.com/kcp-dev/logicalcluster/v3" - syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" ) diff --git a/internal/sync/templating/related.go b/internal/sync/templating/related.go index 1097448..dcaf0ce 100644 --- a/internal/sync/templating/related.go +++ b/internal/sync/templating/related.go @@ -17,10 +17,10 @@ limitations under the License. package templating import ( - "github.com/kcp-dev/logicalcluster/v3" - syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) diff --git a/internal/sync/templating/templating_test.go b/internal/sync/templating/templating_test.go index e432d79..9af6e2b 100644 --- a/internal/sync/templating/templating_test.go +++ b/internal/sync/templating/templating_test.go @@ -19,11 +19,11 @@ package templating import ( "testing" - "github.com/kcp-dev/logicalcluster/v3" - crds "github.com/kcp-dev/api-syncagent/test/crds/example/v1" "github.com/kcp-dev/api-syncagent/test/utils" + "github.com/kcp-dev/logicalcluster/v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/test/e2e/sync/apiexportendpointslice_test.go b/test/e2e/sync/apiexportendpointslice_test.go index 3e928b7..5c8befe 100644 --- a/test/e2e/sync/apiexportendpointslice_test.go +++ b/test/e2e/sync/apiexportendpointslice_test.go @@ -25,11 +25,11 @@ import ( "time" "github.com/go-logr/logr" - "github.com/kcp-dev/logicalcluster/v3" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" "github.com/kcp-dev/api-syncagent/test/utils" + "github.com/kcp-dev/logicalcluster/v3" kcpdevv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" rbacv1 "k8s.io/api/rbac/v1" diff --git a/test/e2e/sync/primary_test.go b/test/e2e/sync/primary_test.go index 52e501a..6ed78cc 100644 --- a/test/e2e/sync/primary_test.go +++ b/test/e2e/sync/primary_test.go @@ -27,11 +27,12 @@ import ( "time" "github.com/go-logr/logr" - "github.com/kcp-dev/logicalcluster/v3" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" "github.com/kcp-dev/api-syncagent/test/utils" + "github.com/kcp-dev/logicalcluster/v3" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/test/e2e/sync/related_test.go b/test/e2e/sync/related_test.go index 29646c0..ea724e3 100644 --- a/test/e2e/sync/related_test.go +++ b/test/e2e/sync/related_test.go @@ -28,7 +28,6 @@ import ( "time" "github.com/go-logr/logr" - "github.com/kcp-dev/logicalcluster/v3" "github.com/kcp-dev/api-syncagent/internal/projection" "github.com/kcp-dev/api-syncagent/internal/test/diff" @@ -36,6 +35,7 @@ import ( crds "github.com/kcp-dev/api-syncagent/test/crds/example/v1" "github.com/kcp-dev/api-syncagent/test/utils" + "github.com/kcp-dev/logicalcluster/v3" kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" corev1 "k8s.io/api/core/v1" diff --git a/test/utils/fixtures.go b/test/utils/fixtures.go index ff1f6aa..6c68acf 100644 --- a/test/utils/fixtures.go +++ b/test/utils/fixtures.go @@ -26,7 +26,6 @@ import ( "time" "github.com/kcp-dev/logicalcluster/v3" - kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" kcptenancyv1alpha1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1" From dcdfafd8826b17e24e9042528217fffa7a16f425 Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Fri, 12 Dec 2025 17:33:25 +0100 Subject: [PATCH 5/8] bump to kcp 0.29 and latest multicluster-provider commit On-behalf-of: @SAP christoph.mewes@sap.com --- go.mod | 72 +++++++++++------------ go.sum | 181 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 124 insertions(+), 129 deletions(-) diff --git a/go.mod b/go.mod index b34ca02..30a8780 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.24.0 replace github.com/kcp-dev/api-syncagent/sdk => ./sdk replace ( - k8s.io/apiextensions-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251113163256-f038ec6bf609 - k8s.io/apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251113163256-f038ec6bf609 + k8s.io/apiextensions-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251209073509-71e0f2506325 + k8s.io/apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251209073509-71e0f2506325 ) require ( @@ -14,35 +14,35 @@ require ( github.com/evanphx/json-patch/v5 v5.9.11 github.com/go-logr/logr v1.4.3 github.com/go-logr/zapr v1.3.0 - github.com/google/cel-go v0.23.2 + github.com/google/cel-go v0.26.0 github.com/google/go-cmp v0.7.0 github.com/kcp-dev/api-syncagent/sdk v0.0.0-00010101000000-000000000000 - github.com/kcp-dev/kcp v0.29.0 + github.com/kcp-dev/kcp v0.29.1-0.20251210093424-08fb9eb48494 github.com/kcp-dev/logicalcluster/v3 v3.0.5 - github.com/kcp-dev/multicluster-provider v0.2.1-0.20250901093233-9ef571c8bd47 - github.com/kcp-dev/sdk v0.29.0 + github.com/kcp-dev/multicluster-provider v0.3.2-0.20251209135920-e758bf0f4e48 + github.com/kcp-dev/sdk v0.28.1-0.20251209130449-436a0347809b github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 github.com/spf13/cobra v1.10.1 github.com/spf13/pflag v1.0.10 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 k8c.io/reconciler v0.5.0 - k8s.io/api v0.33.5 - k8s.io/apiextensions-apiserver v0.33.5 - k8s.io/apimachinery v0.33.5 - k8s.io/apiserver v0.33.5 - k8s.io/client-go v0.33.5 - k8s.io/component-base v0.33.5 - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff + k8s.io/api v0.34.2 + k8s.io/apiextensions-apiserver v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/apiserver v0.34.2 + k8s.io/client-go v0.34.2 + k8s.io/component-base v0.34.2 + k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 - sigs.k8s.io/controller-runtime v0.21.0 - sigs.k8s.io/multicluster-runtime v0.21.0-alpha.9 - sigs.k8s.io/yaml v1.4.0 + sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/multicluster-runtime v0.22.4-beta.1 + sigs.k8s.io/yaml v1.6.0 ) require ( - cel.dev/expr v0.19.1 // indirect + cel.dev/expr v0.24.0 // indirect dario.cat/mergo v1.0.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect @@ -78,7 +78,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect @@ -87,15 +87,14 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kcp-dev/apimachinery/v2 v2.29.0 // indirect - github.com/kcp-dev/client-go v0.29.0 // indirect - github.com/kcp-dev/kcp/sdk v0.28.1 // indirect + github.com/kcp-dev/apimachinery/v2 v2.29.1-0.20251209121225-cf3c0b624983 // indirect + github.com/kcp-dev/client-go v0.28.1-0.20251209170419-79146629224a // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.9.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/gomega v1.38.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -109,9 +108,9 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/etcd/api/v3 v3.5.21 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect - go.etcd.io/etcd/client/v3 v3.5.21 // indirect + go.etcd.io/etcd/api/v3 v3.6.4 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect + go.etcd.io/etcd/client/v3 v3.6.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect @@ -123,20 +122,21 @@ require ( go.opentelemetry.io/otel/trace v1.35.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/crypto v0.45.0 // indirect + golang.org/x/crypto v0.46.0 // indirect golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.14.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect - google.golang.org/grpc v1.71.1 // indirect + google.golang.org/grpc v1.72.1 // indirect google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -144,9 +144,9 @@ require ( k8s.io/component-helpers v0.33.5 // indirect k8s.io/controller-manager v0.33.5 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kubernetes v1.33.5 // indirect + k8s.io/kubernetes v1.34.2 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) diff --git a/go.sum b/go.sum index 27bc056..a96af45 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= -cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -87,17 +87,16 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ 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.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +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.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= -github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= +github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +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.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= @@ -109,12 +108,12 @@ 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.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 v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-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.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -123,30 +122,28 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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/kcp-dev/apimachinery/v2 v2.29.0 h1:hoIomHs0v8N9+kvKYfjM/tVh5l27NH1zCDUV0kRLxn4= -github.com/kcp-dev/apimachinery/v2 v2.29.0/go.mod h1:KizREtsxF5malriCj+Cr87TghwHQIMZYXbVycNBbGHs= -github.com/kcp-dev/client-go v0.29.0 h1:F5pX3Ohy2jUjPZVHNkLCxtFIwvdB8rB/iu3Ops8Yw8w= -github.com/kcp-dev/client-go v0.29.0/go.mod h1:Kl+d0bVdvyLULfZLjWA2Zfem8iOJRC0YbK6PpCOwUZg= -github.com/kcp-dev/kcp v0.29.0 h1:FAfbOEJ6jH0YxhCnjMSQndFCr3ORZKO1wrLZTqV0MYo= -github.com/kcp-dev/kcp v0.29.0/go.mod h1:4+DL+QaHRyb8lQjTWR27KtQ8NBvfrXNI9GIuk1kXkuQ= -github.com/kcp-dev/kcp/sdk v0.28.1 h1:bTtuHVjFRjbwFEqXTPxc1J1JP2Hc3mTYqQ2xfJsi16M= -github.com/kcp-dev/kcp/sdk v0.28.1/go.mod h1:8oZpWxkoMu2TDpx5DgdIGDigByKHKkeqVMA4GiWneoI= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251113163256-f038ec6bf609 h1:2eF4Jq/1bTL2OObwStwOOik1WdLVPOSwHyuiRXjWWbg= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251113163256-f038ec6bf609/go.mod h1:7YRhmmIzd7elyfRQ96hbOfFq7+OvGNZljO5Z/E+mLto= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251113163256-f038ec6bf609 h1:yMWrq/DpozipIHUNrjnqOHr/55rl8eMFlfJJFYSjaTk= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251113163256-f038ec6bf609/go.mod h1:nG4FME1ik2lKNo+jGhrGsZRiI/+1r8Nuldah1bPqqg8= +github.com/kcp-dev/apimachinery/v2 v2.29.1-0.20251209121225-cf3c0b624983 h1:4hCauFTFMwvIhwL9fqZ5omjeZ+vsOUNO1tLsrCeprxM= +github.com/kcp-dev/apimachinery/v2 v2.29.1-0.20251209121225-cf3c0b624983/go.mod h1:DOv0iw5tcgzFBhudwLFe2WHCLqtlgNkuO4AcqbZ4zVo= +github.com/kcp-dev/client-go v0.28.1-0.20251209170419-79146629224a h1:Fv8/Me8eSMcLScRdXTsd0wR4v1Ies8/WdXdbepOFE9s= +github.com/kcp-dev/client-go v0.28.1-0.20251209170419-79146629224a/go.mod h1:SWYbL1dVmUvLQ8DpcQq+tIH14R1i5e4wh1NTW4YaUYc= +github.com/kcp-dev/kcp v0.29.1-0.20251210093424-08fb9eb48494 h1:K5eM4sj+sdKiJ9xwpKpG4aqjNSrPpC8V4LWVHTL3pTc= +github.com/kcp-dev/kcp v0.29.1-0.20251210093424-08fb9eb48494/go.mod h1:5A7we658aPU8CIHN2ht/aejWah5aCn0Pos7UOl7Nej4= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251209073509-71e0f2506325 h1:MmzvuhedtyTW49qG0VWf9X54OwaRslUk6KyC7vhsuI4= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20251209073509-71e0f2506325/go.mod h1:NL2CyapDmJ+5XVVY8qr6niVA3UHVF17kPl0zh6ohkVM= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251209073509-71e0f2506325 h1:YhSbN6w0bbxt0kKS7yIUivV9KBo1488HG0pPnYojP9U= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20251209073509-71e0f2506325/go.mod h1:msyjTyI8TyfhYybEkao5LA8bUrVqz1xhic5zxsfejoM= github.com/kcp-dev/logicalcluster/v3 v3.0.5 h1:JbYakokb+5Uinz09oTXomSUJVQsqfxEvU4RyHUYxHOU= github.com/kcp-dev/logicalcluster/v3 v3.0.5/go.mod h1:EWBUBxdr49fUB1cLMO4nOdBWmYifLbP1LfoL20KkXYY= -github.com/kcp-dev/multicluster-provider v0.2.1-0.20250901093233-9ef571c8bd47 h1:CPrrjZevZqUHrW/BBVvSjzSFrFnASm7srHznhTBhJ0Y= -github.com/kcp-dev/multicluster-provider v0.2.1-0.20250901093233-9ef571c8bd47/go.mod h1:RhSDOKAn/ROpSIFt140Tk2LsM7PXa3a3rov4qbqFERc= -github.com/kcp-dev/sdk v0.29.0 h1:zXJjtcKNduy8MntSdf2CoXSMVWybptPJwq0zKBkf0Iw= -github.com/kcp-dev/sdk v0.29.0/go.mod h1:iOA3cEMbI7jeLHbfc0GlHSXRHfyhOWC0NDEJRdNcBY8= +github.com/kcp-dev/multicluster-provider v0.3.2-0.20251209135920-e758bf0f4e48 h1:QgE4koFt6oU+3cfZquU467Ybv9H/HYikth5neMJeUe0= +github.com/kcp-dev/multicluster-provider v0.3.2-0.20251209135920-e758bf0f4e48/go.mod h1:4QGU39wyNztoYNatdWqbdOV6/R9ZzaIh4DdSj30dm9o= +github.com/kcp-dev/sdk v0.28.1-0.20251209130449-436a0347809b h1:hPwN5SK3L5bx4Ymeb5NeYN0lqIXd+Xt1cAr3qcSlQxU= +github.com/kcp-dev/sdk v0.28.1-0.20251209130449-436a0347809b/go.mod h1:kdlYfujcotSPrBzwtI6qrsNo4JQ+GB1o04buOfKUo2c= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -166,8 +163,9 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx 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/onsi/ginkgo/v2 v2.25.1 h1:Fwp6crTREKM+oA6Cz4MsO8RhKQzs2/gOIVOUscMAfZY= @@ -234,22 +232,20 @@ 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.21 h1:A6O2/JDb3tvHhiIz3xf9nJ7REHvtEFJJ3veW3FbCnS8= -go.etcd.io/etcd/api/v3 v3.5.21/go.mod h1:c3aH5wcvXv/9dqIw2Y810LDXJfhSYdHQ0vxmP3CCHVY= -go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoBqUTc= -go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs= -go.etcd.io/etcd/client/v2 v2.305.21 h1:eLiFfexc2mE+pTLz9WwnoEsX5JTTpLCYVivKkmVXIRA= -go.etcd.io/etcd/client/v2 v2.305.21/go.mod h1:OKkn4hlYNf43hpjEM3Ke3aRdUkhSl8xjKjSf8eCq2J8= -go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY= -go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU= -go.etcd.io/etcd/pkg/v3 v3.5.21 h1:jUItxeKyrDuVuWhdh0HtjUANwyuzcb7/FAeUfABmQsk= -go.etcd.io/etcd/pkg/v3 v3.5.21/go.mod h1:wpZx8Egv1g4y+N7JAsqi2zoUiBIUWznLjqJbylDjWgU= -go.etcd.io/etcd/raft/v3 v3.5.21 h1:dOmE0mT55dIUsX77TKBLq+RgyumsQuYeiRQnW/ylugk= -go.etcd.io/etcd/raft/v3 v3.5.21/go.mod h1:fmcuY5R2SNkklU4+fKVBQi2biVp5vafMrWUEj4TJ4Cs= -go.etcd.io/etcd/server/v3 v3.5.21 h1:9w0/k12majtgarGmlMVuhwXRI2ob3/d1Ik3X5TKo0yU= -go.etcd.io/etcd/server/v3 v3.5.21/go.mod h1:G1mOzdwuzKT1VRL7SqRchli/qcFrtLBTAQ4lV20sXXo= +go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= +go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= +go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= +go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= +go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= +go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= +go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= +go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= +go.etcd.io/etcd/pkg/v3 v3.6.4 h1:fy8bmXIec1Q35/jRZ0KOes8vuFxbvdN0aAFqmEfJZWA= +go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= +go.etcd.io/etcd/server/v3 v3.6.4 h1:LsCA7CzjVt+8WGrdsnh6RhC0XqCsLkBly3ve5rTxMAU= +go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg= +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= @@ -278,15 +274,17 @@ go.uber.org/goleak v1.3.1-0.20241121203838-4ff5fa6529ee h1:uOMbcH1Dmxv45VkkpZQYo go.uber.org/goleak v1.3.1-0.20241121203838-4ff5fa6529ee/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -295,48 +293,46 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn 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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= 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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI= -google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +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.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -353,40 +349,39 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8c.io/reconciler v0.5.0 h1:BHpelg1UfI/7oBFctqOq8sX6qzflXpl3SlvHe7e8wak= k8c.io/reconciler v0.5.0/go.mod h1:pT1+SVcVXJQeBJhpJBXQ5XW64QnKKeYTnVlQf0dGE0k= -k8s.io/api v0.33.5 h1:YR+uhYj05jdRpcksv8kjSliW+v9hwXxn6Cv10aR8Juw= -k8s.io/api v0.33.5/go.mod h1:2gzShdwXKT5yPGiqrTrn/U/nLZ7ZyT4WuAj3XGDVgVs= -k8s.io/apimachinery v0.33.5 h1:NiT64hln4TQXeYR18/ES39OrNsjGz8NguxsBgp+6QIo= -k8s.io/apimachinery v0.33.5/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/client-go v0.33.5 h1:I8BdmQGxInpkMEnJvV6iG7dqzP3JRlpZZlib3OMFc3o= -k8s.io/client-go v0.33.5/go.mod h1:W8PQP4MxbM4ypgagVE65mUUqK1/ByQkSALF9tzuQ6u0= -k8s.io/component-base v0.33.5 h1:4D3kxjEx1pJRy3WHAZsmX3+LCpmd4ftE+2J4v6naTnQ= -k8s.io/component-base v0.33.5/go.mod h1:Zma1YjBVuuGxIbspj1vGR3/5blzo2ARf1v0QTtog1to= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= +k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= +k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= k8s.io/component-helpers v0.33.5 h1:1LDSMzn7YTreVLPaOBJK36ase/FWi2sDpeJJvbEBO2s= k8s.io/component-helpers v0.33.5/go.mod h1:C3HsDU2lANSLgTTgMJ0TFnG5xZrVrxR3Ss9n7Wrsw4s= k8s.io/controller-manager v0.33.5 h1:abmssknXnhOhW533583v2SYQObD5RhYiSL7Za1rezGM= k8s.io/controller-manager v0.33.5/go.mod h1:KuQeAlf4vI2+qj5fwPVLaDlbtrTBA/8L/LqQvI74Ow0= 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.33.3 h1:7cQWC+GSH211NgY8LRKjBXNtkzra5SkpYzeZrOt5D+8= -k8s.io/kms v0.33.3/go.mod h1:C1I8mjFFBNzfUZXYt9FZVJ8MJl7ynFbGgZFbBzkBJ3E= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/kubernetes v1.33.5 h1:uf0/5VVQodYf61FudCbwtWjg5ApQcKKA7mwanTLjxyw= -k8s.io/kubernetes v1.33.5/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00= +k8s.io/kms v0.34.2 h1:91rj4MDZLyIT9KxG8J5/CcMH666Z88CF/xJQeuPfJc8= +k8s.io/kms v0.34.2/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/kubernetes v1.34.2 h1:WQdDvYJazkmkwSncgNwGvVtaCt4TYXIU3wSMRgvp3MI= +k8s.io/kubernetes v1.34.2/go.mod h1:m6pZk6a179pRo2wsTiCPORJ86iOEQmfIzUvtyEF8BwA= 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/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= 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/multicluster-runtime v0.21.0-alpha.9 h1:baonM4f081WWct3U7O4EfqrxcUGtmCrFDbsT1FQ8xlo= -sigs.k8s.io/multicluster-runtime v0.21.0-alpha.9/go.mod h1:CpBzLMLQKdm+UCchd2FiGPiDdCxM5dgCCPKuaQ6Fsv0= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/multicluster-runtime v0.22.4-beta.1 h1:0XWbDINepM9UOyLkqhG4g7BtGBFKCDvZFyPsw1vufKE= +sigs.k8s.io/multicluster-runtime v0.22.4-beta.1/go.mod h1:zSMb4mC8MAZK42l8eE1ywkeX6vjuNRenYzJ1w+GPdfI= 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.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From 2509acc76e9d84618dccf9db82e0c0ddaf893791 Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Fri, 12 Dec 2025 17:35:39 +0100 Subject: [PATCH 6/8] remove --apiexport-ref CLI flag On-behalf-of: @SAP christoph.mewes@sap.com --- cmd/api-syncagent/kcp.go | 35 +++++------------ cmd/api-syncagent/main.go | 41 ++++++++++---------- cmd/api-syncagent/options.go | 20 ++-------- test/e2e/sync/apiexportendpointslice_test.go | 8 ++-- test/utils/fixtures.go | 18 +++++++++ test/utils/process.go | 29 +------------- 6 files changed, 57 insertions(+), 94 deletions(-) diff --git a/cmd/api-syncagent/kcp.go b/cmd/api-syncagent/kcp.go index ebdd520..f3fdb92 100644 --- a/cmd/api-syncagent/kcp.go +++ b/cmd/api-syncagent/kcp.go @@ -38,25 +38,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/cluster" ) -// The agent has two potentially different kcp clusters: -// -// endpointCluster - this is where the source of the virtual workspace URLs -// live, i.e. where the APIExport/EndpointSlice. -// managedCluster - this is where the APIExport and APIResourceSchemas -// exist that are meant to be reconciled. -// -// The managedCluster always exists, the endpointCluster only if the workspace -// for the virtual workspace source is different from the managed cluster. - // setupEndpointKcpCluster sets up a plain, non-kcp-aware ctrl-runtime Cluster object // that is solvely used to watch whichever object holds the virtual workspace URLs, // either the APIExport or the APIExportEndpointSlice. -func setupEndpointKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { - // no need for a dedicated endpoint cluster - if endpoint.EndpointSlice == nil || endpoint.EndpointSlice.Cluster == endpoint.APIExport.Cluster { - return nil, nil - } - +func setupEndpointKcpCluster(endpointSlice qualifiedAPIExportEndpointSlice) (cluster.Cluster, error) { scheme := runtime.NewScheme() if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { @@ -71,11 +56,11 @@ func setupEndpointKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { // restrict the cache's selectors accordingly so we can still make use of caching. byObject := map[ctrlruntimeclient.Object]cache.ByObject{ &kcpapisv1alpha1.APIExportEndpointSlice{}: { - Field: fields.SelectorFromSet(fields.Set{"metadata.name": endpoint.EndpointSlice.Name}), + Field: fields.SelectorFromSet(fields.Set{"metadata.name": endpointSlice.Name}), }, } - return cluster.New(endpoint.EndpointSlice.Config, func(o *cluster.Options) { + return cluster.New(endpointSlice.Config, func(o *cluster.Options) { o.Scheme = scheme o.Cache = cache.Options{ Scheme: scheme, @@ -86,7 +71,7 @@ func setupEndpointKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { // setupManagedKcpCluster sets up a plain, non-kcp-aware ctrl-runtime Cluster object // that is solvely used to manage the APIExport and APIResourceSchemas. -func setupManagedKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { +func setupManagedKcpCluster(apiExport qualifiedAPIExport) (cluster.Cluster, error) { scheme := runtime.NewScheme() if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { @@ -101,11 +86,11 @@ func setupManagedKcpCluster(endpoint *syncEndpoint) (cluster.Cluster, error) { // restrict the cache's selectors accordingly so we can still make use of caching. byObject := map[ctrlruntimeclient.Object]cache.ByObject{ &kcpapisv1alpha1.APIExport{}: { - Field: fields.SelectorFromSet(fields.Set{"metadata.name": endpoint.APIExport.Name}), + Field: fields.SelectorFromSet(fields.Set{"metadata.name": apiExport.Name}), }, } - return cluster.New(endpoint.APIExport.Config, func(o *cluster.Options) { + return cluster.New(apiExport.Config, func(o *cluster.Options) { o.Scheme = scheme o.Cache = cache.Options{ Scheme: scheme, @@ -121,18 +106,18 @@ type qualifiedCluster struct { } type qualifiedAPIExport struct { - *kcpdevv1alpha1.APIExport + *kcpapisv1alpha1.APIExport qualifiedCluster } type qualifiedAPIExportEndpointSlice struct { - *kcpdevv1alpha1.APIExportEndpointSlice + *kcpapisv1alpha1.APIExportEndpointSlice qualifiedCluster } type syncEndpoint struct { APIExport qualifiedAPIExport - EndpointSlice *qualifiedAPIExportEndpointSlice + EndpointSlice qualifiedAPIExportEndpointSlice } // resolveSyncEndpoint takes the user provided (usually via CLI flags) APIExportEndpointSliceRef and @@ -141,7 +126,7 @@ type syncEndpoint struct { // must point to the cluster where the APIExport lives, and vice versa for the endpoint slice; // however the endpoint slice references an APIExport in potentially another cluster, and for this // case the initialRestConfig will be rewritten accordingly). -func resolveSyncEndpoint(ctx context.Context, initialRestConfig *rest.Config, endpointSliceRef string, apiExportRef string) (*syncEndpoint, error) { +func resolveSyncEndpoint(ctx context.Context, initialRestConfig *rest.Config, endpointSliceRef string) (*syncEndpoint, error) { // construct temporary, uncached client scheme := runtime.NewScheme() if err := kcpcorev1alpha1.AddToScheme(scheme); err != nil { diff --git a/cmd/api-syncagent/main.go b/cmd/api-syncagent/main.go index 770217c..9dcaa72 100644 --- a/cmd/api-syncagent/main.go +++ b/cmd/api-syncagent/main.go @@ -85,13 +85,11 @@ func main() { func run(ctx context.Context, log *zap.SugaredLogger, opts *Options) error { v := version.NewAppVersion() - hello := log.With("version", v.GitVersion, "name", opts.AgentName) - - if opts.APIExportEndpointSliceRef != "" { - hello = hello.With("apiexportendpointslice", opts.APIExportEndpointSliceRef) - } else { - hello = hello.With("apiexport", opts.APIExportRef) - } + hello := log.With( + "version", v.GitVersion, + "name", opts.AgentName, + "apiexportendpointslice", opts.APIExportEndpointSliceRef, + ) hello.Info("Moin, I'm the kcp Sync Agent") @@ -112,22 +110,20 @@ func run(ctx context.Context, log *zap.SugaredLogger, opts *Options) error { return fmt.Errorf("kcp kubeconfig does not point to a specific workspace") } - // We check if the APIExport/APIExportEndpointSlice exists and extract information we need to set up our kcpCluster. - endpoint, err := resolveSyncEndpoint(ctx, kcpRestConfig, opts.APIExportEndpointSliceRef, opts.APIExportRef) + // We check if the APIExportEndpointSlice exists and extract information we need to set up our kcpCluster. + endpoint, err := resolveSyncEndpoint(ctx, kcpRestConfig, opts.APIExportEndpointSliceRef) if err != nil { return fmt.Errorf("failed to resolve APIExport/EndpointSlice: %w", err) } log.Infow("Resolved APIExport", "name", endpoint.APIExport.Name, "workspace", endpoint.APIExport.Path, "logicalcluster", endpoint.APIExport.Cluster) - - if s := endpoint.EndpointSlice; s != nil { - log.Infow("Using APIExportEndpointSlice", "name", endpoint.EndpointSlice.Name, "workspace", s.Path, "logicalcluster", s.Cluster) - } + log.Infow("Using APIExportEndpointSlice", "name", endpoint.EndpointSlice.Name, "workspace", endpoint.EndpointSlice.Path, "logicalcluster", endpoint.EndpointSlice.Cluster) // init the "permanent" kcp cluster connections - // always need the managedKcpCluster - managedKcpCluster, err := setupManagedKcpCluster(endpoint) + // always need the managedKcpCluster, this is where we will manage the APIExport and + // its resource schemas. + managedKcpCluster, err := setupManagedKcpCluster(endpoint.APIExport) if err != nil { return fmt.Errorf("failed to initialize managed kcp cluster: %w", err) } @@ -138,13 +134,16 @@ func run(ctx context.Context, log *zap.SugaredLogger, opts *Options) error { return fmt.Errorf("failed to add managed kcp cluster runnable: %w", err) } - // the endpoint cluster can be nil - endpointKcpCluster, err := setupEndpointKcpCluster(endpoint) - if err != nil { - return fmt.Errorf("failed to initialize endpoint kcp cluster: %w", err) - } + endpointSliceCluster := managedKcpCluster + + // If needed, start an additional cluster for the endpoint workspace, where + // the EndpointSlice lives. + if endpoint.EndpointSlice.Cluster != endpoint.APIExport.Cluster { + endpointKcpCluster, err := setupEndpointKcpCluster(endpoint.EndpointSlice) + if err != nil { + return fmt.Errorf("failed to initialize endpoint kcp cluster: %w", err) + } - if endpointKcpCluster != nil { if err := mgr.Add(endpointKcpCluster); err != nil { return fmt.Errorf("failed to add endpoint kcp cluster runnable: %w", err) } diff --git a/cmd/api-syncagent/options.go b/cmd/api-syncagent/options.go index 9781cb1..0ad8aba 100644 --- a/cmd/api-syncagent/options.go +++ b/cmd/api-syncagent/options.go @@ -54,15 +54,6 @@ type Options struct { // If not given, defaults to "-syncagent". AgentName string - // APIExportRef references the APIExport within a kcp workspace that this - // Sync Agent should work with by name. The APIExport has to already exist, but it must not have - // any pre-existing resource schemas configured, the agent will fill them in based on - // PublishedResources. - // - // Deprecated: Use APIExportEndpointSliceRef instead. If an APIExport is referenced, the agent - // will attempt to find and use an endpoint slice of the same name. - APIExportRef string - // APIExportEndpointSliceRef references the APIExportEndpointSlice within a kcp workspace that this // Sync Agent should work with by name. The agent will automatically manage the resource schemas // in the APIExport referenced by this endpoint slice. @@ -96,7 +87,6 @@ func (o *Options) AddFlags(flags *pflag.FlagSet) { flags.StringVar(&o.KcpKubeconfig, "kcp-kubeconfig", o.KcpKubeconfig, "kubeconfig file of kcp") flags.StringVar(&o.Namespace, "namespace", o.Namespace, "Kubernetes namespace the Sync Agent is running in") flags.StringVar(&o.AgentName, "agent-name", o.AgentName, "name of this Sync Agent, must not be changed after the first run, can be left blank to auto-generate a name") - flags.StringVar(&o.APIExportRef, "apiexport-ref", o.APIExportRef, "name of the APIExport in kcp that this Sync Agent is powering (deprecated, use --apiexportendpointslice-ref instead)") flags.StringVar(&o.APIExportEndpointSliceRef, "apiexportendpointslice-ref", o.APIExportEndpointSliceRef, "name of the APIExportEndpointSlice in kcp that this Sync Agent is powering") flags.StringVar(&o.PublishedResourceSelectorString, "published-resource-selector", o.PublishedResourceSelectorString, "restrict this Sync Agent to only process PublishedResources matching this label selector (optional)") flags.BoolVar(&o.EnableLeaderElection, "enable-leader-election", o.EnableLeaderElection, "whether to perform leader election") @@ -124,12 +114,8 @@ func (o *Options) Validate() error { } } - if len(o.APIExportRef) == 0 && len(o.APIExportEndpointSliceRef) == 0 { - errs = append(errs, errors.New("either --apiexportendpointslice-ref or --apiexport-ref is required")) - } - - if len(o.APIExportRef) != 0 && len(o.APIExportEndpointSliceRef) != 0 { - errs = append(errs, errors.New("--apiexportendpointslice-ref and --apiexport-ref are mutually exclusive")) + if len(o.APIExportEndpointSliceRef) == 0 { + errs = append(errs, errors.New("--apiexportendpointslice-ref is required")) } if len(o.KcpKubeconfig) == 0 { @@ -156,7 +142,7 @@ func (o *Options) Complete() error { errs := []error{} if len(o.AgentName) == 0 { - o.AgentName = o.APIExportRef + "-syncagent" + o.AgentName = o.APIExportEndpointSliceRef + "-syncagent" } if s := o.PublishedResourceSelectorString; len(s) > 0 { diff --git a/test/e2e/sync/apiexportendpointslice_test.go b/test/e2e/sync/apiexportendpointslice_test.go index 5c8befe..f8073f2 100644 --- a/test/e2e/sync/apiexportendpointslice_test.go +++ b/test/e2e/sync/apiexportendpointslice_test.go @@ -88,8 +88,8 @@ func TestAPIExportEndpointSliceSameCluster(t *testing.T) { t.Fatalf("Failed to create PublishedResource: %v", err) } - // In kcp 0.27, we have to manually create the AEES. To make this test work consistently with - // 0.27 and later versions, we simply always create one. + // Every APIExport we create with CreateOrganization already has an EndpointSlice, + // but we create a custom one just to prove a point. kcpClusterClient := utils.GetKcpAdminClusterClient(t) orgClient := kcpClusterClient.Cluster(logicalcluster.NewPath("root").Join(orgWorkspace)) @@ -110,7 +110,7 @@ func TestAPIExportEndpointSliceSameCluster(t *testing.T) { } // start the agent in the background to update the APIExport with the CronTabs API; - utils.RunEndpointSliceAgent(ctx, t, "bob", orgKubconfig, envtestKubeconfig, endpointSlice.Name, "") + utils.RunAgent(ctx, t, "bob", orgKubconfig, envtestKubeconfig, endpointSlice.Name, "") // wait until the API is available teamClusterPath := logicalcluster.NewPath("root").Join(orgWorkspace).Join("team-1") @@ -253,7 +253,7 @@ func TestAPIExportEndpointSliceDifferentCluster(t *testing.T) { } // start the agent in the background to update the APIExport with the CronTabs API - utils.RunEndpointSliceAgent(ctx, t, "bob", endpointKubeconfig, envtestKubeconfig, endpointSlice.Name, "") + utils.RunAgent(ctx, t, "bob", endpointKubeconfig, envtestKubeconfig, endpointSlice.Name, "") // wait until the API is available diff --git a/test/utils/fixtures.go b/test/utils/fixtures.go index 6c68acf..0005412 100644 --- a/test/utils/fixtures.go +++ b/test/utils/fixtures.go @@ -128,6 +128,24 @@ func CreateAPIExport(t *testing.T, ctx context.Context, client ctrlruntimeclient t.Fatalf("Failed to create APIExport: %v", err) } + // In kcp 0.27, we have to manually create the AEES. To make the tests work consistently with + // 0.27 and later versions, we simply always create one. + endpointSlice := &kcpapisv1alpha1.APIExportEndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: kcpapisv1alpha1.APIExportEndpointSliceSpec{ + APIExport: kcpapisv1alpha1.ExportBindingReference{ + Name: name, + }, + }, + } + + t.Logf("Creating APIExportEndpointSlice %q…", endpointSlice.Name) + if err := client.Create(ctx, endpointSlice); err != nil { + t.Fatalf("Failed to create APIExportEndpointSlice: %v", err) + } + // grant permissions to access/manage the APIExport if rbacSubject != nil { clusterRoleName := "api-syncagent" diff --git a/test/utils/process.go b/test/utils/process.go index ca3690a..e9cbbab 100644 --- a/test/utils/process.go +++ b/test/utils/process.go @@ -68,38 +68,13 @@ func uniqueLogfile(t *testing.T, basename string) string { return fmt.Sprintf("%s_%02d.log", testName, counter) } -func RunEndpointSliceAgent( - ctx context.Context, - t *testing.T, - name string, - kcpKubeconfig string, - localKubeconfig string, - apiExportEndpointSlice string, - labelSelector string, -) context.CancelFunc { - return runAgent(ctx, t, name, kcpKubeconfig, localKubeconfig, "--apiexportendpointslice-ref", apiExportEndpointSlice, labelSelector) -} - func RunAgent( ctx context.Context, t *testing.T, name string, kcpKubeconfig string, localKubeconfig string, - apiExport string, - labelSelector string, -) context.CancelFunc { - return runAgent(ctx, t, name, kcpKubeconfig, localKubeconfig, "--apiexport-ref", apiExport, labelSelector) -} - -func runAgent( - ctx context.Context, - t *testing.T, - name string, - kcpKubeconfig string, - localKubeconfig string, - refFlag string, - refValue string, + apiExportEndpointSlice string, labelSelector string, ) context.CancelFunc { t.Helper() @@ -108,7 +83,7 @@ func runAgent( args := []string{ "--agent-name", name, - refFlag, refValue, + "--apiexportendpointslice-ref", apiExportEndpointSlice, "--enable-leader-election=false", "--kubeconfig", localKubeconfig, "--kcp-kubeconfig", kcpKubeconfig, From 0dfa0e5d3f4b880d49a46cf3e56987d226da84d5 Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Fri, 12 Dec 2025 17:35:52 +0100 Subject: [PATCH 7/8] fix CEL API deprecation in Kube 1.34 On-behalf-of: @SAP christoph.mewes@sap.com --- internal/mutation/transformer/cel.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/mutation/transformer/cel.go b/internal/mutation/transformer/cel.go index b167aa6..1c3ee78 100644 --- a/internal/mutation/transformer/cel.go +++ b/internal/mutation/transformer/cel.go @@ -20,7 +20,7 @@ import ( "fmt" "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/decls" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -35,10 +35,10 @@ type celTransformer struct { } func NewCEL(mut *syncagentv1alpha1.ResourceCELMutation) (*celTransformer, error) { - env, err := cel.NewEnv(cel.Declarations( - decls.NewVar("self", decls.Dyn), - decls.NewVar("other", decls.Dyn), - decls.NewVar("value", decls.Dyn), + env, err := cel.NewEnv(cel.VariableDecls( + decls.NewVariable("self", cel.DynType), + decls.NewVariable("other", cel.DynType), + decls.NewVariable("value", cel.DynType), )) if err != nil { return nil, fmt.Errorf("failed to create CEL env: %w", err) From e786eb577e75f39a61e45c4afa70b7783335e48a Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Fri, 12 Dec 2025 17:36:33 +0100 Subject: [PATCH 8/8] refactor how the syncagent integrates with multicluster-runtime Since the mcprovider now watches the APIExportEndpointSlice itself, it does not make sense anymore to start it dynamically on demand. There is no point in having the syncmanager watch an APIExport or EndpointSlice itself, since the mcprovider does not take any URLs as input values anymore, it relies solely on watching the EndpointSlice itself. To accomodate this, I moved the manager and its provider up into the main(). Since the syncmanager controller however not just simply starts an mcmanager/provider, but also deals with dynamically started controllers, I decided to move that behaviour into a new DynamicMultiClusterManager (DMCM). The DMCM is capable of keeping track of all engaged clusters and allows you to add and start controllers at any time. It will automatically pre-seed new controller instances with the known clusters. There is one downside: Right now, if the controller fails to start, the syncmanager controller will not notice, re-reconcile and restore it. There's no back channel for that error from the sync controller's goroutine yet. --- Since the syncagent by design dynamically fills APIExports with resource schemas, when the DMCM is started, there are most likely (at least in cold start situations) no resource schemas yet in the APIExport. Even though the syncmanager waits for the apiresourceschema controller to be done, it currently does not wait for the apiexport controller to be done (in adding that ResourceSchema to the APIExport) (there is no condition or anything to tell when that controller has finished). This means the syncmanager will start new sync controllers at the same time as the apiexport controller is still filling the APIExport. However when the resources are not present yet, these sync controllers fail to boot up, and since there is currently no channel to report this back to the syncmanager, they do not get quickly restarted. To handle this (it might have been simpler to wait for the APIExport to be ready, but alas... I realized my oversight too late), I added an explicit API discovery step, where the sync controllers are only started if and when the primary resource is actually available. --- One major side effect of this whole refactoring is of course: the agent does not longer support watching APIExports for their deprecated virtual workspace URLs. When this is merged, you must provide an APIExportEndpointSlice. On-behalf-of: @SAP christoph.mewes@sap.com --- cmd/api-syncagent/main.go | 36 +- go.mod | 2 +- internal/controller/sync/controller.go | 6 +- internal/controller/syncmanager/controller.go | 481 ++++-------------- internal/controller/syncmanager/doc.go | 6 +- internal/discovery/resource_prober.go | 108 ++++ internal/kcp/multicluster.go | 222 ++++++++ internal/projection/projection.go | 13 + 8 files changed, 478 insertions(+), 396 deletions(-) create mode 100644 internal/discovery/resource_prober.go create mode 100644 internal/kcp/multicluster.go diff --git a/cmd/api-syncagent/main.go b/cmd/api-syncagent/main.go index 9dcaa72..ea3311e 100644 --- a/cmd/api-syncagent/main.go +++ b/cmd/api-syncagent/main.go @@ -32,12 +32,12 @@ import ( "github.com/kcp-dev/api-syncagent/internal/controller/apiexport" "github.com/kcp-dev/api-syncagent/internal/controller/apiresourceschema" "github.com/kcp-dev/api-syncagent/internal/controller/syncmanager" + "github.com/kcp-dev/api-syncagent/internal/discovery" + "github.com/kcp-dev/api-syncagent/internal/kcp" syncagentlog "github.com/kcp-dev/api-syncagent/internal/log" "github.com/kcp-dev/api-syncagent/internal/version" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" - corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" @@ -147,6 +147,21 @@ func run(ctx context.Context, log *zap.SugaredLogger, opts *Options) error { if err := mgr.Add(endpointKcpCluster); err != nil { return fmt.Errorf("failed to add endpoint kcp cluster runnable: %w", err) } + + endpointSliceCluster = endpointKcpCluster + } + + // Setup the magical dynamic multicluster manager. It's a dynamic version of the + // regular mcmanager, capable of starting new controllers at any later time and + // allowing them to be also stopped at any time. The syncmanager needs it to + // start/stop sync controllers for each PublishedResource. + dmcm, err := kcp.NewDynamicMultiClusterManager(endpoint.EndpointSlice.Config, endpoint.EndpointSlice.Name) + if err != nil { + return fmt.Errorf("failed to start dynamic multi cluster manager: %w", err) + } + + if err := mgr.Add(dmcm); err != nil { + return fmt.Errorf("failed to add endpoint kcp cluster runnable: %w", err) } startController := func(name string, creator func() error) error { @@ -177,20 +192,11 @@ func run(ctx context.Context, log *zap.SugaredLogger, opts *Options) error { // This controller is called "sync" because it makes the most sense to the users, even though internally the relevant // controller is the syncmanager (which in turn would start/stop the sync controllers). if err := startController("sync", func() error { - cluster := endpointKcpCluster - if cluster == nil { - cluster = managedKcpCluster - } - - var endpointSlice *kcpdevv1alpha1.APIExportEndpointSlice - if endpoint.EndpointSlice != nil { - endpointSlice = endpoint.EndpointSlice.APIExportEndpointSlice - } - - // It doesn't matter which rest config we specify, as the URL will be overwritten with the - // virtual workspace URL anyway. + // The syncmanager needs to be able to determine whether an API is already bound and available + // before it can start any sync controllers. That discovery logic is encapsulated in the ResourceProber. + prober := discovery.NewResourceProber(endpoint.EndpointSlice.Config, endpointSliceCluster.GetClient(), endpoint.EndpointSlice.Name) - return syncmanager.Add(ctx, mgr, cluster, kcpRestConfig, log, endpoint.APIExport.APIExport, endpointSlice, opts.PublishedResourceSelector, opts.Namespace, opts.AgentName) + return syncmanager.Add(ctx, mgr, prober, dmcm, log, opts.PublishedResourceSelector, opts.Namespace, opts.AgentName) }); err != nil { return err } diff --git a/go.mod b/go.mod index 30a8780..fa0acb8 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/go-logr/zapr v1.3.0 github.com/google/cel-go v0.26.0 github.com/google/go-cmp v0.7.0 + github.com/google/uuid v1.6.0 github.com/kcp-dev/api-syncagent/sdk v0.0.0-00010101000000-000000000000 github.com/kcp-dev/kcp v0.29.1-0.20251210093424-08fb9eb48494 github.com/kcp-dev/logicalcluster/v3 v3.0.5 @@ -79,7 +80,6 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect diff --git a/internal/controller/sync/controller.go b/internal/controller/sync/controller.go index dbd2a42..509d8f4 100644 --- a/internal/controller/sync/controller.go +++ b/internal/controller/sync/controller.go @@ -134,12 +134,12 @@ func Create( // The manager parameter is mostly unused and will be removed in future CR versions. c, err := mccontroller.NewUnmanaged(ControllerName, remoteManager, ctrlOptions) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to instantiate new controller: %w", err) } // watch the target resource in the virtual workspace if err := c.MultiClusterWatch(mcsource.TypedKind(remoteDummy, mchandler.TypedEnqueueRequestForObject[*unstructured.Unstructured]())); err != nil { - return nil, err + return nil, fmt.Errorf("failed to setup remote-side watch: %w", err) } // watch the source resource in the local cluster, but enqueue the origin remote object @@ -158,7 +158,7 @@ func Create( }) if err := c.Watch(source.TypedKind(localManager.GetCache(), localDummy, enqueueRemoteObjForLocalObj, nameFilter)); err != nil { - return nil, err + return nil, fmt.Errorf("failed to setup local-side watch: %w", err) } log.Info("Done setting up unmanaged controller.") diff --git a/internal/controller/syncmanager/controller.go b/internal/controller/syncmanager/controller.go index ddccdb0..0308501 100644 --- a/internal/controller/syncmanager/controller.go +++ b/internal/controller/syncmanager/controller.go @@ -20,37 +20,25 @@ import ( "context" "errors" "fmt" - "slices" "sync" + "time" "go.uber.org/zap" controllersync "github.com/kcp-dev/api-syncagent/internal/controller/sync" - "github.com/kcp-dev/api-syncagent/internal/controllerutil" "github.com/kcp-dev/api-syncagent/internal/controllerutil/predicate" "github.com/kcp-dev/api-syncagent/internal/discovery" + "github.com/kcp-dev/api-syncagent/internal/kcp" + "github.com/kcp-dev/api-syncagent/internal/projection" syncagentv1alpha1 "github.com/kcp-dev/api-syncagent/sdk/apis/syncagent/v1alpha1" - apiexportprovider "github.com/kcp-dev/multicluster-provider/apiexport" - kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" - kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" - mccontroller "sigs.k8s.io/multicluster-runtime/pkg/controller" - mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" - - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/builder" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/cluster" - "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) const ( @@ -60,6 +48,8 @@ const ( numSyncWorkers = 4 ) +type ClusterProviderFunc func() []string + type Reconciler struct { // choose to break good practice of never storing a context in a struct, // and instead opt to use the app's root context for the dynamically @@ -68,62 +58,29 @@ type Reconciler struct { ctx context.Context localManager manager.Manager - kcpCluster cluster.Cluster - kcpRestConfig *rest.Config + dmcm *kcp.DynamicMultiClusterManager log *zap.SugaredLogger recorder record.EventRecorder discoveryClient *discovery.Client + resourceProber *discovery.ResourceProber prFilter labels.Selector stateNamespace string agentName string - // endpointSlice is preferred over apiExport - apiExport *kcpapisv1alpha1.APIExport - endpointSlice *kcpapisv1alpha1.APIExportEndpointSlice - - // URL for which the current vwCluster instance has been created - vwURL string - - // A multi-cluster Manager representing the virtual workspace cluster; this manager will - // not handle the individual controllers' lifecycle, because their lifecycle depends on - // PublishedResources, not the set of workspaces/clusters in the APIExport's virtual workspace. - // This manager is stopped and recreated whenever the APIExport's URL changes. - vwManager mcmanager.Manager - vwManagerCtx context.Context - vwManagerCancel context.CancelFunc - - // The provider based on the APIExport; like the vwManager, this is stopped and recreated - // whenever the APIExport's URL changes. - providerOnce sync.Once - vwProvider *apiexportprovider.Provider - - syncWorkersLock sync.RWMutex + syncCancelsLock sync.RWMutex // A map of sync controllers, one for each PublishedResource, using their // UIDs and resourceVersion as the map keys; using the version ensures that // when a PR changes, the old controller is orphaned and will be shut down. - syncWorkers map[string]syncWorker - - clustersLock sync.RWMutex - // A map of clusters that have been engaged with the shim layer. Since this - // reconciler dynamically starts and stops controllers, we need to keep track - // of clusters and engage them with sync controllers started at a later point in time. - clusters map[string]engagedCluster -} - -type syncWorker struct { - controller mccontroller.Controller - cancel context.CancelFunc + syncCancels map[string]context.CancelCauseFunc } // Add creates a new controller and adds it to the given manager. func Add( ctx context.Context, localManager manager.Manager, - kcpCluster cluster.Cluster, - kcpRestConfig *rest.Config, + resourceProber *discovery.ResourceProber, + dmcm *kcp.DynamicMultiClusterManager, log *zap.SugaredLogger, - apiExport *kcpapisv1alpha1.APIExport, - endpointSlice *kcpapisv1alpha1.APIExportEndpointSlice, prFilter labels.Selector, stateNamespace string, agentName string, @@ -136,43 +93,26 @@ func Add( reconciler := &Reconciler{ ctx: ctx, localManager: localManager, - apiExport: apiExport, - endpointSlice: endpointSlice, - kcpCluster: kcpCluster, - kcpRestConfig: kcpRestConfig, + dmcm: dmcm, log: log, recorder: localManager.GetEventRecorderFor(ControllerName), discoveryClient: discoveryClient, prFilter: prFilter, stateNamespace: stateNamespace, agentName: agentName, - - providerOnce: sync.Once{}, - - syncWorkersLock: sync.RWMutex{}, - syncWorkers: map[string]syncWorker{}, - - clustersLock: sync.RWMutex{}, - clusters: make(map[string]engagedCluster), + resourceProber: resourceProber, + syncCancelsLock: sync.RWMutex{}, + syncCancels: map[string]context.CancelCauseFunc{}, } - bldr := builder.ControllerManagedBy(localManager). + bldr := builder. + ControllerManagedBy(localManager). Named(ControllerName). - WithOptions(controller.Options{ - // this controller is meant to control others, so we only want 1 thread - MaxConcurrentReconciles: 1, - }). - // Watch for changes to the PublishedResources - Watches(&syncagentv1alpha1.PublishedResource{}, controllerutil.EnqueueConst[ctrlruntimeclient.Object]("dummy"), builder.WithPredicates(predicate.ByLabels(prFilter))) - - // Watch for changes to APIExport/EndpointSlice on the kcp side to start/restart the actual syncing controllers; - // the cache is already restricted by a fieldSelector in the main.go to respect the RBAC restrictions, - // so there is no need here to add an additional filter. - if endpointSlice != nil { - bldr.WatchesRawSource(source.Kind(kcpCluster.GetCache(), &kcpapisv1alpha1.APIExportEndpointSlice{}, controllerutil.EnqueueConst[*kcpapisv1alpha1.APIExportEndpointSlice]("dummy"))) - } else { - bldr.WatchesRawSource(source.Kind(kcpCluster.GetCache(), &kcpapisv1alpha1.APIExport{}, controllerutil.EnqueueConst[*kcpapisv1alpha1.APIExport]("dummy"))) - } + Watches( + &syncagentv1alpha1.PublishedResource{}, + &handler.TypedEnqueueRequestForObject[ctrlruntimeclient.Object]{}, + builder.WithPredicates(predicate.ByLabels(prFilter)), + ) _, err = bldr.Build(reconciler) @@ -181,236 +121,119 @@ func Add( func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { log := r.log.Named(ControllerName) - log.Debug("Processing") + log.With("request", req.Name).Debug("Processing") - var err error - - if r.endpointSlice != nil { - if err := r.kcpCluster.GetClient().Get(ctx, ctrlruntimeclient.ObjectKeyFromObject(r.endpointSlice), r.endpointSlice); err != nil { - return reconcile.Result{}, fmt.Errorf("failed to retrieve APIExportEndpointSlice: %w", err) - } + pubRes := &syncagentv1alpha1.PublishedResource{} + if err := r.localManager.GetClient().Get(ctx, req.NamespacedName, pubRes); err != nil { + return reconcile.Result{}, ctrlruntimeclient.IgnoreNotFound(err) + } - urls := r.endpointSlice.Status.APIExportEndpoints + var ( + err error + result reconcile.Result + ) - if len(urls) == 0 { - // the virtual workspace is not ready yet - log.Warn("APIExportEndpointSlice has no URLs.") - } else { - err = r.reconcile(ctx, log, urls[0].URL) - } + if pubRes.DeletionTimestamp != nil || pubRes.Status.ResourceSchemaName == "" || !isSyncEnabled(pubRes) { + // For PubRes that have not yet been processed, have their sync disabled or are in + // deletion, we cleanup and stop any potentially running sync controller. + err = r.cleanupController(log, pubRes) } else { - if err := r.kcpCluster.GetClient().Get(ctx, ctrlruntimeclient.ObjectKeyFromObject(r.apiExport), r.apiExport); err != nil { - return reconcile.Result{}, fmt.Errorf("failed to retrieve APIExport: %w", err) - } - - //nolint:staticcheck - urls := r.apiExport.Status.VirtualWorkspaces - - if len(urls) == 0 { - // the virtual workspace is not ready yet - log.Warn("APIExport has no virtual workspace URLs.") - } else { - err = r.reconcile(ctx, log, urls[0].URL) - } + // Otherwise, ensure a sync controller is running. + result, err = r.ensureSyncController(ctx, log, pubRes) } - return reconcile.Result{}, err + return result, err } -func (r *Reconciler) reconcile(ctx context.Context, log *zap.SugaredLogger, vwURL string) error { - // if the VW URL changed, stop the manager and all sync controllers - if r.vwURL != "" && vwURL != r.vwURL { - r.shutdown(log) - } - - // if kcp had a hiccup and wrote a status without an actual URL - if vwURL == "" { - return nil - } - - // make sure we have a running manager object for the virtual workspace - if err := r.ensureManager(log, vwURL); err != nil { - return fmt.Errorf("failed to ensure virtual workspace manager: %w", err) - } - - // find all PublishedResources - pubResList := &syncagentv1alpha1.PublishedResourceList{} - if err := r.localManager.GetClient().List(ctx, pubResList, &ctrlruntimeclient.ListOptions{ - LabelSelector: r.prFilter, - }); err != nil { - return fmt.Errorf("failed to list PublishedResources: %w", err) - } - - // Filter out those that have not been processed into APIResourceSchemas yet; starting - // sync controllers too early, before the schemes are available, will make the watches - // not work properly. - // Also remove those PRs that have sync disabled. - pubResources := slices.DeleteFunc(pubResList.Items, func(pr syncagentv1alpha1.PublishedResource) bool { - return pr.Status.ResourceSchemaName == "" || !isSyncEnabled(&pr) - }) - - // make sure that for every PublishedResource, a matching sync controller exists - if err := r.ensureSyncControllers(ctx, log, pubResources); err != nil { - return fmt.Errorf("failed to ensure sync controllers: %w", err) - } - - return nil -} +func (r *Reconciler) ensureSyncController(ctx context.Context, log *zap.SugaredLogger, pubRes *syncagentv1alpha1.PublishedResource) (reconcile.Result, error) { + key := getPublishedResourceKey(pubRes) -func (r *Reconciler) ensureManager(log *zap.SugaredLogger, vwURL string) error { - if r.vwManagerCtx == nil { - // Use the global app context so this provider is independent of the reconcile - // context, which might get cancelled right after Reconcile() is done. - r.vwManagerCtx, r.vwManagerCancel = context.WithCancel(r.ctx) + // controller already exists + if _, exists := r.syncCancels[key]; exists { + return reconcile.Result{}, nil } - vwConfig := rest.CopyConfig(r.kcpRestConfig) - vwConfig.Host = vwURL + prlog := log.With("prkey", key, "name", pubRes.Name) - scheme := runtime.NewScheme() - - if err := corev1.AddToScheme(scheme); err != nil { - return fmt.Errorf("failed to register scheme %s: %w", corev1.SchemeGroupVersion, err) - } - - if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { - return fmt.Errorf("failed to register scheme %s: %w", kcpapisv1alpha1.SchemeGroupVersion, err) - } - - if err := kcpcorev1alpha1.AddToScheme(scheme); err != nil { - return fmt.Errorf("failed to register scheme %s: %w", kcpcorev1alpha1.SchemeGroupVersion, err) + // Multicluster-runtime *hates* it when you create a controller that watches + // a resource that doesn't exist yet. So before we can proceed, we need to make + // sure the synced resource is available (there is no good condition to + // check anywhere, so a deep service discovery is required). + resourceBound, err := r.checkResourceIsBoundEverywhere(ctx, log, pubRes) + if err != nil { + return reconcile.Result{}, fmt.Errorf("failed to probe for existence of resource in virtual workspace: %w", err) + } + + if !resourceBound { + prlog.Info("Not all required resources are yet available, re-trying controller creation in a moment") + // TODO: Or return an error to have it exponentially back off? + return reconcile.Result{RequeueAfter: 1 * time.Second}, nil + } + + // Use the global app context so this provider is independent of the reconcile + // context, which might get cancelled right after Reconcile() is done. + ctrlCtx, ctrlCancel := context.WithCancelCause(r.ctx) + + prlog.Info("Creating new sync controller…") + + // create the sync controller; + // use the reconciler's log without any additional reconciling context + syncController, err := controllersync.Create( + // This can be the reconciling context, as it's only used to find the target CRD during setup; + // this context *must not* be stored in the sync controller! + ctx, + r.localManager, + r.dmcm.GetManager(), + pubRes, + r.discoveryClient, + r.stateNamespace, + r.agentName, + r.log, + numSyncWorkers, + ) + if err != nil { + ctrlCancel(errors.New("failed to create sync controller")) + return reconcile.Result{}, fmt.Errorf("failed to create sync controller: %w", err) } - if r.vwProvider == nil { - log.Debug("Setting up APIExport provider…") - - provider, err := apiexportprovider.New(vwConfig, apiexportprovider.Options{ - Scheme: scheme, - }) - if err != nil { - return fmt.Errorf("failed to init apiexport provider: %w", err) - } + r.syncCancels[key] = ctrlCancel - r.vwProvider = provider + // time to start the controller; this will spawn a new goroutine if + // successful; the new controller will be pre-seeded with all knonwn + // (engaged) clusters by the DMCM. + if err = r.dmcm.StartController(ctrlCtx, log.With("prkey", key), syncController); err != nil { + ctrlCancel(errors.New("failed to start sync controller")) + return reconcile.Result{}, fmt.Errorf("failed to start sync controller: %w", err) } - if r.vwManager == nil { - log.Debug("Setting up virtual workspace manager…") - - manager, err := mcmanager.New(vwConfig, r.vwProvider, manager.Options{ - Scheme: scheme, - LeaderElection: false, - Metrics: server.Options{ - BindAddress: "0", - }, - }) - if err != nil { - return fmt.Errorf("failed to initialize cluster: %w", err) - } - - // Make sure the vwManager can Engage() on the controller, even though we - // start and stop them outside the control of the manager. This shim will - // ensure Engage() calls are handed to the underlying sync controller as - // as long as the controller is running. - if err := manager.Add(&controllerShim{reconciler: r}); err != nil { - return fmt.Errorf("failed to initialize cluster: %w", err) - } - - // use the app's root context as the base, not the reconciling context, which - // might get cancelled after Reconcile() is done; - // likewise use the reconciler's log without any additional reconciling context - go func() { - if err := manager.Start(r.vwManagerCtx); err != nil { - log.Fatalw("Failed to start manager.", zap.Error(err)) - } - }() - - log.Debug("Virtual workspace cluster setup completed.") - - r.vwURL = vwURL - r.vwManager = manager - } - - r.providerOnce.Do(func() { - log.Debug("Starting virtual workspace provider…") - // start the provider - go func() { - // Use the global app context so this provider is independent of the reconcile - // context, which might get cancelled right after Reconcile() is done. - if err := r.vwProvider.Run(r.vwManagerCtx, r.vwManager); err != nil { - log.Fatalw("Failed to start apiexport provider", zap.Error(err)) - } - }() - }) - - return nil -} - -type engagedCluster struct { - ctx context.Context - cl cluster.Cluster + return reconcile.Result{}, nil } -type controllerShim struct { - reconciler *Reconciler -} +func (r *Reconciler) cleanupController(log *zap.SugaredLogger, pubRes *syncagentv1alpha1.PublishedResource) error { + key := getPublishedResourceKey(pubRes) + log.Infow("Stopping sync controller…", "prkey", key) -func (s *controllerShim) Engage(ctx context.Context, clusterName string, cl cluster.Cluster) error { - if _, ok := s.reconciler.clusters[clusterName]; !ok { - s.reconciler.clustersLock.Lock() - s.reconciler.clusters[clusterName] = engagedCluster{ctx: ctx, cl: cl} - s.reconciler.clustersLock.Unlock() - - // start a goroutine to make sure we remove the cluster when the context is done - go func() { - <-ctx.Done() - s.reconciler.clustersLock.Lock() - delete(s.reconciler.clusters, clusterName) - s.reconciler.clustersLock.Unlock() - }() - } + r.syncCancelsLock.Lock() + defer r.syncCancelsLock.Unlock() - s.reconciler.syncWorkersLock.RLock() - defer s.reconciler.syncWorkersLock.RUnlock() - for _, worker := range s.reconciler.syncWorkers { - if err := worker.controller.Engage(ctx, clusterName, cl); err != nil { - return err - } + cancel, ok := r.syncCancels[key] + if ok { + cancel(errors.New("controller is no longer needed")) + delete(r.syncCancels, key) } return nil } -func (s *controllerShim) Start(_ context.Context) error { - // NOP, controllers are started outside the control of the manager. - return nil -} - -// shutdown will cancel the current context and thereby stop the manager and all -// sync controllers at the same time. -func (r *Reconciler) shutdown(log *zap.SugaredLogger) { - log.Debug("Shutting down existing manager…") - - if r.vwManagerCancel != nil { - r.vwManagerCancel() +func (r *Reconciler) checkResourceIsBoundEverywhere(ctx context.Context, log *zap.SugaredLogger, pubRes *syncagentv1alpha1.PublishedResource) (bool, error) { + projectedGVK, err := projection.ProjectPublishedResourceGVK(ctx, r.discoveryClient, pubRes) + if err != nil { + return false, fmt.Errorf("failed to determine projected primary GVK: %w", err) } - r.vwProvider = nil - r.vwManager = nil - r.vwManagerCtx = nil - r.vwManagerCancel = nil - r.vwURL = "" - r.providerOnce = sync.Once{} - - r.clustersLock.Lock() - r.clusters = make(map[string]engagedCluster) - r.clustersLock.Unlock() - - // Free all workers; since their contexts are based on the manager's context, - // they have also been cancelled already above. - r.syncWorkersLock.Lock() - r.syncWorkers = make(map[string]syncWorker) - r.syncWorkersLock.Unlock() + return r.resourceProber.HasGVK(ctx, projectedGVK) + + // TODO: If the syncagent ever watches related resources, it also needs to check their GVRs here. } func getPublishedResourceKey(pr *syncagentv1alpha1.PublishedResource) string { @@ -420,91 +243,3 @@ func getPublishedResourceKey(pr *syncagentv1alpha1.PublishedResource) string { func isSyncEnabled(pr *syncagentv1alpha1.PublishedResource) bool { return pr.Spec.Synchronization == nil || pr.Spec.Synchronization.Enabled } - -func (r *Reconciler) ensureSyncControllers(ctx context.Context, log *zap.SugaredLogger, publishedResources []syncagentv1alpha1.PublishedResource) error { - requiredWorkers := sets.New[string]() - for _, pr := range publishedResources { - requiredWorkers.Insert(getPublishedResourceKey(&pr)) - } - - // stop controllers that are no longer needed - for key, worker := range r.syncWorkers { - if requiredWorkers.Has(key) { - continue - } - - log.Infow("Stopping sync controller…", "key", key) - - worker.cancel() - - r.syncWorkersLock.Lock() - delete(r.syncWorkers, key) - r.syncWorkersLock.Unlock() - } - - // start missing controllers - for _, pubRes := range publishedResources { - key := getPublishedResourceKey(&pubRes) - - // controller already exists - if _, exists := r.syncWorkers[key]; exists { - continue - } - - prlog := log.With("key", key, "name", pubRes.Name) - ctrlCtx, ctrlCancel := context.WithCancel(r.vwManagerCtx) - - prlog.Info("Creating new sync controller…") - - // create the sync controller; - // use the reconciler's log without any additional reconciling context - syncController, err := controllersync.Create( - // This can be the reconciling context, as it's only used to find the target CRD during setup; - // this context *must not* be stored in the sync controller! - ctx, - r.localManager, - r.vwManager, - &pubRes, - r.discoveryClient, - r.stateNamespace, - r.agentName, - r.log, - numSyncWorkers, - ) - if err != nil { - ctrlCancel() - return fmt.Errorf("failed to create sync controller: %w", err) - } - - r.syncWorkersLock.Lock() - r.syncWorkers[key] = syncWorker{ - controller: syncController, - cancel: ctrlCancel, - } - r.syncWorkersLock.Unlock() - - go func() { - log.Infow("Starting sync controller…", "key", key) - if err := syncController.Start(ctrlCtx); err != nil && !errors.Is(err, context.Canceled) { - ctrlCancel() - prlog.Errorw("failed to start sync controller", zap.Error(err)) - } - - prlog.Debug("Stopped sync controller") - - r.syncWorkersLock.Lock() - delete(r.syncWorkers, key) - r.syncWorkersLock.Unlock() - }() - - r.clustersLock.RLock() - defer r.clustersLock.RUnlock() - for name, ec := range r.clusters { - if err := syncController.Engage(ec.ctx, name, ec.cl); err != nil { - prlog.Errorw("failed to engage cluster", zap.Error(err), "cluster", name) - } - } - } - - return nil -} diff --git a/internal/controller/syncmanager/doc.go b/internal/controller/syncmanager/doc.go index d0f2a91..62954f7 100644 --- a/internal/controller/syncmanager/doc.go +++ b/internal/controller/syncmanager/doc.go @@ -15,9 +15,7 @@ limitations under the License. */ /* -Package syncmanager contains a controller that watches the APIExport we manage -in kcp. Once the virtual workspace URL for said APIExport is ready, the -controller will begin to synchronize resources back and forth between kcp -(i.e. all relevant workspaces) and the service cluster. +Package syncmanager contains a controller that, using a DynamicMultiClusterManager, +starts and stop sync controllers for each of the PublishedResources. */ package syncmanager diff --git a/internal/discovery/resource_prober.go b/internal/discovery/resource_prober.go new file mode 100644 index 0000000..e11567a --- /dev/null +++ b/internal/discovery/resource_prober.go @@ -0,0 +1,108 @@ +/* +Copyright 2025 The KCP 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 discovery + +import ( + "context" + "fmt" + + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/discovery" + "k8s.io/client-go/rest" + ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +type ResourceProber struct { + name string + config *rest.Config + client ctrlruntimeclient.Client +} + +func NewResourceProber(endpointSliceWorkspaceConfig *rest.Config, endpointSliceWorkspaceClient ctrlruntimeclient.Client, endpointSliceName string) *ResourceProber { + return &ResourceProber{ + name: endpointSliceName, + config: endpointSliceWorkspaceConfig, + client: endpointSliceWorkspaceClient, + } +} + +func (p *ResourceProber) HasGVR(ctx context.Context, gvr schema.GroupVersionResource) (bool, error) { + return p.hasAPIThing(ctx, func(apiGroup, version, resource, _ string) bool { + return apiGroup == gvr.Group && version == gvr.Version && resource == gvr.Resource + }) +} + +func (p *ResourceProber) HasGVK(ctx context.Context, gvk schema.GroupVersionKind) (bool, error) { + return p.hasAPIThing(ctx, func(apiGroup, version, _, kind string) bool { + return apiGroup == gvk.Group && version == gvk.Version && kind == gvk.Kind + }) +} + +func (p *ResourceProber) hasAPIThing(ctx context.Context, match matchFunc) (bool, error) { + endpointSlice := &kcpapisv1alpha1.APIExportEndpointSlice{} + if err := p.client.Get(ctx, types.NamespacedName{Name: p.name}, endpointSlice); err != nil { + return false, fmt.Errorf("failed to get APIExportEndpointSlice: %w", err) + } + + // connect to each of the endpoints and check if the resource is available there + for _, endpoint := range endpointSlice.Status.APIExportEndpoints { + has, err := p.hasAPIThingInEndpoint(endpoint.URL, match) + if err != nil || !has { + return has, err + } + } + + return true, nil +} + +type matchFunc func(apiGroup, version, resource, kind string) bool + +func (p *ResourceProber) hasAPIThingInEndpoint(endpoint string, match matchFunc) (bool, error) { + endpointConfig := rest.CopyConfig(p.config) + endpointConfig.Host = endpoint + "/clusters/*" + + discoveryClient, err := discovery.NewDiscoveryClientForConfig(endpointConfig) + if err != nil { + return false, fmt.Errorf("failed to create discovery client: %w", err) + } + + _, resourceLists, err := discoveryClient.ServerGroupsAndResources() + if err != nil { + return false, fmt.Errorf("failed to discover APIs: %w", err) + } + + for _, resList := range resourceLists { + // .Group on an APIResource is empty for built-in resources, so we must + // parse and check GroupVersion of the entire list. + gv, err := schema.ParseGroupVersion(resList.GroupVersion) + if err != nil { + return false, fmt.Errorf("invalid API group version %q reported by Kubernetes: %w", resList.GroupVersion, err) + } + + for _, res := range resList.APIResources { + // res.Version is empty for built-in resources + if match(gv.Group, gv.Version, res.Name, res.Kind) { + return true, nil + } + } + } + + return false, nil +} diff --git a/internal/kcp/multicluster.go b/internal/kcp/multicluster.go new file mode 100644 index 0000000..6214e06 --- /dev/null +++ b/internal/kcp/multicluster.go @@ -0,0 +1,222 @@ +/* +Copyright 2025 The KCP 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 kcp + +import ( + "context" + "errors" + "fmt" + "sync" + + "github.com/google/uuid" + "go.uber.org/zap" + + apiexportprovider "github.com/kcp-dev/multicluster-provider/apiexport" + kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/cluster" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/metrics/server" + mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" +) + +// DynamicMultiClusterManager (DMCM) is an extension to the regular multicluster manager. +// It's capable of starting new controllers at any time by keeping track of all +// engaged clusters (so that controllers that start later can be pre-seeded). +// Likewise it's possible to stop any of the controllers at any time. +// +// The DMCM interface is goroutine-safe. +type DynamicMultiClusterManager struct { + manager mcmanager.Manager + + runnablesLock sync.RWMutex + // A map of sync controllers, one for each PublishedResource, using their + // UIDs and resourceVersion as the map keys; using the version ensures that + // when a PR changes, the old controller is orphaned and will be shut down. + runnables map[string]mcmanager.Runnable + + tracker *clusterTracker +} + +func NewDynamicMultiClusterManager(cfg *rest.Config, endpointSliceName string) (*DynamicMultiClusterManager, error) { + scheme := runtime.NewScheme() + + if err := corev1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", corev1.SchemeGroupVersion, err) + } + + if err := kcpapisv1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpapisv1alpha1.SchemeGroupVersion, err) + } + + if err := kcpcorev1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("failed to register scheme %s: %w", kcpcorev1alpha1.SchemeGroupVersion, err) + } + + // Setup the multicluster provider, it will watch the EndpointSlice on its own. + provider, err := apiexportprovider.New(cfg, endpointSliceName, apiexportprovider.Options{ + Scheme: scheme, + }) + if err != nil { + return nil, fmt.Errorf("failed to create multicluster provider: %w", err) + } + + // Setup a multiClusterManager, which will use the apiexport provider and + // try to engage a dummy controller, which will just keep track of all the + // engaged clusters over the entire lifetime of the process. + multiClusterManager, err := mcmanager.New(cfg, provider, manager.Options{ + Scheme: scheme, + LeaderElection: false, + Metrics: server.Options{ + BindAddress: "0", + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to initialize manager: %w", err) + } + + dynManager := &DynamicMultiClusterManager{ + manager: multiClusterManager, + runnablesLock: sync.RWMutex{}, + runnables: map[string]mcmanager.Runnable{}, + } + + tracker := newClusterTracker(dynManager) + dynManager.tracker = tracker + + // Start our tracker as the first and most important runnable in this mcmanager. + if err := multiClusterManager.Add(tracker); err != nil { + return nil, fmt.Errorf("failed to initialize cluster: %w", err) + } + + return dynManager, nil +} + +func (dmcm *DynamicMultiClusterManager) GetManager() mcmanager.Manager { + return dmcm.manager +} + +func (dmcm *DynamicMultiClusterManager) Start(ctx context.Context) error { + return dmcm.manager.Start(ctx) +} + +// StartController is like pre-seeding the new controller, adding it to the DMCM and +// then starting it in its own goroutine. +func (dmcm *DynamicMultiClusterManager) StartController(ctx context.Context, log *zap.SugaredLogger, controller mcmanager.Runnable) error { + // we need some sort of map key, but it does not matter what that key is + mapKey := uuid.New().String() + + // keep track of this controller so we can forward the Engage() calls to it + dmcm.runnablesLock.Lock() + defer dmcm.runnablesLock.Unlock() + dmcm.runnables[mapKey] = controller + + // start it + log = log.With("component", "DynamicMultiClusterManager", "dmcmkey", mapKey) + + go func() { + log.Debug("Starting controller") + if err := controller.Start(ctx); err != nil && !errors.Is(err, context.Canceled) { + log.Errorw("Controller failed to start", zap.Error(err)) + } + + log.Debug("Removing controller") + dmcm.runnablesLock.Lock() + delete(dmcm.runnables, mapKey) + dmcm.runnablesLock.Unlock() + }() + + // pre-seed the controller + if err := dmcm.tracker.PreSeedController(controller); err != nil { + return fmt.Errorf("failed to pre-seed controller: %w", err) + } + + return nil +} + +type engagedCluster struct { + ctx context.Context + cl cluster.Cluster +} + +// clusterTracker is a dummy controller, which doesn't do anything besides keeping +// track of all Engage() calls, so the DMCM can pre-seed new controllers. +type clusterTracker struct { + dynManager *DynamicMultiClusterManager + lock sync.RWMutex + clusters map[string]engagedCluster +} + +func newClusterTracker(dmcm *DynamicMultiClusterManager) *clusterTracker { + return &clusterTracker{ + dynManager: dmcm, + lock: sync.RWMutex{}, + clusters: map[string]engagedCluster{}, + } +} + +func (s *clusterTracker) Start(_ context.Context) error { + // NOP, this shim doesn't need any further setup + return nil +} + +func (s *clusterTracker) Engage(ctx context.Context, clusterName string, cl cluster.Cluster) error { + if _, ok := s.clusters[clusterName]; !ok { + s.lock.Lock() + s.clusters[clusterName] = engagedCluster{ctx: ctx, cl: cl} + s.lock.Unlock() + + // start a goroutine to make sure we remove the cluster when the context is done + go func() { + <-ctx.Done() + + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.clusters, clusterName) + }() + } + + // forward the Engage() call to all running controllers + s.dynManager.runnablesLock.RLock() + defer s.dynManager.runnablesLock.RUnlock() + + for _, ctrl := range s.dynManager.runnables { + if err := ctrl.Engage(ctx, clusterName, cl); err != nil { + return err + } + } + + return nil +} + +func (s *clusterTracker) PreSeedController(controller mcmanager.Runnable) error { + s.lock.RLock() + defer s.lock.RUnlock() + + for name, engaged := range s.clusters { + if err := controller.Engage(engaged.ctx, name, engaged.cl); err != nil { + return fmt.Errorf("failed to engage with cluster: %w", err) + } + } + + return nil +} diff --git a/internal/projection/projection.go b/internal/projection/projection.go index 16b62bd..3fba3a4 100644 --- a/internal/projection/projection.go +++ b/internal/projection/projection.go @@ -134,6 +134,19 @@ func ProjectPublishedResource(ctx context.Context, client *discovery.Client, pub return projectedCRD, nil } +func ProjectPublishedResourceGVK(ctx context.Context, client *discovery.Client, pubRes *syncagentv1alpha1.PublishedResource) (schema.GroupVersionKind, error) { + // find the resource that the PublishedResource is referring to + localGK := PublishedResourceSourceGK(pubRes) + + // fetch the original, full CRD from the cluster + crd, err := client.RetrieveCRD(ctx, localGK) + if err != nil { + return schema.GroupVersionKind{}, fmt.Errorf("failed to discover resource defined in PublishedResource: %w", err) + } + + return PublishedResourceProjectedGVK(crd, pubRes) +} + func RelatedResourceGVR(rr *syncagentv1alpha1.RelatedResourceSpec) schema.GroupVersionResource { resultGVR := schema.GroupVersionResource{ Group: rr.Group,