From b2c1cf65671bdff642efc727a42f08ac38cfdcda Mon Sep 17 00:00:00 2001 From: Stefan Bueringer Date: Wed, 22 May 2024 19:47:31 +0200 Subject: [PATCH 1/9] default --use-deprecated-gcs to true Signed-off-by: tnthornton <2375126+tnthornton@users.noreply.github.com> --- tools/setup-envtest/README.md | 7 ++++--- tools/setup-envtest/main.go | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/setup-envtest/README.md b/tools/setup-envtest/README.md index 0482dd3162..03f9442402 100644 --- a/tools/setup-envtest/README.md +++ b/tools/setup-envtest/README.md @@ -47,12 +47,13 @@ setup-envtest use -i --use-env # sideload a pre-downloaded tarball as Kubernetes 1.16.2 into our store setup-envtest sideload 1.16.2 < downloaded-envtest.tar.gz -# Per default envtest binaries are downloaded from: +# If --use-deprecated-gcs is set to false envtest binaries are downloaded from: # https://raw.githubusercontent.com/kubernetes-sigs/controller-tools/master/envtest-releases.yaml # To download from a custom index use the following: -setup-envtest use --index https://custom.com/envtest-releases.yaml +# Note: In controller-runtime v0.19.0 --use-deprecated-gcs will default to false. +setup-envtest use --use-deprecated-gcs=false --index https://custom.com/envtest-releases.yaml -# To download from the kubebuilder-tools GCS bucket: (default behavior before v0.18) +# To download from the kubebuilder-tools GCS bucket: (default behavior until v0.18) # Note: This is a Google-owned bucket and it might be shutdown at any time # see: https://github.com/kubernetes/k8s.io/issues/2647#event-12439345373 # Note: This flag will also be removed soon. diff --git a/tools/setup-envtest/main.go b/tools/setup-envtest/main.go index 7e2761a4f6..62670af515 100644 --- a/tools/setup-envtest/main.go +++ b/tools/setup-envtest/main.go @@ -50,7 +50,7 @@ var ( binDir = flag.String("bin-dir", "", "directory to store binary assets (default: $OS_SPECIFIC_DATA_DIR/envtest-binaries)") - useDeprecatedGCS = flag.Bool("use-deprecated-gcs", false, "use GCS to fetch envtest binaries. Note: This is deprecated and will be removed soon. For more details see: https://github.com/kubernetes-sigs/controller-runtime/pull/2811") + useDeprecatedGCS = flag.Bool("use-deprecated-gcs", true, "use GCS to fetch envtest binaries. Note: This is deprecated and will be removed soon. For more details see: https://github.com/kubernetes-sigs/controller-runtime/pull/2811. In controller-runtime v0.19.0 this flag will default to false") // These flags are only used with --use-deprecated-gcs. remoteBucket = flag.String("remote-bucket", "kubebuilder-tools", "remote GCS bucket to download from (only used with --use-deprecated-gcs)") @@ -58,7 +58,7 @@ var ( "remote server to query from. You can override this if you want to run "+ "an internal storage server instead, or for testing. (only used with --use-deprecated-gcs)") - // This flag is only used if --use-deprecated-gcs is not set or false (default). + // This flag is only used if --use-deprecated-gcs is set to false. index = flag.String("index", remote.DefaultIndexURL, "index to discover envtest binaries (only used if --use-deprecated-gcs is not set, or set to false)") ) From 67ea9081d64282aa500b609098ad330b2973211c Mon Sep 17 00:00:00 2001 From: Christoph Mewes Date: Mon, 9 Oct 2023 11:29:26 +0200 Subject: [PATCH 2/9] UPSTREAM: : kcp modifications Signed-off-by: Dr. Stefan Schimanski --- .prow.yaml | 13 ++ DOWNSTREAM_OWNERS | 5 + DOWNSTREAM_OWNERS_ALIASES | 0 go.mod | 1 + go.sum | 46 +++--- pkg/cache/cache.go | 19 ++- pkg/cache/cache_test.go | 7 +- pkg/cache/defaulting_test.go | 3 + pkg/cache/informer_cache.go | 19 ++- pkg/cache/internal/cache_reader.go | 51 +++++- pkg/cache/internal/informers.go | 4 +- pkg/cache/kcp_test.go | 138 ++++++++++++++++ pkg/client/client.go | 8 + pkg/client/interfaces.go | 6 + pkg/cluster/cluster.go | 11 +- pkg/handler/enqueue.go | 38 +++-- pkg/handler/enqueue_owner.go | 11 +- pkg/kcp/helper.go | 34 ++++ pkg/kcp/wrappers.go | 250 +++++++++++++++++++++++++++++ pkg/kontext/kontext.go | 24 +++ pkg/manager/manager.go | 6 + pkg/reconcile/reconcile.go | 4 + 22 files changed, 633 insertions(+), 65 deletions(-) create mode 100644 .prow.yaml create mode 100644 DOWNSTREAM_OWNERS create mode 100644 DOWNSTREAM_OWNERS_ALIASES create mode 100644 pkg/cache/kcp_test.go create mode 100644 pkg/kcp/helper.go create mode 100644 pkg/kcp/wrappers.go create mode 100644 pkg/kontext/kontext.go diff --git a/.prow.yaml b/.prow.yaml new file mode 100644 index 0000000000..54514caa1a --- /dev/null +++ b/.prow.yaml @@ -0,0 +1,13 @@ +presubmits: + - name: pull-controller-runtime-everything + always_run: true + decorate: true + clone_uri: "ssh://git@github.com/kcp-dev/controller-runtime.git" + labels: + preset-goproxy: "true" + spec: + containers: + - image: ghcr.io/kcp-dev/infra/build:1.22.2-1 + command: + - make + - test \ No newline at end of file diff --git a/DOWNSTREAM_OWNERS b/DOWNSTREAM_OWNERS new file mode 100644 index 0000000000..3f5abef6e3 --- /dev/null +++ b/DOWNSTREAM_OWNERS @@ -0,0 +1,5 @@ +approvers: + - sttts + - xrstf + - mjudeikis + - embik diff --git a/DOWNSTREAM_OWNERS_ALIASES b/DOWNSTREAM_OWNERS_ALIASES new file mode 100644 index 0000000000..e69de29bb2 diff --git a/go.mod b/go.mod index 3fd1aa9562..5e6cf5249c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module sigs.k8s.io/controller-runtime go 1.22.0 require ( + github.com/evanphx/json-patch v5.6.0+incompatible // Using v4 to match upstream github.com/evanphx/json-patch/v5 v5.9.0 github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 diff --git a/go.sum b/go.sum index cb957a9e14..154b3c3ab7 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -18,8 +18,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -46,8 +46,11 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= @@ -60,20 +63,27 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 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.0.0-alpha.0.0.20230926071920-57d168bcbe34 h1:tom0JX5OmAeOOmkGv8LaYHDtA1xAKDiQL5U0vhYYgdM= +github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34/go.mod h1:cWoaYGHl1nlzdEM2xvMzIASkEZJZLSf5nhe17M7wDhw= +github.com/kcp-dev/controller-runtime/examples/kcp v0.0.0-20240429114249-607a854c2cfe h1:blMRNjSiWkdZHQeU4B0WraZVVVniDklkUSdom7FWlxU= +github.com/kcp-dev/controller-runtime/examples/kcp v0.0.0-20240429114249-607a854c2cfe/go.mod h1:J5yVL/5SPHDegVbEV9k7K/WpjVFvQUZMt/KB+sPR0V0= +github.com/kcp-dev/kcp/sdk v0.24.0 h1:ZTfStDOQshVU2cnrqjgMo9xb0VNblkmrgMRtl0PCQEY= +github.com/kcp-dev/kcp/sdk v0.24.0/go.mod h1:Pd2xxw/qhgfF2xgHolVwheq9VOJwPtNrBmxgBlYmjfk= +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/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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -122,7 +132,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -216,11 +226,11 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 706f9c6cdd..e89cddacf9 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" "sort" + "strings" "time" "golang.org/x/exp/maps" @@ -169,6 +170,14 @@ type Options struct { // instead of `reconcile.Result{}`. SyncPeriod *time.Duration + // NewInformerFunc is a function that is used to create SharedIndexInformers. + // Defaults to cache.NewSharedIndexInformer from client-go + NewInformerFunc client.NewInformerFunc + + // Indexers is the indexers that the informers will be configured to use. + // Will always have the standard NamespaceIndex. + Indexers toolscache.Indexers + // ReaderFailOnMissingInformer configures the cache to return a ErrResourceNotCached error when a user // requests, using Get() and List(), a resource the cache does not already have an informer for. // @@ -225,9 +234,6 @@ type Options struct { // ByObject restricts the cache's ListWatch to the desired fields per GVK at the specified object. // If unset, this will fall through to the Default* settings. ByObject map[client.Object]ByObject - - // newInformer allows overriding of NewSharedIndexInformer for testing. - newInformer *func(toolscache.ListerWatcher, runtime.Object, time.Duration, toolscache.Indexers) toolscache.SharedIndexInformer } // ByObject offers more fine-grained control over the cache's ListWatch by object. @@ -398,9 +404,10 @@ func newCache(restConfig *rest.Config, opts Options) newCacheFunc { Transform: config.Transform, WatchErrorHandler: opts.DefaultWatchErrorHandler, UnsafeDisableDeepCopy: ptr.Deref(config.UnsafeDisableDeepCopy, false), - NewInformer: opts.newInformer, + NewInformer: opts.NewInformerFunc, }), readerFailOnMissingInformer: opts.ReaderFailOnMissingInformer, + clusterIndexes: strings.HasSuffix(restConfig.Host, "/clusters/*"), } } } @@ -507,6 +514,10 @@ func defaultOpts(config *rest.Config, opts Options) (Options, error) { if opts.SyncPeriod == nil { opts.SyncPeriod = &defaultSyncPeriod } + + if opts.NewInformerFunc == nil { + opts.NewInformerFunc = toolscache.NewSharedIndexInformer + } return opts, nil } diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 7a21c87c37..1b37a2d94f 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -544,14 +544,9 @@ func NonBlockingGetTest(createCacheFunc func(config *rest.Config, opts cache.Opt Expect(err).NotTo(HaveOccurred()) By("creating the informer cache") - v := reflect.ValueOf(&opts).Elem() - newInformerField := v.FieldByName("newInformer") - newFakeInformer := func(_ kcache.ListerWatcher, _ runtime.Object, _ time.Duration, _ kcache.Indexers) kcache.SharedIndexInformer { + opts.NewInformerFunc = func(_ kcache.ListerWatcher, _ runtime.Object, _ time.Duration, _ kcache.Indexers) kcache.SharedIndexInformer { return &controllertest.FakeInformer{Synced: false} } - reflect.NewAt(newInformerField.Type(), newInformerField.Addr().UnsafePointer()). - Elem(). - Set(reflect.ValueOf(&newFakeInformer)) informerCache, err = createCacheFunc(cfg, opts) Expect(err).NotTo(HaveOccurred()) By("running the cache and waiting for it to sync") diff --git a/pkg/cache/defaulting_test.go b/pkg/cache/defaulting_test.go index 3c01bf8404..930a1535ba 100644 --- a/pkg/cache/defaulting_test.go +++ b/pkg/cache/defaulting_test.go @@ -401,6 +401,9 @@ func TestDefaultOpts(t *testing.T) { t.Fatal(err) } + // We cannot reference kcp.NewInformerWithClusterIndexes due to import cycle. + defaulted.NewInformerFunc = nil + if diff := tc.verification(defaulted); diff != "" { t.Errorf("expected config differs from actual: %s", diff) } diff --git a/pkg/cache/informer_cache.go b/pkg/cache/informer_cache.go index 091667b7fa..71b94da174 100644 --- a/pkg/cache/informer_cache.go +++ b/pkg/cache/informer_cache.go @@ -31,6 +31,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/cache/internal" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + + "github.com/kcp-dev/logicalcluster/v3" ) var ( @@ -68,6 +70,7 @@ type informerCache struct { scheme *runtime.Scheme *internal.Informers readerFailOnMissingInformer bool + clusterIndexes bool } // Get implements Reader. @@ -217,10 +220,10 @@ func (ic *informerCache) IndexField(ctx context.Context, obj client.Object, fiel if err != nil { return err } - return indexByField(informer, field, extractValue) + return indexByField(informer, field, extractValue, ic.clusterIndexes) } -func indexByField(informer Informer, field string, extractValue client.IndexerFunc) error { +func indexByField(informer Informer, field string, extractValue client.IndexerFunc, clusterIndexes bool) error { indexFunc := func(objRaw interface{}) ([]string, error) { // TODO(directxman12): check if this is the correct type? obj, isObj := objRaw.(client.Object) @@ -233,6 +236,13 @@ func indexByField(informer Informer, field string, extractValue client.IndexerFu } ns := meta.GetNamespace() + keyFunc := internal.KeyToNamespacedKey + if clusterName := logicalcluster.From(obj); clusterIndexes && !clusterName.Empty() { + keyFunc = func(ns, val string) string { + return internal.KeyToClusteredKey(clusterName.String(), ns, val) + } + } + rawVals := extractValue(obj) var vals []string if ns == "" { @@ -242,14 +252,15 @@ func indexByField(informer Informer, field string, extractValue client.IndexerFu // if we need to add non-namespaced versions too, double the length vals = make([]string, len(rawVals)*2) } + for i, rawVal := range rawVals { // save a namespaced variant, so that we can ask // "what are all the object matching a given index *in a given namespace*" - vals[i] = internal.KeyToNamespacedKey(ns, rawVal) + vals[i] = keyFunc(ns, rawVal) if ns != "" { // if we have a namespace, also inject a special index key for listing // regardless of the object namespace - vals[i+len(rawVals)] = internal.KeyToNamespacedKey("", rawVal) + vals[i+len(rawVals)] = keyFunc("", rawVal) } } diff --git a/pkg/cache/internal/cache_reader.go b/pkg/cache/internal/cache_reader.go index 81ee960b73..aaf0966f5a 100644 --- a/pkg/cache/internal/cache_reader.go +++ b/pkg/cache/internal/cache_reader.go @@ -21,6 +21,9 @@ import ( "fmt" "reflect" + kcpcache "github.com/kcp-dev/apimachinery/v2/pkg/cache" + "github.com/kcp-dev/logicalcluster/v3" + apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/fields" @@ -31,6 +34,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/internal/field/selector" + "sigs.k8s.io/controller-runtime/pkg/kontext" ) // CacheReader is a client.Reader. @@ -54,12 +58,22 @@ type CacheReader struct { } // Get checks the indexer for the object and writes a copy of it if found. -func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, _ ...client.GetOption) error { +func (c *CacheReader) Get(ctx context.Context, key client.ObjectKey, out client.Object, _ ...client.GetOption) error { if c.scopeName == apimeta.RESTScopeNameRoot { key.Namespace = "" } storeKey := objectKeyToStoreKey(key) + // create cluster-aware key for KCP + _, isClusterAware := c.indexer.GetIndexers()[kcpcache.ClusterAndNamespaceIndexName] + clusterName, _ := kontext.ClusterFrom(ctx) + if isClusterAware && clusterName.Empty() { + return fmt.Errorf("cluster-aware cache requires a cluster in context") + } + if isClusterAware { + storeKey = clusterName.String() + "|" + storeKey + } + // Lookup the object from the indexer cache obj, exists, err := c.indexer.GetByKey(storeKey) if err != nil { @@ -105,7 +119,7 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Ob } // List lists items out of the indexer and writes them to out. -func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...client.ListOption) error { +func (c *CacheReader) List(ctx context.Context, out client.ObjectList, opts ...client.ListOption) error { var objs []interface{} var err error @@ -116,6 +130,9 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli return fmt.Errorf("continue list option is not supported by the cache") } + _, isClusterAware := c.indexer.GetIndexers()[kcpcache.ClusterAndNamespaceIndexName] + clusterName, _ := kontext.ClusterFrom(ctx) + switch { case listOpts.FieldSelector != nil: requiresExact := selector.RequiresExactMatch(listOpts.FieldSelector) @@ -125,11 +142,19 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli // list all objects by the field selector. If this is namespaced and we have one, ask for the // namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces" // namespace. - objs, err = byIndexes(c.indexer, listOpts.FieldSelector.Requirements(), listOpts.Namespace) + objs, err = byIndexes(c.indexer, listOpts.FieldSelector.Requirements(), clusterName, listOpts.Namespace) case listOpts.Namespace != "": - objs, err = c.indexer.ByIndex(cache.NamespaceIndex, listOpts.Namespace) + if isClusterAware && !clusterName.Empty() { + objs, err = c.indexer.ByIndex(kcpcache.ClusterAndNamespaceIndexName, kcpcache.ClusterAndNamespaceIndexKey(clusterName, listOpts.Namespace)) + } else { + objs, err = c.indexer.ByIndex(cache.NamespaceIndex, listOpts.Namespace) + } default: - objs = c.indexer.List() + if isClusterAware && !clusterName.Empty() { + objs, err = c.indexer.ByIndex(kcpcache.ClusterIndexName, kcpcache.ClusterIndexKey(clusterName)) + } else { + objs = c.indexer.List() + } } if err != nil { return err @@ -177,16 +202,22 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli return apimeta.SetList(out, runtimeObjs) } -func byIndexes(indexer cache.Indexer, requires fields.Requirements, namespace string) ([]interface{}, error) { +func byIndexes(indexer cache.Indexer, requires fields.Requirements, clusterName logicalcluster.Name, namespace string) ([]interface{}, error) { var ( err error objs []interface{} vals []string ) indexers := indexer.GetIndexers() + _, isClusterAware := indexers[kcpcache.ClusterAndNamespaceIndexName] for idx, req := range requires { indexName := FieldIndexName(req.Field) - indexedValue := KeyToNamespacedKey(namespace, req.Value) + var indexedValue string + if isClusterAware { + indexedValue = KeyToClusteredKey(clusterName.String(), namespace, req.Value) + } else { + indexedValue = KeyToNamespacedKey(namespace, req.Value) + } if idx == 0 { // we use first require to get snapshot data // TODO(halfcrazy): use complicated index when client-go provides byIndexes @@ -253,3 +284,9 @@ func KeyToNamespacedKey(ns string, baseKey string) string { } return allNamespacesNamespace + "/" + baseKey } + +// KeyToClusteredKey prefixes the given index key with a cluster name +// for use in field selector indexes. +func KeyToClusteredKey(clusterName string, ns string, baseKey string) string { + return clusterName + "|" + KeyToNamespacedKey(ns, baseKey) +} diff --git a/pkg/cache/internal/informers.go b/pkg/cache/internal/informers.go index cd8c6774ca..ae10b2f600 100644 --- a/pkg/cache/internal/informers.go +++ b/pkg/cache/internal/informers.go @@ -47,7 +47,7 @@ type InformersOpts struct { Mapper meta.RESTMapper ResyncPeriod time.Duration Namespace string - NewInformer *func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer + NewInformer func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer Selector Selector Transform cache.TransformFunc UnsafeDisableDeepCopy bool @@ -58,7 +58,7 @@ type InformersOpts struct { func NewInformers(config *rest.Config, options *InformersOpts) *Informers { newInformer := cache.NewSharedIndexInformer if options.NewInformer != nil { - newInformer = *options.NewInformer + newInformer = options.NewInformer } return &Informers{ config: config, diff --git a/pkg/cache/kcp_test.go b/pkg/cache/kcp_test.go new file mode 100644 index 0000000000..11c48457e9 --- /dev/null +++ b/pkg/cache/kcp_test.go @@ -0,0 +1,138 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("informer cache against a kube cluster", func() { + BeforeEach(func() { + By("Annotating the default namespace with kcp.io/cluster") + cl, err := client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + ns := &corev1.Namespace{} + err = cl.Get(context.Background(), client.ObjectKey{Name: "default"}, ns) + Expect(err).NotTo(HaveOccurred()) + ns.Annotations = map[string]string{"kcp.io/cluster": "cluster1"} + err = cl.Update(context.Background(), ns) + Expect(err).NotTo(HaveOccurred()) + }) + + Describe("KCP cluster-unaware informer cache", func() { + // Test whether we can have a cluster-unaware informer cache against a single workspace. + // I.e. every object has a kcp.io/cluster annotation, but it should not be taken + // into consideration by the cache to compute the key. + It("should be able to get the default namespace despite kcp.io/cluster annotation", func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c, err := cache.New(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + + go c.Start(ctx) //nolint:errcheck // Start is blocking, and error not relevant here. + c.WaitForCacheSync(ctx) + + By("By getting the default namespace with the informer") + ns := &corev1.Namespace{} + err = c.Get(ctx, client.ObjectKey{Name: "default"}, ns) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should support indexes with cluster-less keys", func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c, err := cache.New(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + + By("Indexing the default namespace by name") + err = c.IndexField(ctx, &corev1.Namespace{}, "name-clusterless", func(obj client.Object) []string { + return []string{"key-" + obj.GetName()} + }) + Expect(err).NotTo(HaveOccurred()) + + go c.Start(ctx) //nolint:errcheck // Start is blocking, and error not relevant here. + c.WaitForCacheSync(ctx) + + By("By getting the default namespace via the custom index") + nss := &corev1.NamespaceList{} + err = c.List(ctx, nss, client.MatchingFieldsSelector{ + Selector: fields.OneTermEqualSelector("name-clusterless", "key-default"), + }) + Expect(err).NotTo(HaveOccurred()) + Expect(nss.Items).To(HaveLen(1)) + }) + }) + + // TODO: get envtest in place with kcp + /* + Describe("KCP cluster-aware informer cache", func() { + It("should be able to get the default namespace with kcp.io/cluster annotation", func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c, err := kcp.NewClusterAwareCache(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + + go c.Start(ctx) //nolint:errcheck // Start is blocking, and error not relevant here. + c.WaitForCacheSync(ctx) + + By("By getting the default namespace with the informer, but cluster-less key should fail") + ns := &corev1.Namespace{} + err = c.Get(ctx, client.ObjectKey{Name: "default"}, ns) + Expect(err).To(HaveOccurred()) + + By("By getting the default namespace with the informer, but cluster-aware key should succeed") + err = c.Get(kontext.WithCluster(ctx, "cluster1"), client.ObjectKey{Name: "default", Namespace: "cluster1"}, ns) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should support indexes with cluster-aware keys", func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c, err := kcp.NewClusterAwareCache(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + + By("Indexing the default namespace by name") + err = c.IndexField(ctx, &corev1.Namespace{}, "name-clusteraware", func(obj client.Object) []string { + return []string{"key-" + obj.GetName()} + }) + Expect(err).NotTo(HaveOccurred()) + + go c.Start(ctx) //nolint:errcheck // Start is blocking, and error not relevant here. + c.WaitForCacheSync(ctx) + + By("By getting the default namespace via the custom index") + nss := &corev1.NamespaceList{} + err = c.List(ctx, nss, client.MatchingFieldsSelector{ + Selector: fields.OneTermEqualSelector("name-clusteraware", "key-default"), + }) + Expect(err).NotTo(HaveOccurred()) + Expect(nss.Items).To(HaveLen(1)) + }) + }) + */ +}) diff --git a/pkg/client/client.go b/pkg/client/client.go index fe9862b814..c41b4f72f7 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -71,6 +71,14 @@ type CacheOptions struct { // NewClientFunc allows a user to define how to create a client. type NewClientFunc func(config *rest.Config, options Options) (Client, error) +// NewAPIReaderFunc allows a user to define how to create an API server reader. +type NewAPIReaderFunc func(config *rest.Config, options Options) (Reader, error) + +// NewAPIReader creates a new API server reader. +func NewAPIReader(config *rest.Config, options Options) (Reader, error) { + return New(config, options) +} + // New returns a new Client using the provided config and Options. // // By default, the client surfaces warnings returned by the server. To diff --git a/pkg/client/interfaces.go b/pkg/client/interfaces.go index 3cd745e4c0..6c47fc35b8 100644 --- a/pkg/client/interfaces.go +++ b/pkg/client/interfaces.go @@ -18,9 +18,11 @@ package client import ( "context" + "time" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/cache" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" @@ -44,6 +46,10 @@ type Patch interface { Data(obj Object) ([]byte, error) } +// NewInformerFunc describes a function that creates SharedIndexInformers. +// Its signature matches cache.NewSharedIndexInformer from client-go. +type NewInformerFunc func(cache.ListerWatcher, runtime.Object, time.Duration, cache.Indexers) cache.SharedIndexInformer + // TODO(directxman12): is there a sane way to deal with get/delete options? // Reader knows how to read and list Kubernetes objects. diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 248893ea31..8836658d2b 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -121,6 +121,11 @@ type Options struct { // By default, the client will use the cache for reads and direct calls for writes. Client client.Options + // NewAPIReaderFunc is the function that creates the APIReader client to be + // used by the manager. If not set this will use the default new APIReader + // function. + NewAPIReader client.NewAPIReaderFunc + // NewClient is the func that creates the client to be used by the manager. // If not set this will create a Client backed by a Cache for read operations // and a direct Client for write operations. @@ -230,7 +235,7 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) { } // Create the API Reader, a client with no cache. - clientReader, err := client.New(config, client.Options{ + clientReader, err := options.NewAPIReader(config, client.Options{ HTTPClient: options.HTTPClient, Scheme: options.Scheme, Mapper: mapper, @@ -280,6 +285,10 @@ func setOptionsDefaults(options Options, config *rest.Config) (Options, error) { options.MapperProvider = apiutil.NewDynamicRESTMapper } + if options.NewAPIReader == nil { + options.NewAPIReader = client.NewAPIReader + } + // Allow users to define how to create a new client if options.NewClient == nil { options.NewClient = client.New diff --git a/pkg/handler/enqueue.go b/pkg/handler/enqueue.go index 1a1d1ab2f4..8c0e3733e0 100644 --- a/pkg/handler/enqueue.go +++ b/pkg/handler/enqueue.go @@ -20,6 +20,8 @@ import ( "context" "reflect" + "github.com/kcp-dev/logicalcluster/v3" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/util/workqueue" "sigs.k8s.io/controller-runtime/pkg/client" @@ -52,25 +54,16 @@ func (e *TypedEnqueueRequestForObject[T]) Create(ctx context.Context, evt event. enqueueLog.Error(nil, "CreateEvent received with no metadata", "event", evt) return } - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: evt.Object.GetName(), - Namespace: evt.Object.GetNamespace(), - }}) + q.Add(request(evt.Object)) } // Update implements EventHandler. func (e *TypedEnqueueRequestForObject[T]) Update(ctx context.Context, evt event.TypedUpdateEvent[T], q workqueue.TypedRateLimitingInterface[reconcile.Request]) { switch { case !isNil(evt.ObjectNew): - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: evt.ObjectNew.GetName(), - Namespace: evt.ObjectNew.GetNamespace(), - }}) + q.Add(request(evt.ObjectNew)) case !isNil(evt.ObjectOld): - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: evt.ObjectOld.GetName(), - Namespace: evt.ObjectOld.GetNamespace(), - }}) + q.Add(request(evt.ObjectOld)) default: enqueueLog.Error(nil, "UpdateEvent received with no metadata", "event", evt) } @@ -82,10 +75,7 @@ func (e *TypedEnqueueRequestForObject[T]) Delete(ctx context.Context, evt event. enqueueLog.Error(nil, "DeleteEvent received with no metadata", "event", evt) return } - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: evt.Object.GetName(), - Namespace: evt.Object.GetNamespace(), - }}) + q.Add(request(evt.Object)) } // Generic implements EventHandler. @@ -94,10 +84,18 @@ func (e *TypedEnqueueRequestForObject[T]) Generic(ctx context.Context, evt event enqueueLog.Error(nil, "GenericEvent received with no metadata", "event", evt) return } - q.Add(reconcile.Request{NamespacedName: types.NamespacedName{ - Name: evt.Object.GetName(), - Namespace: evt.Object.GetNamespace(), - }}) + q.Add(request(evt.Object)) +} + +func request(obj client.Object) reconcile.Request { + return reconcile.Request{ + // TODO(kcp) Need to implement a non-kcp-specific way to support this + ClusterName: logicalcluster.From(obj).String(), + NamespacedName: types.NamespacedName{ + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + }, + } } func isNil(arg any) bool { diff --git a/pkg/handler/enqueue_owner.go b/pkg/handler/enqueue_owner.go index 1680043b46..12b32d1546 100644 --- a/pkg/handler/enqueue_owner.go +++ b/pkg/handler/enqueue_owner.go @@ -20,6 +20,8 @@ import ( "context" "fmt" + "github.com/kcp-dev/logicalcluster/v3" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -181,9 +183,12 @@ func (e *enqueueRequestForOwner[object]) getOwnerReconcileRequest(obj metav1.Obj // object in the event. if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group { // Match found - add a Request for the object referred to in the OwnerReference - request := reconcile.Request{NamespacedName: types.NamespacedName{ - Name: ref.Name, - }} + request := reconcile.Request{ + ClusterName: logicalcluster.From(object).String(), + NamespacedName: types.NamespacedName{ + Name: ref.Name, + }, + } // if owner is not namespaced then we should not set the namespace mapping, err := e.mapper.RESTMapping(e.groupKind, refGV.Version) diff --git a/pkg/kcp/helper.go b/pkg/kcp/helper.go new file mode 100644 index 0000000000..85d3900127 --- /dev/null +++ b/pkg/kcp/helper.go @@ -0,0 +1,34 @@ +/* +Copyright 2024 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" + + "github.com/kcp-dev/logicalcluster/v3" + "sigs.k8s.io/controller-runtime/pkg/kontext" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// WithClusterInContext injects a cluster name into a context such that +// cluster clients and cache work out of the box. +func WithClusterInContext(r reconcile.Reconciler) reconcile.Reconciler { + return reconcile.Func(func(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + ctx = kontext.WithCluster(ctx, logicalcluster.Name(req.ClusterName)) + return r.Reconcile(ctx, req) + }) +} diff --git a/pkg/kcp/wrappers.go b/pkg/kcp/wrappers.go new file mode 100644 index 0000000000..597be213c2 --- /dev/null +++ b/pkg/kcp/wrappers.go @@ -0,0 +1,250 @@ +/* +Copyright 2022 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 ( + "fmt" + "net/http" + "regexp" + "strings" + "time" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" + k8scache "k8s.io/client-go/tools/cache" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/kontext" + "sigs.k8s.io/controller-runtime/pkg/manager" + + kcpcache "github.com/kcp-dev/apimachinery/v2/pkg/cache" + "github.com/kcp-dev/apimachinery/v2/third_party/informers" + "github.com/kcp-dev/logicalcluster/v3" +) + +// NewClusterAwareManager returns a kcp-aware manager with appropriate defaults for cache and +// client creation. +func NewClusterAwareManager(cfg *rest.Config, options ctrl.Options) (manager.Manager, error) { + if options.NewCache == nil { + options.NewCache = NewClusterAwareCache + } + + if options.NewAPIReader == nil { + options.NewAPIReader = NewClusterAwareAPIReader + } + + if options.NewClient == nil { + options.NewClient = NewClusterAwareClient + } + + if options.MapperProvider == nil { + options.MapperProvider = NewClusterAwareMapperProvider + } + + cfg.Wrap(func(rt http.RoundTripper) http.RoundTripper { + return newClusterRoundTripper(rt) + }) + return ctrl.NewManager(cfg, options) +} + +// NewInformerWithClusterIndexes returns a SharedIndexInformer that is configured +// ClusterIndexName and ClusterAndNamespaceIndexName indexes. +func NewInformerWithClusterIndexes(lw k8scache.ListerWatcher, obj runtime.Object, syncPeriod time.Duration, indexers k8scache.Indexers) k8scache.SharedIndexInformer { + indexers[kcpcache.ClusterIndexName] = kcpcache.ClusterIndexFunc + indexers[kcpcache.ClusterAndNamespaceIndexName] = kcpcache.ClusterAndNamespaceIndexFunc + + return informers.NewSharedIndexInformer(lw, obj, syncPeriod, indexers) +} + +// NewClusterAwareCache returns a cache.Cache that handles multi-cluster watches. +func NewClusterAwareCache(config *rest.Config, opts cache.Options) (cache.Cache, error) { + c := rest.CopyConfig(config) + c.Host += "/clusters/*" + + opts.NewInformerFunc = NewInformerWithClusterIndexes + return cache.New(c, opts) +} + +// NewClusterAwareAPIReader returns a client.Reader that provides read-only access to the API server, +// and is configured to use the context to scope requests to the proper cluster. To scope requests, +// pass the request context with the cluster set. +// Example: +// +// import ( +// "context" +// kcpclient "github.com/kcp-dev/apimachinery/v2/pkg/client" +// ctrl "sigs.k8s.io/controller-runtime" +// ) +// func (r *reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +// ctx = kcpclient.WithCluster(ctx, req.ObjectKey.Cluster) +// // from here on pass this context to all client calls +// ... +// } +func NewClusterAwareAPIReader(config *rest.Config, opts client.Options) (client.Reader, error) { + httpClient, err := ClusterAwareHTTPClient(config) + if err != nil { + return nil, err + } + opts.HTTPClient = httpClient + return client.NewAPIReader(config, opts) +} + +// NewClusterAwareClient returns a client.Client that is configured to use the context +// to scope requests to the proper cluster. To scope requests, pass the request context with the cluster set. +// Example: +// +// import ( +// "context" +// kcpclient "github.com/kcp-dev/apimachinery/v2/pkg/client" +// ctrl "sigs.k8s.io/controller-runtime" +// ) +// func (r *reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +// ctx = kcpclient.WithCluster(ctx, req.ObjectKey.Cluster) +// // from here on pass this context to all client calls +// ... +// } +func NewClusterAwareClient(config *rest.Config, opts client.Options) (client.Client, error) { + httpClient, err := ClusterAwareHTTPClient(config) + if err != nil { + return nil, err + } + opts.HTTPClient = httpClient + return client.New(config, opts) +} + +// NewClusterAwareClientForConfig returns a client.Client that is configured to use the context to scope +// requests to the proper cluster. To scope requests, pass the request context with the cluster set. +// Example: +// +// import ( +// "context" +// kcpclient "github.com/kcp-dev/apimachinery/v2/pkg/client" +// ctrl "sigs.k8s.io/controller-runtime" +// ) +// func (r *reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +// ctx = kcpclient.WithCluster(ctx, req.ObjectKey.Cluster) +// // from here on pass this context to all client calls +// ... +// } +func NewClusterAwareClientForConfig(config *rest.Config, httpClient *http.Client) (client.Client, error) { + restMapper, err := NewClusterAwareMapperProvider(config, httpClient) + if err != nil { + return nil, err + } + return client.New(config, client.Options{ + Mapper: restMapper, + HTTPClient: httpClient, + }) +} + +// ClusterAwareHTTPClient returns an http.Client with a cluster aware round tripper. +func ClusterAwareHTTPClient(config *rest.Config) (*http.Client, error) { + httpClient, err := rest.HTTPClientFor(config) + if err != nil { + return nil, err + } + + httpClient.Transport = newClusterRoundTripper(httpClient.Transport) + return httpClient, nil +} + +// NewClusterAwareMapperProvider is a MapperProvider that returns a logical cluster aware meta.RESTMapper. +func NewClusterAwareMapperProvider(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error) { + mapperCfg := rest.CopyConfig(c) + if !strings.HasSuffix(mapperCfg.Host, "/clusters/*") { + mapperCfg.Host += "/clusters/*" + } + + return apiutil.NewDynamicRESTMapper(mapperCfg, httpClient) +} + +// ClusterAwareBuilderWithOptions returns a cluster aware Cache constructor that will build +// a cache honoring the options argument, this is useful to specify options like +// SelectorsDefaultNamespaces +// WARNING: If SelectorsByObject is specified, filtered out resources are not +// returned. +// WARNING: If UnsafeDisableDeepCopy is enabled, you must DeepCopy any object +// returned from cache get/list before mutating it. +func ClusterAwareBuilderWithOptions(options cache.Options) cache.NewCacheFunc { + return func(config *rest.Config, opts cache.Options) (cache.Cache, error) { + if options.Scheme == nil { + options.Scheme = opts.Scheme + } + if options.Mapper == nil { + options.Mapper = opts.Mapper + } + if options.SyncPeriod == nil { + options.SyncPeriod = opts.SyncPeriod + } + if opts.DefaultNamespaces == nil { + opts.DefaultNamespaces = options.DefaultNamespaces + } + + return NewClusterAwareCache(config, options) + } +} + +// clusterRoundTripper is a cluster aware wrapper around http.RoundTripper. +type clusterRoundTripper struct { + delegate http.RoundTripper +} + +// newClusterRoundTripper creates a new cluster aware round tripper. +func newClusterRoundTripper(delegate http.RoundTripper) *clusterRoundTripper { + return &clusterRoundTripper{ + delegate: delegate, + } +} + +func (c *clusterRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + cluster, ok := kontext.ClusterFrom(req.Context()) + if ok { + req = req.Clone(req.Context()) + req.URL.Path = generatePath(req.URL.Path, cluster.Path()) + req.URL.RawPath = generatePath(req.URL.RawPath, cluster.Path()) + } + return c.delegate.RoundTrip(req) +} + +// apiRegex matches any string that has /api/ or /apis/ in it. +var apiRegex = regexp.MustCompile(`(/api/|/apis/)`) + +// generatePath formats the request path to target the specified cluster. +func generatePath(originalPath string, clusterPath logicalcluster.Path) string { + // If the originalPath already has cluster.Path() then the path was already modifed and no change needed + if strings.Contains(originalPath, clusterPath.RequestPath()) { + return originalPath + } + // If the originalPath has /api/ or /apis/ in it, it might be anywhere in the path, so we use a regex to find and + // replaces /api/ or /apis/ with $cluster/api/ or $cluster/apis/ + if apiRegex.MatchString(originalPath) { + return apiRegex.ReplaceAllString(originalPath, fmt.Sprintf("%s$1", clusterPath.RequestPath())) + } + // Otherwise, we're just prepending /clusters/$name + path := clusterPath.RequestPath() + // if the original path is relative, add a / separator + if len(originalPath) > 0 && originalPath[0] != '/' { + path += "/" + } + // finally append the original path + path += originalPath + return path +} diff --git a/pkg/kontext/kontext.go b/pkg/kontext/kontext.go new file mode 100644 index 0000000000..4de151a742 --- /dev/null +++ b/pkg/kontext/kontext.go @@ -0,0 +1,24 @@ +package kontext + +import ( + "context" + + "github.com/kcp-dev/logicalcluster/v3" +) + +type key int + +const ( + keyCluster key = iota +) + +// WithCluster injects a cluster name into a context. +func WithCluster(ctx context.Context, cluster logicalcluster.Name) context.Context { + return context.WithValue(ctx, keyCluster, cluster) +} + +// ClusterFrom extracts a cluster name from the context. +func ClusterFrom(ctx context.Context) (logicalcluster.Name, bool) { + s, ok := ctx.Value(keyCluster).(logicalcluster.Name) + return s, ok +} diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 3166f4818f..6abc0b0412 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -126,6 +126,11 @@ type Options struct { // Only use a custom NewCache if you know what you are doing. NewCache cache.NewCacheFunc + // NewAPIReaderFunc is the function that creates the APIReader client to be + // used by the manager. If not set this will use the default new APIReader + // function. + NewAPIReader client.NewAPIReaderFunc + // Client is the client.Options that will be used to create the default Client. // By default, the client will use the cache for reads and direct calls for writes. Client client.Options @@ -330,6 +335,7 @@ func New(config *rest.Config, options Options) (Manager, error) { clusterOptions.MapperProvider = options.MapperProvider clusterOptions.Logger = options.Logger clusterOptions.NewCache = options.NewCache + clusterOptions.NewAPIReader = options.NewAPIReader clusterOptions.NewClient = options.NewClient clusterOptions.Cache = options.Cache clusterOptions.Client = options.Client diff --git a/pkg/reconcile/reconcile.go b/pkg/reconcile/reconcile.go index ee63f681cc..3e24fadb65 100644 --- a/pkg/reconcile/reconcile.go +++ b/pkg/reconcile/reconcile.go @@ -50,6 +50,10 @@ func (r *Result) IsZero() bool { type Request struct { // NamespacedName is the name and namespace of the object to reconcile. types.NamespacedName + + // ClusterName can be used for reconciling requests across multiple clusters, + // to prevent objects with the same name and namespace from conflicting + ClusterName string } /* From fc8185822aacffd6d0e2562fec1e8cb58454b926 Mon Sep 17 00:00:00 2001 From: Mangirdas Judeikis Date: Fri, 5 Apr 2024 18:27:15 +0300 Subject: [PATCH 3/9] UPSTREAM: : kcp example Signed-off-by: Dr. Stefan Schimanski --- .gitignore | 2 + .prow.yaml | 23 +- Makefile | 3 + examples/builtins/main.go | 1 + examples/kcp/Makefile | 91 +++++ examples/kcp/README.md | 85 +++++ .../kcp/apis/v1alpha1/groupversion_info.go | 36 ++ examples/kcp/apis/v1alpha1/widget_types.go | 56 +++ .../apis/v1alpha1/zz_generated.deepcopy.go | 98 +++++ examples/kcp/config/consumers/bootstrap.go | 43 +++ .../config/consumers/consumer1-workspace.yaml | 14 + .../config/consumers/consumer2-workspace.yaml | 14 + .../config/crds/data.my.domain_widgets.yaml | 55 +++ examples/kcp/config/helpers/bootstrap.go | 143 ++++++++ examples/kcp/config/main.go | 113 ++++++ examples/kcp/config/widgets/bootstrap.go | 43 +++ .../resources/apiexport-data.my.domain.yaml | 16 + ...resourceschema-widgets.data.my.domain.yaml | 52 +++ .../kcp/config/widgets/resources/bootstrap.go | 47 +++ .../kcp/config/widgets/widgets-workspace.yaml | 14 + .../config/widgets/widgets-workspacetype.yaml | 12 + .../kcp/controllers/configmap/reconciler.go | 145 ++++++++ examples/kcp/controllers/widget/reconciler.go | 83 +++++ examples/kcp/go.mod | 117 ++++++ examples/{scratch-env => kcp}/go.sum | 82 ++++- examples/kcp/hack/go-install.sh | 62 ++++ examples/kcp/hack/tools/go.mod | 61 ++++ examples/kcp/hack/tools/go.sum | 240 +++++++++++++ examples/kcp/hack/tools/tools.go | 25 ++ examples/kcp/hack/update-codegen-crds.sh | 50 +++ examples/kcp/main.go | 199 +++++++++++ examples/kcp/test/e2e/audit-policy.yaml | 30 ++ examples/kcp/test/e2e/controller_test.go | 336 ++++++++++++++++++ examples/scratch-env/go.mod | 69 ---- examples/scratch-env/main.go | 132 ------- go.mod | 8 +- go.sum | 6 - 37 files changed, 2392 insertions(+), 214 deletions(-) create mode 100644 examples/kcp/Makefile create mode 100644 examples/kcp/README.md create mode 100644 examples/kcp/apis/v1alpha1/groupversion_info.go create mode 100644 examples/kcp/apis/v1alpha1/widget_types.go create mode 100644 examples/kcp/apis/v1alpha1/zz_generated.deepcopy.go create mode 100644 examples/kcp/config/consumers/bootstrap.go create mode 100644 examples/kcp/config/consumers/consumer1-workspace.yaml create mode 100644 examples/kcp/config/consumers/consumer2-workspace.yaml create mode 100644 examples/kcp/config/crds/data.my.domain_widgets.yaml create mode 100644 examples/kcp/config/helpers/bootstrap.go create mode 100644 examples/kcp/config/main.go create mode 100644 examples/kcp/config/widgets/bootstrap.go create mode 100644 examples/kcp/config/widgets/resources/apiexport-data.my.domain.yaml create mode 100644 examples/kcp/config/widgets/resources/apiresourceschema-widgets.data.my.domain.yaml create mode 100644 examples/kcp/config/widgets/resources/bootstrap.go create mode 100644 examples/kcp/config/widgets/widgets-workspace.yaml create mode 100644 examples/kcp/config/widgets/widgets-workspacetype.yaml create mode 100644 examples/kcp/controllers/configmap/reconciler.go create mode 100644 examples/kcp/controllers/widget/reconciler.go create mode 100644 examples/kcp/go.mod rename examples/{scratch-env => kcp}/go.sum (69%) create mode 100755 examples/kcp/hack/go-install.sh create mode 100644 examples/kcp/hack/tools/go.mod create mode 100644 examples/kcp/hack/tools/go.sum create mode 100644 examples/kcp/hack/tools/tools.go create mode 100755 examples/kcp/hack/update-codegen-crds.sh create mode 100644 examples/kcp/main.go create mode 100644 examples/kcp/test/e2e/audit-policy.yaml create mode 100644 examples/kcp/test/e2e/controller_test.go delete mode 100644 examples/scratch-env/go.mod delete mode 100644 examples/scratch-env/main.go diff --git a/.gitignore b/.gitignore index 2ddc5a8b87..4b09af17f4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ tools/setup-envtest/out junit-report.xml /artifacts + +examples/kcp/.gitignore diff --git a/.prow.yaml b/.prow.yaml index 54514caa1a..1f9f775c1c 100644 --- a/.prow.yaml +++ b/.prow.yaml @@ -10,4 +10,25 @@ presubmits: - image: ghcr.io/kcp-dev/infra/build:1.22.2-1 command: - make - - test \ No newline at end of file + - test + + - name: pull-controller-runtime-example-e2e + decorate: true + # only run e2e tests if code changed. + run_if_changed: "(pkg|examples|go.mod|go.sum|Makefile|.prow.yaml)" + clone_uri: "https://github.com/kcp-dev/controller-runtime" + labels: + preset-goproxy: "true" + spec: + containers: + - image: ghcr.io/kcp-dev/infra/build:1.22.2-1 + env: + - name: KUBECONFIG + value: /home/prow/go/src/github.com/kcp-dev/controller-runtime/examples/kcp/.test/kcp.kubeconfig + command: + - make + - test-kcp-e2e + resources: + requests: + memory: 6Gi + cpu: 4 diff --git a/Makefile b/Makefile index 9d92b97730..0575eb8124 100644 --- a/Makefile +++ b/Makefile @@ -216,3 +216,6 @@ verify-apidiff: $(GO_APIDIFF) ## Check for API differences go-version: ## Print the go version we use to compile our binaries and images @echo $(GO_VERSION) +.PHONY: test-kcp-e2e +test-kcp-e2e: + cd examples/kcp && make kcp-server kcp-controller test diff --git a/examples/builtins/main.go b/examples/builtins/main.go index 5a6e313f7b..cd2ca0b4d8 100644 --- a/examples/builtins/main.go +++ b/examples/builtins/main.go @@ -42,6 +42,7 @@ func main() { // Setup a Manager entryLog.Info("setting up manager") + mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{}) if err != nil { entryLog.Error(err, "unable to set up overall controller manager") diff --git a/examples/kcp/Makefile b/examples/kcp/Makefile new file mode 100644 index 0000000000..b0d167a9df --- /dev/null +++ b/examples/kcp/Makefile @@ -0,0 +1,91 @@ +SHELL := /bin/bash + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +GO_INSTALL = ./hack/go-install.sh + +LOCALBIN ?= $(shell pwd)/bin +TOOLS_DIR=hack/tools +TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin) +ARTIFACT_DIR ?= .test + +KCP ?= $(LOCALBIN)/kcp +KUBECTL_KCP ?= $(LOCALBIN)/kubectl-kcp + +KCP_VERSION ?= 0.23.0 +CONTROLLER_GEN := $(TOOLS_BIN_DIR)/controller-gen +export CONTROLLER_GEN # so hack scripts can use it + +KCP_APIGEN_VER := v0.21.0 +KCP_APIGEN_BIN := apigen +KCP_APIGEN_GEN := $(TOOLS_BIN_DIR)/$(KCP_APIGEN_BIN)-$(KCP_APIGEN_VER) +export KCP_APIGEN_GEN # so hack scripts can use it + +OS ?= $(shell go env GOOS ) +ARCH ?= $(shell go env GOARCH ) + +$(KCP): ## Download kcp locally if necessary. + mkdir -p $(LOCALBIN) + curl -L -s -o - https://github.com/kcp-dev/kcp/releases/download/v$(KCP_VERSION)/kcp_$(KCP_VERSION)_$(OS)_$(ARCH).tar.gz | tar --directory $(LOCALBIN)/../ -xvzf - bin/kcp + touch $(KCP) # we download an "old" file, so make will re-download to refresh it unless we make it newer than the owning dir + +$(KUBECTL_KCP): ## Download kcp kubectl plugins locally if necessary. + curl -L -s -o - https://github.com/kcp-dev/kcp/releases/download/v$(KCP_VERSION)/kubectl-kcp-plugin_$(KCP_VERSION)_$(OS)_$(ARCH).tar.gz | tar --directory $(LOCALBIN)/../ -xvzf - bin + touch $(KUBECTL_KCP) # we download an "old" file, so make will re-download to refresh it unless we make it newer than the owning dir + +$(KCP_APIGEN_GEN): + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) github.com/kcp-dev/kcp/sdk/cmd/apigen $(KCP_APIGEN_BIN) $(KCP_APIGEN_VER) + +$(CONTROLLER_GEN): $(TOOLS_DIR)/go.mod # Build controller-gen from tools folder. + cd $(TOOLS_DIR) && go build -tags=tools -o bin/controller-gen sigs.k8s.io/controller-tools/cmd/controller-gen + +build: $(KCP) $(KUBECTL_KCP) build-controller + +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +build-controller: ## Build the controller binary. + go build -o $(LOCALBIN)/kcp-controller ./main.go + +.PHONY: kcp-server +kcp-server: $(KCP) $(ARTIFACT_DIR)/kcp ## Run the kcp server. + @if [[ ! -s $(ARTIFACT_DIR)/kcp.log ]]; then ( $(KCP) start -v 5 --root-directory $(ARTIFACT_DIR)/kcp --kubeconfig-path $(ARTIFACT_DIR)/kcp.kubeconfig --audit-log-maxsize 1024 --audit-log-mode=batch --audit-log-batch-max-wait=1s --audit-log-batch-max-size=1000 --audit-log-batch-buffer-size=10000 --audit-log-batch-throttle-burst=15 --audit-log-batch-throttle-enable=true --audit-log-batch-throttle-qps=10 --audit-policy-file ./test/e2e/audit-policy.yaml --audit-log-path $(ARTIFACT_DIR)/audit.log >$(ARTIFACT_DIR)/kcp.log 2>&1 & ); fi + @echo "Waiting for kcp server to generate kubeconfig..." + @while true; do if [[ ! -s $(ARTIFACT_DIR)/kcp.kubeconfig ]]; then sleep 0.2; else break; fi; done + @echo "Waiting for kcp server to be ready..." + @while true; do if ! kubectl --kubeconfig $(ARTIFACT_DIR)/kcp.kubeconfig get --raw /readyz >$(ARTIFACT_DIR)/kcp.probe.log 2>&1; then sleep 0.2; else break; fi; done + @echo "kcp server is ready and running in the background. To stop run 'make test-cleanup'" + +.PHONY: kcp-bootstrap +kcp-bootstrap: ## Bootstrap the kcp server. + @go run ./config/main.go + +.PHONY: kcp-controller +kcp-controller: build kcp-bootstrap ## Run the kcp-controller. + @echo "Starting kcp-controller in the background. To stop run 'make test-cleanup'" + @if [[ ! -s $(ARTIFACT_DIR)/controller.log ]]; then ( ./bin/kcp-controller >$(ARTIFACT_DIR)/controller.log 2>&1 & ); fi + +.PHONY: test-e2e-cleanup +test-cleanup: ## Clean up processes and directories from an end-to-end test run. + rm -rf $(ARTIFACT_DIR) || true + pkill -sigterm kcp || true + pkill -sigterm kubectl || true + pkill -sigterm kcp-controller || true + +$(ARTIFACT_DIR)/kcp: ## Create a directory for the kcp server data. + mkdir -p $(ARTIFACT_DIR)/kcp + +generate: build $(CONTROLLER_GEN) $(KCP_APIGEN_GEN) # Generate code + ./hack/update-codegen-crds.sh + +run-local: build-controller kcp-bootstrap + ./bin/kcp-controller + +.PHONY: test # Run tests +test: + go test ./... --workspace=root --kubeconfig=$(CURDIR)/$(ARTIFACT_DIR)/kcp.kubeconfig diff --git a/examples/kcp/README.md b/examples/kcp/README.md new file mode 100644 index 0000000000..760f141055 --- /dev/null +++ b/examples/kcp/README.md @@ -0,0 +1,85 @@ +# controller-runtime-example +An example project that is multi-cluster aware and works with [kcp](https://github.com/kcp-dev/kcp) + +## Description + +In this example, we intentionally not using advanced kubebuilder patterns to keep the example simple and easy to understand. +In the future, we will add more advanced examples. Example covers 3 parts: +1. KCP bootstrapping - creating APIExport & consumer workspaces + 1. Creating WorkspaceType for particular exports +2. Running controller with APIExport aware configuration and reconciling multiple consumer workspaces + + +This example contains an example project that works with APIExports and multiple kcp workspaces. It demonstrates +two reconcilers: + +1. ConfigMap + 1. Get a ConfigMap for the key from the queue, from the correct logical cluster + 2. If the ConfigMap has labels["name"], set labels["response"] = "hello-$name" and save the changes + 3. List all ConfigMaps in the logical cluster and log each one's namespace and name + 4. If the ConfigMap from step 1 has data["namespace"] set, create a namespace whose name is the data value. + 5. If the ConfigMap from step 1 has data["secretData"] set, create a secret in the same namespace as the ConfigMap, + with an owner reference to the ConfigMap, and data["dataFromCM"] set to the data value. + +2. Widget + 1. Show how to list all Widget instances across all logical clusters + 2. Get a Widget for the key from the queue, from the correct logical cluster + 3. List all Widgets in the same logical cluster + 4. Count the number of Widgets (list length) + 5. Make sure `.status.total` matches the current count (via a `patch`) + +## Getting Started + +### Running on kcp + +1. Run KCP with the following command: + +```sh +make kcp-server +``` + +From this point onwards you can inspect kcp configuration using kubeconfig: + +```sh +export KUBECONFIG=.test/kcp.kubeconfig +``` + +1. Bootstrap the KCP server with the following command: + +```sh +export KUBECONFIG=./.test/kcp.kubeconfig +make kcp-bootstrap +``` + +1. Run controller: + +```sh +export KUBECONFIG=./.test/kcp.kubeconfig +make run-local +``` + +1. In separate shell you can run tests to exercise the controller: + +```sh +export KUBECONFIG=./.test/kcp.kubeconfig +make test +``` + +### Uninstall resources +To delete the resources from kcp: + +```sh +make test-clean +``` + + + +### How it works +This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) + +It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/) +which provides a reconcile function responsible for synchronizing resources until the desired state is reached. + + + + diff --git a/examples/kcp/apis/v1alpha1/groupversion_info.go b/examples/kcp/apis/v1alpha1/groupversion_info.go new file mode 100644 index 0000000000..5e15b53ba7 --- /dev/null +++ b/examples/kcp/apis/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2024. + +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 v1alpha1 contains API Schema definitions for the data v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=data.my.domain +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "data.my.domain", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/examples/kcp/apis/v1alpha1/widget_types.go b/examples/kcp/apis/v1alpha1/widget_types.go new file mode 100644 index 0000000000..038a1cee41 --- /dev/null +++ b/examples/kcp/apis/v1alpha1/widget_types.go @@ -0,0 +1,56 @@ +/* +Copyright 2024. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// WidgetSpec defines the desired state of Widget +type WidgetSpec struct { + Foo string `json:"foo,omitempty"` +} + +// WidgetStatus defines the observed state of Widget +type WidgetStatus struct { + Total int `json:"total,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Widget is the Schema for the widgets API +type Widget struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec WidgetSpec `json:"spec,omitempty"` + Status WidgetStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// WidgetList contains a list of Widget +type WidgetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Widget `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Widget{}, &WidgetList{}) +} diff --git a/examples/kcp/apis/v1alpha1/zz_generated.deepcopy.go b/examples/kcp/apis/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..e4892949c4 --- /dev/null +++ b/examples/kcp/apis/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,98 @@ +//go:build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Widget) DeepCopyInto(out *Widget) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Widget. +func (in *Widget) DeepCopy() *Widget { + if in == nil { + return nil + } + out := new(Widget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Widget) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WidgetList) DeepCopyInto(out *WidgetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Widget, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WidgetList. +func (in *WidgetList) DeepCopy() *WidgetList { + if in == nil { + return nil + } + out := new(WidgetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WidgetList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WidgetSpec) DeepCopyInto(out *WidgetSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WidgetSpec. +func (in *WidgetSpec) DeepCopy() *WidgetSpec { + if in == nil { + return nil + } + out := new(WidgetSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WidgetStatus) DeepCopyInto(out *WidgetStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WidgetStatus. +func (in *WidgetStatus) DeepCopy() *WidgetStatus { + if in == nil { + return nil + } + out := new(WidgetStatus) + in.DeepCopyInto(out) + return out +} diff --git a/examples/kcp/config/consumers/bootstrap.go b/examples/kcp/config/consumers/bootstrap.go new file mode 100644 index 0000000000..ed68b8bebc --- /dev/null +++ b/examples/kcp/config/consumers/bootstrap.go @@ -0,0 +1,43 @@ +/* +Copyright 2024 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 consumers + +import ( + "context" + "embed" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + confighelpers "github.com/kcp-dev/controller-runtime/examples/kcp/config/helpers" +) + +//go:embed *.yaml +var fs embed.FS + +// Bootstrap creates resources in this package by continuously retrying the list. +// This is blocking, i.e. it only returns (with error) when the context is closed or with nil when +// the bootstrapping is successfully completed. +func Bootstrap( + ctx context.Context, + client client.Client, +) error { + log := log.FromContext(ctx) + + log.Info("Bootstrapping consumers workspaces") + return confighelpers.Bootstrap(ctx, client, fs) +} diff --git a/examples/kcp/config/consumers/consumer1-workspace.yaml b/examples/kcp/config/consumers/consumer1-workspace.yaml new file mode 100644 index 0000000000..3161d713a9 --- /dev/null +++ b/examples/kcp/config/consumers/consumer1-workspace.yaml @@ -0,0 +1,14 @@ +apiVersion: tenancy.kcp.io/v1alpha1 +kind: Workspace +metadata: + name: consumer1 + annotations: + bootstrap.kcp.io/create-only: "true" +spec: + type: + name: widgets + path: root + location: + selector: + matchLabels: + name: root diff --git a/examples/kcp/config/consumers/consumer2-workspace.yaml b/examples/kcp/config/consumers/consumer2-workspace.yaml new file mode 100644 index 0000000000..e75e3121a8 --- /dev/null +++ b/examples/kcp/config/consumers/consumer2-workspace.yaml @@ -0,0 +1,14 @@ +apiVersion: tenancy.kcp.io/v1alpha1 +kind: Workspace +metadata: + name: consumer2 + annotations: + bootstrap.kcp.io/create-only: "true" +spec: + type: + name: widgets + path: root + location: + selector: + matchLabels: + name: root diff --git a/examples/kcp/config/crds/data.my.domain_widgets.yaml b/examples/kcp/config/crds/data.my.domain_widgets.yaml new file mode 100644 index 0000000000..02c5feaa38 --- /dev/null +++ b/examples/kcp/config/crds/data.my.domain_widgets.yaml @@ -0,0 +1,55 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: widgets.data.my.domain +spec: + group: data.my.domain + names: + kind: Widget + listKind: WidgetList + plural: widgets + singular: widget + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Widget is the Schema for the widgets API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: WidgetSpec defines the desired state of Widget + properties: + foo: + type: string + type: object + status: + description: WidgetStatus defines the observed state of Widget + properties: + total: + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/examples/kcp/config/helpers/bootstrap.go b/examples/kcp/config/helpers/bootstrap.go new file mode 100644 index 0000000000..93dd23058c --- /dev/null +++ b/examples/kcp/config/helpers/bootstrap.go @@ -0,0 +1,143 @@ +package helpers + +import ( + "bufio" + "bytes" + "context" + "embed" + "errors" + "fmt" + "io" + "text/template" + "time" + + extensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" + apimachineryerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/wait" + kubeyaml "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Bootstrap creates resources in a package's fs by +// continuously retrying the list. This is blocking, i.e. it only returns (with error) +// when the context is closed or with nil when the bootstrapping is successfully completed. +func Bootstrap(ctx context.Context, client client.Client, fs embed.FS) error { + // bootstrap non-crd resources + return wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (bool, error) { + if err := CreateResourcesFromFS(ctx, client, fs); err != nil { + log.FromContext(ctx).WithValues("err", err).Info("failed to bootstrap resources, retrying") + return false, nil + } + return true, nil + }) +} + +// CreateResourcesFromFS creates all resources from a filesystem. +func CreateResourcesFromFS(ctx context.Context, client client.Client, fs embed.FS) error { + files, err := fs.ReadDir(".") + if err != nil { + return err + } + + var errs []error + for _, f := range files { + if f.IsDir() { + continue + } + if err := CreateResourceFromFS(ctx, client, f.Name(), fs); err != nil { + errs = append(errs, err) + } + } + return apimachineryerrors.NewAggregate(errs) +} + +// CreateResourceFromFS creates given resource file. +func CreateResourceFromFS(ctx context.Context, client client.Client, filename string, fs embed.FS) error { + raw, err := fs.ReadFile(filename) + if err != nil { + return fmt.Errorf("could not read %s: %w", filename, err) + } + + if len(raw) == 0 { + return nil // ignore empty files + } + + d := kubeyaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(raw))) + var errs []error + for i := 1; ; i++ { + doc, err := d.Read() + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return err + } + if len(bytes.TrimSpace(doc)) == 0 { + continue + } + + if err := createResourceFromFS(ctx, client, doc); err != nil { + errs = append(errs, fmt.Errorf("failed to create resource %s doc %d: %w", filename, i, err)) + } + } + return apimachineryerrors.NewAggregate(errs) +} + +func createResourceFromFS(ctx context.Context, client client.Client, raw []byte) error { + log := log.FromContext(ctx) + + type Input struct { + Batteries map[string]bool + } + input := Input{ + Batteries: map[string]bool{}, + } + tmpl, err := template.New("manifest").Parse(string(raw)) + if err != nil { + return fmt.Errorf("failed to parse manifest: %w", err) + } + var buf bytes.Buffer + if err := tmpl.Execute(&buf, input); err != nil { + return fmt.Errorf("failed to execute manifest: %w", err) + } + + obj, gvk, err := extensionsapiserver.Codecs.UniversalDeserializer().Decode(buf.Bytes(), nil, &unstructured.Unstructured{}) + if err != nil { + return fmt.Errorf("could not decode raw: %w", err) + } + u, ok := obj.(*unstructured.Unstructured) + if !ok { + return fmt.Errorf("decoded into incorrect type, got %T, wanted %T", obj, &unstructured.Unstructured{}) + } + + key := types.NamespacedName{ + Namespace: u.GetNamespace(), + Name: u.GetName(), + } + err = client.Create(ctx, u) + if err != nil { + if apierrors.IsAlreadyExists(err) { + err = client.Get(ctx, key, u) + if err != nil { + return err + } + + u.SetResourceVersion(u.GetResourceVersion()) + err = client.Update(ctx, u) + if err != nil { + return fmt.Errorf("could not update %s %s: %w", gvk.Kind, key.String(), err) + } else { + log.WithValues("resource", u.GetName(), "kind", gvk.Kind).Info("updated object") + return nil + } + } + return err + } + + log.WithValues("resource", u.GetName(), "kind", gvk.Kind).Info("created object") + + return nil +} diff --git a/examples/kcp/config/main.go b/examples/kcp/config/main.go new file mode 100644 index 0000000000..f41a49313b --- /dev/null +++ b/examples/kcp/config/main.go @@ -0,0 +1,113 @@ +/* +Copyright 2024 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 main + +import ( + "os" + + kcpclienthelper "github.com/kcp-dev/apimachinery/v2/pkg/client" + apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + "github.com/kcp-dev/kcp/sdk/apis/core" + corev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + tenancyv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/tenancy/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/kcp-dev/controller-runtime/examples/kcp/config/consumers" + "github.com/kcp-dev/controller-runtime/examples/kcp/config/widgets" + widgetresources "github.com/kcp-dev/controller-runtime/examples/kcp/config/widgets/resources" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" +) + +// config is bootstrap set of assets for the controller-runtime examples. +// It includes the following assets: +// - crds/* for the Widget type - autogenerated from the Widget type definition +// - widgets/resources/* - a set of Widget resources for KCP to manage. Automatically generated by kcp apigen +// see Makefile & hack/update-codegen-crds.sh for more details + +// It is intended to be running with higher privileges than the examples themselves +// to ensure system (kcp) is bootstrapped. In real world scenarios, this would be +// done by the platform operator to enable service providers to deploy their +// controllers. + +func init() { + utilruntime.Must(tenancyv1alpha1.AddToScheme(clientgoscheme.Scheme)) + utilruntime.Must(clientgoscheme.AddToScheme(clientgoscheme.Scheme)) + utilruntime.Must(corev1alpha1.AddToScheme(clientgoscheme.Scheme)) + utilruntime.Must(apisv1alpha1.AddToScheme(clientgoscheme.Scheme)) +} + +var ( + // clusterName is the workspace to host common APIs. + clusterName = logicalcluster.NewPath("root:widgets") +) + +func main() { + opts := zap.Options{ + Development: true, + } + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + ctx := ctrl.SetupSignalHandler() + log := ctrllog.FromContext(ctx) + + restConfig, err := config.GetConfigWithContext("base") + if err != nil { + log.Error(err, "unable to get config") + os.Exit(1) + } + + restCopy := rest.CopyConfig(restConfig) + restRoot := rest.AddUserAgent(kcpclienthelper.SetCluster(restCopy, core.RootCluster.Path()), "bootstrap-root") + rootClient, err := client.New(restRoot, client.Options{}) + if err != nil { + log.Error(err, "unable to create client") + os.Exit(1) + } + + restCopy = rest.CopyConfig(restConfig) + restWidgets := rest.AddUserAgent(kcpclienthelper.SetCluster(restCopy, clusterName), "bootstrap-widgets") + widgetsClient, err := client.New(restWidgets, client.Options{}) + if err != nil { + log.Error(err, "unable to create client") + os.Exit(1) + } + + err = widgets.Bootstrap(ctx, rootClient) + if err != nil { + log.Error(err, "failed to bootstrap widgets") + os.Exit(1) + } + + err = widgetresources.Bootstrap(ctx, widgetsClient) + if err != nil { + log.Error(err, "failed to bootstrap resources") + os.Exit(1) + } + + err = consumers.Bootstrap(ctx, rootClient) + if err != nil { + log.Error(err, "failed to bootstrap consumers") + os.Exit(1) + } +} diff --git a/examples/kcp/config/widgets/bootstrap.go b/examples/kcp/config/widgets/bootstrap.go new file mode 100644 index 0000000000..f68414eac5 --- /dev/null +++ b/examples/kcp/config/widgets/bootstrap.go @@ -0,0 +1,43 @@ +/* +Copyright 2024 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 widgets + +import ( + "context" + "embed" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + confighelpers "github.com/kcp-dev/controller-runtime/examples/kcp/config/helpers" +) + +//go:embed *.yaml +var fs embed.FS + +// Bootstrap creates resources in this package by continuously retrying the list. +// This is blocking, i.e. it only returns (with error) when the context is closed or with nil when +// the bootstrapping is successfully completed. +func Bootstrap( + ctx context.Context, + client client.Client, +) error { + log := log.FromContext(ctx) + + log.Info("Bootstrapping widgets workspace") + return confighelpers.Bootstrap(ctx, client, fs) +} diff --git a/examples/kcp/config/widgets/resources/apiexport-data.my.domain.yaml b/examples/kcp/config/widgets/resources/apiexport-data.my.domain.yaml new file mode 100644 index 0000000000..2b7bd64182 --- /dev/null +++ b/examples/kcp/config/widgets/resources/apiexport-data.my.domain.yaml @@ -0,0 +1,16 @@ +apiVersion: apis.kcp.io/v1alpha1 +kind: APIExport +metadata: + creationTimestamp: null + name: data.my.domain +spec: + latestResourceSchemas: + - v240406-90e42b7b.widgets.data.my.domain + permissionClaims: + - all: true + resource: configmaps + - all: true + resource: secrets + - all: true + resource: namespaces +status: {} diff --git a/examples/kcp/config/widgets/resources/apiresourceschema-widgets.data.my.domain.yaml b/examples/kcp/config/widgets/resources/apiresourceschema-widgets.data.my.domain.yaml new file mode 100644 index 0000000000..a3d5bfbbaa --- /dev/null +++ b/examples/kcp/config/widgets/resources/apiresourceschema-widgets.data.my.domain.yaml @@ -0,0 +1,52 @@ +apiVersion: apis.kcp.io/v1alpha1 +kind: APIResourceSchema +metadata: + creationTimestamp: null + name: v240406-90e42b7b.widgets.data.my.domain +spec: + group: data.my.domain + names: + kind: Widget + listKind: WidgetList + plural: widgets + singular: widget + scope: Namespaced + versions: + - name: v1alpha1 + schema: + description: Widget is the Schema for the widgets API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: WidgetSpec defines the desired state of Widget + properties: + foo: + type: string + type: object + status: + description: WidgetStatus defines the observed state of Widget + properties: + total: + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/examples/kcp/config/widgets/resources/bootstrap.go b/examples/kcp/config/widgets/resources/bootstrap.go new file mode 100644 index 0000000000..50daebb94b --- /dev/null +++ b/examples/kcp/config/widgets/resources/bootstrap.go @@ -0,0 +1,47 @@ +/* +Copyright 2024 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 resources + +import ( + "context" + "embed" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + confighelpers "github.com/kcp-dev/controller-runtime/examples/kcp/config/helpers" +) + +//go:embed *.yaml +var fs embed.FS + +// Bootstrap creates resources in this package by continuously retrying the list. +// This is blocking, i.e. it only returns (with error) when the context is closed or with nil when +// the bootstrapping is successfully completed. +func Bootstrap( + ctx context.Context, + client client.Client, +) error { + log := log.FromContext(ctx) + + log.Info("Bootstrapping widgets resources") + if err := confighelpers.Bootstrap(ctx, client, fs); err != nil { + return err + } + + return nil +} diff --git a/examples/kcp/config/widgets/widgets-workspace.yaml b/examples/kcp/config/widgets/widgets-workspace.yaml new file mode 100644 index 0000000000..7b3c863c4e --- /dev/null +++ b/examples/kcp/config/widgets/widgets-workspace.yaml @@ -0,0 +1,14 @@ +apiVersion: tenancy.kcp.io/v1alpha1 +kind: Workspace +metadata: + name: widgets + annotations: + bootstrap.kcp.io/create-only: "true" +spec: + type: + name: universal + path: root + location: + selector: + matchLabels: + name: root diff --git a/examples/kcp/config/widgets/widgets-workspacetype.yaml b/examples/kcp/config/widgets/widgets-workspacetype.yaml new file mode 100644 index 0000000000..c9290aa36e --- /dev/null +++ b/examples/kcp/config/widgets/widgets-workspacetype.yaml @@ -0,0 +1,12 @@ +apiVersion: tenancy.kcp.io/v1alpha1 +kind: WorkspaceType +metadata: + name: widgets +spec: + extend: + with: + - name: universal + path: root + defaultAPIBindings: + - path: root:widgets + export: data.my.domain diff --git a/examples/kcp/controllers/configmap/reconciler.go b/examples/kcp/controllers/configmap/reconciler.go new file mode 100644 index 0000000000..b08c3279c0 --- /dev/null +++ b/examples/kcp/controllers/configmap/reconciler.go @@ -0,0 +1,145 @@ +/* +Copyright 2024 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 configmap + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/kcp" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/kontext" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +type Reconciler struct { + Client client.Client +} + +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx).WithValues("cluster", req.ClusterName) + + // Test get + var cm corev1.ConfigMap + if err := r.Client.Get(ctx, req.NamespacedName, &cm); err != nil { + log.Error(err, "unable to get configmap") + return ctrl.Result{}, nil + } + + log.Info("Get: retrieved configMap") + if cm.Labels["name"] != "" { + response := fmt.Sprintf("hello-%s", cm.Labels["name"]) + + if cm.Labels["response"] != response { + cm.Labels["response"] = response + + // Test Update + if err := r.Client.Update(ctx, &cm); err != nil { + return ctrl.Result{}, err + } + + log.Info("Update: updated configMap") + return ctrl.Result{}, nil + } + } + + // Test list + var cms corev1.ConfigMapList + if err := r.Client.List(ctx, &cms); err != nil { + log.Error(err, "unable to list configmaps") + return ctrl.Result{}, nil + } + log.Info("List: got", "itemCount", len(cms.Items)) + found := false + for _, other := range cms.Items { + cluster, ok := kontext.ClusterFrom(ctx) + if !ok { + log.Info("List: got", "clusterName", cluster.String(), "namespace", other.Namespace, "name", other.Name) + } else if other.Name == cm.Name && other.Namespace == cm.Namespace { + if found { + return ctrl.Result{}, fmt.Errorf("there should be listed only one configmap with the given name '%s' for the given namespace '%s' when the clusterName is not available", cm.Name, cm.Namespace) + } + found = true + log.Info("Found in listed configmaps", "namespace", cm.Namespace, "name", cm.Name) + } + } + + // If the configmap has a namespace field, create the corresponding namespace + nsName, exists := cm.Data["namespace"] + if exists { + var namespace corev1.Namespace + if err := r.Client.Get(ctx, types.NamespacedName{Name: nsName}, &namespace); err != nil { + if !apierrors.IsNotFound(err) { + log.Error(err, "unable to get namespace") + return ctrl.Result{}, err + } + + // Need to create ns + namespace.SetName(nsName) + if err = r.Client.Create(ctx, &namespace); err != nil { + log.Error(err, "unable to create namespace") + return ctrl.Result{}, err + } + log.Info("Create: created ", "namespace", nsName) + return ctrl.Result{Requeue: true}, nil + } + log.Info("Exists", "namespace", nsName) + } + + // If the configmap has a secretData field, create a secret in the same namespace + // If the secret already exists but is out of sync, it will be non-destructively patched + secretData, exists := cm.Data["secretData"] + if exists { + var secret corev1.Secret + secret.SetName(cm.GetName()) + secret.SetNamespace(cm.GetNamespace()) + secret.SetOwnerReferences([]metav1.OwnerReference{{ + Name: cm.GetName(), + UID: cm.GetUID(), + APIVersion: "v1", + Kind: "ConfigMap", + Controller: func() *bool { x := true; return &x }(), + }}) + secret.Data = map[string][]byte{"dataFromCM": []byte(secretData)} + + operationResult, err := controllerutil.CreateOrPatch(ctx, r.Client, &secret, func() error { + secret.Data["dataFromCM"] = []byte(secretData) + return nil + }) + if err != nil { + log.Error(err, "unable to create or patch secret") + return ctrl.Result{}, err + } + log.Info(string(operationResult), "secret", secret.GetName()) + } + + return ctrl.Result{}, nil +} + +func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&corev1.ConfigMap{}). + Owns(&corev1.Secret{}). + Complete(kcp.WithClusterInContext(r)) +} diff --git a/examples/kcp/controllers/widget/reconciler.go b/examples/kcp/controllers/widget/reconciler.go new file mode 100644 index 0000000000..0e33f2f889 --- /dev/null +++ b/examples/kcp/controllers/widget/reconciler.go @@ -0,0 +1,83 @@ +/* +Copyright 2024 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 widget + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/kcp" + "sigs.k8s.io/controller-runtime/pkg/log" + + datav1alpha1 "github.com/kcp-dev/controller-runtime/examples/kcp/apis/v1alpha1" +) + +// Reconciler reconciles a Widget object +type Reconciler struct { + Client client.Client +} + +// Reconcile TODO +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := log.FromContext(ctx) + + // Include the clusterName from req.ObjectKey in the logger, similar to the namespace and name keys that are already + // there. + log = log.WithValues("clusterName", req.ClusterName) + + // You probably wouldn't need to do this, but if you wanted to list all instances across all logical clusters: + var allWidgets datav1alpha1.WidgetList + if err := r.Client.List(ctx, &allWidgets); err != nil { + return ctrl.Result{}, err + } + + log.Info("Listed all widgets across all workspaces", "count", len(allWidgets.Items)) + + log.Info("Getting widget") + var w datav1alpha1.Widget + if err := r.Client.Get(ctx, req.NamespacedName, &w); err != nil { + if errors.IsNotFound(err) { + // Normal - was deleted + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + log.Info("Listing all widgets in the current logical cluster") + var list datav1alpha1.WidgetList + if err := r.Client.List(ctx, &list); err != nil { + return ctrl.Result{}, err + } + + log.Info("Patching widget status to store total widget count in the current logical cluster") + orig := w.DeepCopy() + w.Status.Total = len(list.Items) + if err := r.Client.Status().Patch(ctx, &w, client.MergeFromWithOptions(orig, client.MergeFromWithOptimisticLock{})); err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&datav1alpha1.Widget{}). + Complete(kcp.WithClusterInContext(r)) +} diff --git a/examples/kcp/go.mod b/examples/kcp/go.mod new file mode 100644 index 0000000000..db3f29bbaf --- /dev/null +++ b/examples/kcp/go.mod @@ -0,0 +1,117 @@ +module github.com/kcp-dev/controller-runtime/examples/kcp + +go 1.22.0 + +// IMPORTANT: This is only an example replace directive. This is so examples can be run with the latest version of controller-runtime. +// In your own projects, you should not use replace directives like this. Instead, you should replace, but with kcp-dev/controller-runtime instead of ../../ +replace sigs.k8s.io/controller-runtime => ../../ + +require ( + github.com/google/go-cmp v0.6.0 + github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34 + github.com/kcp-dev/kcp/sdk v0.24.0 + github.com/kcp-dev/logicalcluster/v3 v3.0.5 + k8s.io/api v0.30.1 + k8s.io/apiextensions-apiserver v0.30.1 + k8s.io/apimachinery v0.30.1 + k8s.io/client-go v0.30.1 + k8s.io/klog/v2 v2.120.1 + sigs.k8s.io/controller-runtime v0.12.3 +) + +require ( + github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.17.8 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + 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/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/gomega v1.32.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.10 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect + go.etcd.io/etcd/client/v3 v3.5.10 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 // indirect + go.opentelemetry.io/otel v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect + go.opentelemetry.io/otel/metric v1.20.0 // indirect + go.opentelemetry.io/otel/sdk v1.20.0 // indirect + go.opentelemetry.io/otel/trace v1.20.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.3.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiserver v0.30.1 // indirect + k8s.io/component-base v0.30.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + +replace ( + github.com/prometheus/client_golang => github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/common => github.com/prometheus/common v0.44.0 +) diff --git a/examples/scratch-env/go.sum b/examples/kcp/go.sum similarity index 69% rename from examples/scratch-env/go.sum rename to examples/kcp/go.sum index 89d30c15c1..005b3b06b0 100644 --- a/examples/scratch-env/go.sum +++ b/examples/kcp/go.sum @@ -1,3 +1,14 @@ +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -5,14 +16,18 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -32,10 +47,18 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto= +github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -54,6 +77,12 @@ 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.0-alpha.0.0.20230926071920-57d168bcbe34 h1:tom0JX5OmAeOOmkGv8LaYHDtA1xAKDiQL5U0vhYYgdM= +github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34/go.mod h1:cWoaYGHl1nlzdEM2xvMzIASkEZJZLSf5nhe17M7wDhw= +github.com/kcp-dev/kcp/sdk v0.24.0 h1:ZTfStDOQshVU2cnrqjgMo9xb0VNblkmrgMRtl0PCQEY= +github.com/kcp-dev/kcp/sdk v0.24.0/go.mod h1:Pd2xxw/qhgfF2xgHolVwheq9VOJwPtNrBmxgBlYmjfk= +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/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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -65,6 +94,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/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= @@ -93,6 +124,10 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA= +github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -106,6 +141,40 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/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.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= +go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= +go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= +go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= +go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4= +go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= +go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= +go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= +go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM= +go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= +go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA= +go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc= +go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg= +go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0/go.mod h1:HyABWq60Uy1kjJSa2BVOxUVao8Cdick5AWSKPutqy6U= +go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= +go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= +go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA= +go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM= +go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM= +go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= +go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= +go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -130,6 +199,8 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht 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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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= @@ -164,10 +235,13 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= diff --git a/examples/kcp/hack/go-install.sh b/examples/kcp/hack/go-install.sh new file mode 100755 index 0000000000..dd323c5666 --- /dev/null +++ b/examples/kcp/hack/go-install.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +# Copyright 2024 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. + +# Originally copied from +# https://github.com/kubernetes-sigs/cluster-api-provider-gcp/blob/c26a68b23e9317323d5d37660fe9d29b3d2ff40c/scripts/go_install.sh + +set -o errexit +set -o nounset +set -o pipefail + +if [[ -z "${1:-}" ]]; then + echo "must provide module as first parameter" + exit 1 +fi + +if [[ -z "${2:-}" ]]; then + echo "must provide binary name as second parameter" + exit 1 +fi + +if [[ -z "${3:-}" ]]; then + echo "must provide version as third parameter" + exit 1 +fi + +if [[ -z "${GOBIN:-}" ]]; then + echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory." + exit 1 +fi + +mkdir -p "${GOBIN}" + +tmp_dir=$(mktemp -d -t goinstall_XXXXXXXXXX) +function clean { + rm -rf "${tmp_dir}" +} +trap clean EXIT + +rm "${GOBIN}/${2}"* > /dev/null 2>&1 || true + +cd "${tmp_dir}" + +# create a new module in the tmp directory +go mod init fake/mod + +# install the golang module specified as the first argument +go install -tags kcptools "${1}@${3}" +mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}" +ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}" diff --git a/examples/kcp/hack/tools/go.mod b/examples/kcp/hack/tools/go.mod new file mode 100644 index 0000000000..e2c3eac332 --- /dev/null +++ b/examples/kcp/hack/tools/go.mod @@ -0,0 +1,61 @@ +module sigs.k8s.io/controller-runtime/hack/tools + +go 1.21 + +toolchain go1.21.5 + +require ( + github.com/joelanford/go-apidiff v0.8.2 + sigs.k8s.io/controller-tools v0.14.0 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-git/go-git/v5 v5.11.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/gobuffalo/flect v1.0.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/skeema/knownhosts v1.2.1 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20230811145653-3b0b5b66b5f1 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.29.0 // indirect + k8s.io/apiextensions-apiserver v0.29.0 // indirect + k8s.io/apimachinery v0.29.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/examples/kcp/hack/tools/go.sum b/examples/kcp/hack/tools/go.sum new file mode 100644 index 0000000000..151b377222 --- /dev/null +++ b/examples/kcp/hack/tools/go.sum @@ -0,0 +1,240 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= +github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/joelanford/go-apidiff v0.8.2 h1:AvHPY3vYINr6I2xGMHqhDKoszpdsDmH4VHZtit6NJKk= +github.com/joelanford/go-apidiff v0.8.2/go.mod h1:3fPoVVLpPCaU8aOuR7X1xDABzcWbLGKeeMerR2Pxulk= +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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20230811145653-3b0b5b66b5f1 h1:EFPukSCgigmk1W0azH8EMt97AoMjMOgtJ3Z3sGM9AGw= +golang.org/x/exp v0.0.0-20230811145653-3b0b5b66b5f1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +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= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= +k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= +k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= +k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= +k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= +k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A= +sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/examples/kcp/hack/tools/tools.go b/examples/kcp/hack/tools/tools.go new file mode 100644 index 0000000000..481a7c6f01 --- /dev/null +++ b/examples/kcp/hack/tools/tools.go @@ -0,0 +1,25 @@ +// +build tools + +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This package imports things required by build scripts, to force `go mod` to see them as dependencies +package tools + +import ( + _ "github.com/joelanford/go-apidiff" + _ "sigs.k8s.io/controller-tools/cmd/controller-gen" +) diff --git a/examples/kcp/hack/update-codegen-crds.sh b/examples/kcp/hack/update-codegen-crds.sh new file mode 100755 index 0000000000..03524149f0 --- /dev/null +++ b/examples/kcp/hack/update-codegen-crds.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +# Copyright 2024 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. + +set -o errexit +set -o nounset +set -o pipefail +set -o xtrace + +if [[ -z "${CONTROLLER_GEN:-}" ]]; then + echo "You must either set CONTROLLER_GEN to the path to controller-gen or invoke via make" + exit 1 +fi + +REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +# Update generated CRD YAML +( + cd "${REPO_ROOT}/apis" + "${CONTROLLER_GEN}" \ + crd \ + rbac:roleName=manager-role \ + webhook \ + paths="./..." \ + output:crd:artifacts:config="${REPO_ROOT}"/config/crds +) + +for CRD in "${REPO_ROOT}"/config/crds/*.yaml; do + if [ -f "${CRD}-patch" ]; then + echo "Applying ${CRD}" + ${YAML_PATCH} -o "${CRD}-patch" < "${CRD}" > "${CRD}.patched" + mv "${CRD}.patched" "${CRD}" + fi +done + +( + ${KCP_APIGEN_GEN} --input-dir "${REPO_ROOT}"/config/crds --output-dir "${REPO_ROOT}"/config/widgets/resources +) diff --git a/examples/kcp/main.go b/examples/kcp/main.go new file mode 100644 index 0000000000..6480643410 --- /dev/null +++ b/examples/kcp/main.go @@ -0,0 +1,199 @@ +/* +Copyright 2024 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 main + +import ( + "context" + "flag" + "fmt" + "os" + + kcpclienthelper "github.com/kcp-dev/apimachinery/v2/pkg/client" + "github.com/kcp-dev/controller-runtime/examples/kcp/controllers/configmap" + "github.com/kcp-dev/controller-runtime/examples/kcp/controllers/widget" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes/scheme" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + + datav1alpha1 "github.com/kcp-dev/controller-runtime/examples/kcp/apis/v1alpha1" + "github.com/kcp-dev/logicalcluster/v3" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/kcp" + + apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme.Scheme)) + utilruntime.Must(datav1alpha1.AddToScheme(scheme.Scheme)) + utilruntime.Must(apisv1alpha1.AddToScheme(scheme.Scheme)) +} + +func main() { + var opts Options + opts.addFlags(flag.CommandLine) + flag.Parse() + flag.Lookup("v").Value.Set("6") + + ctx := ctrl.SetupSignalHandler() + if err := runController(ctx, opts); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +} + +type Options struct { + MetricsAddr string + EnableLeaderElection bool + ProbeAddr string + APIExportName string + KubeconfigContext string +} + +func (o *Options) addFlags(fs *flag.FlagSet) { + fs.StringVar(&o.KubeconfigContext, "context", "", "kubeconfig context") + fs.StringVar(&o.APIExportName, "api-export-name", "data.my.domain", "The name of the APIExport.") + fs.StringVar(&o.MetricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") + fs.StringVar(&o.ProbeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + fs.BoolVar(&o.EnableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + + klog.InitFlags(fs) +} + +func runController(ctx context.Context, opts Options) error { + log := ctrl.Log.WithName("setup").WithValues("api-export-name", opts.APIExportName) + + // Important: We use non-controller-runtime client loader so we can always + // be sure we have correct kubeconfig file. This ease the development and maintenance + // of the example. In production, you should use the controller-runtime client loader + // to load the kubeconfig file dedicated to workspace where APIExport is located. + // restConfig := ctrl.GetConfigOrDie() + widgetsCluster := logicalcluster.NewPath("root:widgets") + widgetsConfig, err := config.GetConfigWithContext("base") + if err != nil { + return fmt.Errorf("unable to get config: %w", err) + } + widgetsConfig = rest.AddUserAgent(kcpclienthelper.SetCluster(widgetsConfig, widgetsCluster), "kcp-controller-runtime-example") + + ctrlOpts := ctrl.Options{ + HealthProbeBindAddress: opts.ProbeAddr, + LeaderElection: opts.EnableLeaderElection, + LeaderElectionID: "68a0532d.my.domain", + LeaderElectionConfig: widgetsConfig, + } + + // create a manager, either with or without kcp support + var mgr ctrl.Manager + if isKcp, err := kcpAPIsGroupPresent(widgetsConfig); err != nil { + return fmt.Errorf("error checking for kcp APIs group: %w", err) + } else if isKcp { + log.Info("Looking up virtual workspace URL") + exportConfig, err := restConfigForAPIExport(ctx, widgetsConfig, opts.APIExportName) + if err != nil { + return fmt.Errorf("error looking up virtual workspace URL: %w", err) + } + log.Info("Using virtual workspace URL", "url", exportConfig.Host) + + mgr, err = kcp.NewClusterAwareManager(exportConfig, ctrlOpts) + if err != nil { + return fmt.Errorf("unable to create cluster aware manager: %w", err) + } + } else { + log.Info("The apis.kcp.dev group is not present - creating standard manager") + mgr, err = ctrl.NewManager(widgetsConfig, ctrlOpts) + if err != nil { + return fmt.Errorf("unable to create manager: %w", err) + } + } + + // create controllers + if err = (&configmap.Reconciler{Client: mgr.GetClient()}).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create configmap controller: %w", err) + } + if err = (&widget.Reconciler{Client: mgr.GetClient()}).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create widget controller: %w", err) + } + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + return fmt.Errorf("unable to set up health check: %w", err) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + return fmt.Errorf("unable to set up ready check: %w", err) + } + + log.Info("Starting manager") + return mgr.Start(ctx) +} + +// restConfigForAPIExport returns a *rest.Config properly configured to communicate with the endpoint for the +// APIExport's virtual workspace. +func restConfigForAPIExport(ctx context.Context, cfg *rest.Config, apiExportName string) (*rest.Config, error) { + apiExportClient, err := client.New(cfg, client.Options{}) + if err != nil { + return nil, fmt.Errorf("error creating APIExport client: %w", err) + } + + var apiExport apisv1alpha1.APIExport + if err := apiExportClient.Get(ctx, types.NamespacedName{Name: apiExportName}, &apiExport); err != nil { + return nil, fmt.Errorf("error getting APIExport %q: %w", apiExportName, err) + } + + if len(apiExport.Status.VirtualWorkspaces) < 1 { + return nil, fmt.Errorf("APIExport %q status.virtualWorkspaces is empty", apiExportName) + } + + // create a new rest.Config with the APIExport's virtual workspace URL + exportConfig := rest.CopyConfig(cfg) + exportConfig.Host = apiExport.Status.VirtualWorkspaces[0].URL // TODO(ncdc): sharding support + + return exportConfig, nil +} + +func kcpAPIsGroupPresent(cfg *rest.Config) (bool, error) { + discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg) + if err != nil { + return false, fmt.Errorf("failed to create discovery client: %w", err) + } + apiGroupList, err := discoveryClient.ServerGroups() + if err != nil { + return false, fmt.Errorf("failed to get server groups: %w", err) + } + + for _, group := range apiGroupList.Groups { + if group.Name == apisv1alpha1.SchemeGroupVersion.Group { + for _, version := range group.Versions { + if version.Version == apisv1alpha1.SchemeGroupVersion.Version { + return true, nil + } + } + } + } + return false, nil +} diff --git a/examples/kcp/test/e2e/audit-policy.yaml b/examples/kcp/test/e2e/audit-policy.yaml new file mode 100644 index 0000000000..9b1b0384de --- /dev/null +++ b/examples/kcp/test/e2e/audit-policy.yaml @@ -0,0 +1,30 @@ +apiVersion: audit.k8s.io/v1 +kind: Policy +omitStages: + - RequestReceived +omitManagedFields: true +rules: + - level: None + nonResourceURLs: + - "/api*" + - "/version" + + - level: Metadata + resources: + - group: "" + resources: ["secrets", "configmaps"] + - group: "authorization.k8s.io" + resources: ["subjectaccessreviews"] + + - level: Metadata + verbs: ["list", "watch"] + + - level: Metadata + verbs: ["get", "delete"] + omitStages: + - ResponseStarted + + - level: RequestResponse + verbs: ["create", "update", "patch"] + omitStages: + - ResponseStarted diff --git a/examples/kcp/test/e2e/controller_test.go b/examples/kcp/test/e2e/controller_test.go new file mode 100644 index 0000000000..8a28ddf0f6 --- /dev/null +++ b/examples/kcp/test/e2e/controller_test.go @@ -0,0 +1,336 @@ +package e2e + +import ( + "context" + "flag" + "fmt" + "math/rand" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + kcpclienthelper "github.com/kcp-dev/apimachinery/v2/pkg/client" + apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" + corev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + tenancyv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/tenancy/v1alpha1" + "github.com/kcp-dev/kcp/sdk/apis/third_party/conditions/util/conditions" + + "github.com/kcp-dev/logicalcluster/v3" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/wait" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + + datav1alpha1 "github.com/kcp-dev/controller-runtime/examples/kcp/apis/v1alpha1" +) + +// The tests in this package expect to be called when: +// - kcp is running +// - the controller-manager from this repo is running +// +// We can then check that the controllers defined here are working as expected. + +var workspaceName string + +func init() { + rand.Seed(time.Now().Unix()) + flag.StringVar(&workspaceName, "workspace", "", "Workspace in which to run these tests.") +} + +func parentWorkspace(t *testing.T) logicalcluster.Path { + if workspaceName == "" { + t.Fatal("--workspace cannot be empty") + } + + return logicalcluster.NewPath(workspaceName) +} + +func loadClusterConfig(t *testing.T, clusterName logicalcluster.Path) *rest.Config { + t.Helper() + restConfig, err := config.GetConfigWithContext("base") + if err != nil { + t.Fatalf("failed to load *rest.Config: %v", err) + } + return rest.AddUserAgent(kcpclienthelper.SetCluster(restConfig, clusterName), t.Name()) +} + +func loadClient(t *testing.T, clusterName logicalcluster.Path) client.Client { + t.Helper() + scheme := runtime.NewScheme() + if err := clientgoscheme.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add client go to scheme: %v", err) + } + if err := tenancyv1alpha1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add %s to scheme: %v", tenancyv1alpha1.SchemeGroupVersion, err) + } + if err := datav1alpha1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add %s to scheme: %v", datav1alpha1.GroupVersion, err) + } + if err := apisv1alpha1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add %s to scheme: %v", apisv1alpha1.SchemeGroupVersion, err) + } + tenancyClient, err := client.New(loadClusterConfig(t, clusterName), client.Options{Scheme: scheme}) + if err != nil { + t.Fatalf("failed to create a client: %v", err) + } + return tenancyClient +} + +func createWorkspace(t *testing.T, clusterName logicalcluster.Path) client.Client { + t.Helper() + parent, ok := clusterName.Parent() + if !ok { + t.Fatalf("cluster %s has no parent", clusterName) + } + c := loadClient(t, parent) + t.Logf("creating workspace %s", clusterName) + if err := c.Create(context.TODO(), &tenancyv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName.Base(), + }, + Spec: tenancyv1alpha1.WorkspaceSpec{ + Type: tenancyv1alpha1.WorkspaceTypeReference{ + Name: "widgets", + Path: "root", + }, + }, + }); err != nil { + t.Fatalf("failed to create workspace: %s: %v", clusterName, err) + } + + t.Logf("waiting for workspace %s to be ready", clusterName) + var workspace tenancyv1alpha1.Workspace + if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (done bool, err error) { + fetchErr := c.Get(context.TODO(), client.ObjectKey{Name: clusterName.Base()}, &workspace) + if fetchErr != nil { + t.Logf("failed to get workspace %s: %v", clusterName, err) + return false, fetchErr + } + var reason string + if actual, expected := workspace.Status.Phase, corev1alpha1.LogicalClusterPhaseReady; actual != expected { + reason = fmt.Sprintf("phase is %s, not %s", actual, expected) + t.Logf("not done waiting for workspace %s to be ready: %s", clusterName, reason) + } + return reason == "", nil + }); err != nil { + t.Fatalf("workspace %s never ready: %v", clusterName, err) + } + + return waitingForAPIBinding(t, clusterName) +} + +func waitingForAPIBinding(t *testing.T, workspaceCluster logicalcluster.Path) client.Client { + c := loadClient(t, workspaceCluster) + ctx := context.TODO() + apiNamePrefix := "data.my.domain" // matches bootstrapped name + + list := &apisv1alpha1.APIBindingList{} + err := c.List(ctx, list) + if err != nil { + t.Fatalf("failed to list APIBindings: %v", err) + } + + apiName := "" + for _, apiBinding := range list.Items { + if strings.HasPrefix(apiBinding.Name, apiNamePrefix) { + apiName = apiBinding.Name + break + } + } + + t.Logf("waiting for APIBinding %s|%s to be bound", workspaceCluster, apiName) + var apiBinding apisv1alpha1.APIBinding + if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (done bool, err error) { + fetchErr := c.Get(context.TODO(), client.ObjectKey{Name: apiName}, &apiBinding) + if fetchErr != nil { + t.Logf("failed to get APIBinding %s|%s: %v", workspaceCluster, apiName, err) + return false, fetchErr + } + var reason string + if !conditions.IsTrue(&apiBinding, apisv1alpha1.InitialBindingCompleted) { + condition := conditions.Get(&apiBinding, apisv1alpha1.InitialBindingCompleted) + if condition != nil { + reason = fmt.Sprintf("%s: %s", condition.Reason, condition.Message) + } else { + reason = "no condition present" + } + t.Logf("not done waiting for APIBinding %s|%s to be bound: %s", workspaceCluster, apiName, reason) + } + return conditions.IsTrue(&apiBinding, apisv1alpha1.InitialBindingCompleted), nil + }); err != nil { + t.Fatalf("APIBinding %s|%s never bound: %v", workspaceCluster, apiName, err) + } + + return c +} + +const characters = "abcdefghijklmnopqrstuvwxyz" + +func randomName() string { + b := make([]byte, 10) + for i := range b { + b[i] = characters[rand.Intn(len(characters))] + } + return string(b) +} + +// TestConfigMapController verifies that our ConfigMap behavior works. +func TestConfigMapController(t *testing.T) { + t.Parallel() + for i := 0; i < 3; i++ { + t.Run(fmt.Sprintf("attempt-%d", i), func(t *testing.T) { + t.Parallel() + workspaceCluster := parentWorkspace(t).Join(randomName()) + c := createWorkspace(t, workspaceCluster) + + namespaceName := randomName() + t.Logf("creating namespace %s|%s", workspaceCluster, namespaceName) + if err := c.Create(context.TODO(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + }); err != nil { + t.Fatalf("failed to create a namespace: %v", err) + } + + otherNamespaceName := randomName() + data := randomName() + configmapName := randomName() + t.Logf("creating configmap %s|%s/%s", workspaceCluster, namespaceName, configmapName) + if err := c.Create(context.TODO(), &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configmapName, + Namespace: namespaceName, + Labels: map[string]string{ + "name": "timothy", + }, + }, + Data: map[string]string{ + "namespace": otherNamespaceName, + "secretData": data, + }, + }); err != nil { + t.Fatalf("failed to create a configmap: %v", err) + } + + t.Logf("waiting for configmap %s|%s to have a response", workspaceCluster, configmapName) + var configmap corev1.ConfigMap + if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (done bool, err error) { + fetchErr := c.Get(context.TODO(), client.ObjectKey{Namespace: namespaceName, Name: configmapName}, &configmap) + if fetchErr != nil { + t.Logf("failed to get configmap %s|%s/%s: %v", workspaceCluster, namespaceName, configmapName, err) + return false, fetchErr + } + response, ok := configmap.Labels["response"] + if !ok { + t.Logf("configmap %s|%s/%s has no response set", workspaceCluster, namespaceName, configmapName) + } + diff := cmp.Diff(response, "hello-timothy") + if ok && diff != "" { + t.Logf("configmap %s|%s/%s has an invalid response: %v", workspaceCluster, namespaceName, configmapName, diff) + } + return diff == "", nil + }); err != nil { + t.Fatalf("configmap %s|%s/%s never got a response: %v", workspaceCluster, namespaceName, configmapName, err) + } + + t.Logf("waiting for namespace %s|%s to exist", workspaceCluster, otherNamespaceName) + var otherNamespace corev1.Namespace + if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (done bool, err error) { + fetchErr := c.Get(context.TODO(), client.ObjectKey{Name: otherNamespaceName}, &otherNamespace) + if fetchErr != nil && !apierrors.IsNotFound(fetchErr) { + t.Logf("failed to get namespace %s|%s: %v", workspaceCluster, otherNamespaceName, fetchErr) + return false, fetchErr + } + return fetchErr == nil, nil + }); err != nil { + t.Fatalf("namespace %s|%s never created: %v", workspaceCluster, otherNamespaceName, err) + } + + t.Logf("waiting for secret %s|%s/%s to exist and have correct data", workspaceCluster, namespaceName, configmapName) + var secret corev1.Secret + if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (done bool, err error) { + fetchErr := c.Get(context.TODO(), client.ObjectKey{Namespace: namespaceName, Name: configmapName}, &secret) + if fetchErr != nil && !apierrors.IsNotFound(fetchErr) { + t.Logf("failed to get secret %s|%s/%s: %v", workspaceCluster, namespaceName, configmapName, fetchErr) + return false, fetchErr + } + response, ok := secret.Data["dataFromCM"] + if !ok { + t.Logf("secret %s|%s/%s has no data set", workspaceCluster, namespaceName, configmapName) + } + diff := cmp.Diff(string(response), data) + if ok && diff != "" { + t.Logf("secret %s|%s/%s has invalid data: %v", workspaceCluster, namespaceName, configmapName, diff) + } + return diff == "", nil + }); err != nil { + t.Fatalf("secret %s|%s/%s never created: %v", workspaceCluster, namespaceName, configmapName, err) + } + }) + } +} + +// TestWidgetController verifies that our ConfigMap behavior works. +func TestWidgetController(t *testing.T) { + t.Parallel() + for i := 0; i < 3; i++ { + t.Run(fmt.Sprintf("attempt-%d", i), func(t *testing.T) { + t.Parallel() + workspaceCluster := parentWorkspace(t).Join(randomName()) + c := createWorkspace(t, workspaceCluster) + + var totalWidgets int + for i := 0; i < 3; i++ { + namespaceName := randomName() + t.Logf("creating namespace %s|%s", workspaceCluster, namespaceName) + if err := c.Create(context.TODO(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + }); err != nil { + t.Fatalf("failed to create a namespace: %v", err) + } + numWidgets := rand.Intn(10) + for i := 0; i < numWidgets; i++ { + if err := c.Create(context.TODO(), &datav1alpha1.Widget{ + ObjectMeta: metav1.ObjectMeta{Namespace: namespaceName, Name: fmt.Sprintf("widget-%d", i)}, + Spec: datav1alpha1.WidgetSpec{Foo: fmt.Sprintf("intended-%d", i)}, + }); err != nil { + t.Fatalf("failed to create widget: %v", err) + } + } + totalWidgets += numWidgets + } + + t.Logf("waiting for all widgets in cluster %s to have a correct status", workspaceCluster) + var allWidgets datav1alpha1.WidgetList + if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (done bool, err error) { + fetchErr := c.List(context.TODO(), &allWidgets) + if fetchErr != nil { + t.Logf("failed to get widgets in cluster %s: %v", workspaceCluster, err) + return false, fetchErr + } + var errs []error + for _, widget := range allWidgets.Items { + if actual, expected := widget.Status.Total, totalWidgets; actual != expected { + errs = append(errs, fmt.Errorf("widget %s|%s .status.total incorrect: %d != %d", workspaceCluster, widget.Name, actual, expected)) + } + } + validationErr := errors.NewAggregate(errs) + if validationErr != nil { + t.Logf("widgets in cluster %s invalid: %v", workspaceCluster, validationErr) + } + return validationErr == nil, nil + }); err != nil { + t.Fatalf("widgets in cluster %s never got correct statuses: %v", workspaceCluster, err) + } + }) + } +} diff --git a/examples/scratch-env/go.mod b/examples/scratch-env/go.mod deleted file mode 100644 index dceb4d12aa..0000000000 --- a/examples/scratch-env/go.mod +++ /dev/null @@ -1,69 +0,0 @@ -module sigs.k8s.io/controller-runtime/examples/scratch-env - -go 1.22.0 - -require ( - github.com/spf13/pflag v1.0.5 - go.uber.org/zap v1.26.0 - sigs.k8s.io/controller-runtime v0.0.0-00010101000000-000000000000 -) - -require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/x448/float16 v0.8.4 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.3.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.31.0 // indirect - k8s.io/apiextensions-apiserver v0.31.0 // indirect - k8s.io/apimachinery v0.31.0 // indirect - k8s.io/client-go v0.31.0 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) - -replace sigs.k8s.io/controller-runtime => ../.. diff --git a/examples/scratch-env/main.go b/examples/scratch-env/main.go deleted file mode 100644 index b8305ffed3..0000000000 --- a/examples/scratch-env/main.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - goflag "flag" - "os" - - flag "github.com/spf13/pflag" - "go.uber.org/zap" - - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logzap "sigs.k8s.io/controller-runtime/pkg/log/zap" -) - -var ( - crdPaths = flag.StringSlice("crd-paths", nil, "paths to files or directories containing CRDs to install on start") - webhookPaths = flag.StringSlice("webhook-paths", nil, "paths to files or directories containing webhook configurations to install on start") - attachControlPlaneOut = flag.Bool("debug-env", false, "attach to test env (apiserver & etcd) output -- just a convinience flag to force KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT=true") -) - -// have a separate function so we can return an exit code w/o skipping defers -func runMain() int { - loggerOpts := &logzap.Options{ - Development: true, // a sane default - ZapOpts: []zap.Option{zap.AddCaller()}, - } - { - var goFlagSet goflag.FlagSet - loggerOpts.BindFlags(&goFlagSet) - flag.CommandLine.AddGoFlagSet(&goFlagSet) - } - flag.Parse() - ctrl.SetLogger(logzap.New(logzap.UseFlagOptions(loggerOpts))) - ctrl.Log.Info("Starting...") - - log := ctrl.Log.WithName("main") - - env := &envtest.Environment{} - env.CRDInstallOptions.Paths = *crdPaths - env.WebhookInstallOptions.Paths = *webhookPaths - - if *attachControlPlaneOut { - os.Setenv("KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT", "true") - } - - log.Info("Starting apiserver & etcd") - cfg, err := env.Start() - if err != nil { - log.Error(err, "unable to start the test environment") - // shut down the environment in case we started it and failed while - // installing CRDs or provisioning users. - if err := env.Stop(); err != nil { - log.Error(err, "unable to stop the test environment after an error (this might be expected, but just though you should know)") - } - return 1 - } - - log.Info("apiserver running", "host", cfg.Host) - - // NB(directxman12): this group is unfortunately named, but various - // kubernetes versions require us to use it to get "admin" access. - user, err := env.ControlPlane.AddUser(envtest.User{ - Name: "envtest-admin", - Groups: []string{"system:masters"}, - }, nil) - if err != nil { - log.Error(err, "unable to provision admin user, continuing on without it") - return 1 - } - - // TODO(directxman12): add support for writing to a new context in an existing file - kubeconfigFile, err := os.CreateTemp("", "scratch-env-kubeconfig-") - if err != nil { - log.Error(err, "unable to create kubeconfig file, continuing on without it") - return 1 - } - defer os.Remove(kubeconfigFile.Name()) - - { - log := log.WithValues("path", kubeconfigFile.Name()) - log.V(1).Info("Writing kubeconfig") - - kubeConfig, err := user.KubeConfig() - if err != nil { - log.Error(err, "unable to create kubeconfig") - } - - if _, err := kubeconfigFile.Write(kubeConfig); err != nil { - log.Error(err, "unable to save kubeconfig") - return 1 - } - - log.Info("Wrote kubeconfig") - } - - if opts := env.WebhookInstallOptions; opts.LocalServingPort != 0 { - log.Info("webhooks configured for", "host", opts.LocalServingHost, "port", opts.LocalServingPort, "dir", opts.LocalServingCertDir) - } - - ctx := ctrl.SetupSignalHandler() - <-ctx.Done() - - log.Info("Shutting down apiserver & etcd") - err = env.Stop() - if err != nil { - log.Error(err, "unable to stop the test environment") - return 1 - } - - log.Info("Shutdown successful") - return 0 -} - -func main() { - os.Exit(runMain()) -} diff --git a/go.mod b/go.mod index 5e6cf5249c..c69cf43fd5 100644 --- a/go.mod +++ b/go.mod @@ -32,8 +32,12 @@ require ( ) require ( - github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect + github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34 + github.com/kcp-dev/logicalcluster/v3 v3.0.5 +) + +require ( + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect diff --git a/go.sum b/go.sum index 154b3c3ab7..37bb1d64be 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -78,10 +76,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34 h1:tom0JX5OmAeOOmkGv8LaYHDtA1xAKDiQL5U0vhYYgdM= github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34/go.mod h1:cWoaYGHl1nlzdEM2xvMzIASkEZJZLSf5nhe17M7wDhw= -github.com/kcp-dev/controller-runtime/examples/kcp v0.0.0-20240429114249-607a854c2cfe h1:blMRNjSiWkdZHQeU4B0WraZVVVniDklkUSdom7FWlxU= -github.com/kcp-dev/controller-runtime/examples/kcp v0.0.0-20240429114249-607a854c2cfe/go.mod h1:J5yVL/5SPHDegVbEV9k7K/WpjVFvQUZMt/KB+sPR0V0= -github.com/kcp-dev/kcp/sdk v0.24.0 h1:ZTfStDOQshVU2cnrqjgMo9xb0VNblkmrgMRtl0PCQEY= -github.com/kcp-dev/kcp/sdk v0.24.0/go.mod h1:Pd2xxw/qhgfF2xgHolVwheq9VOJwPtNrBmxgBlYmjfk= 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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= From 2e28210143aa2a237f758853247affa9e494a996 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Tue, 20 Aug 2024 23:50:47 +0200 Subject: [PATCH 4/9] UPSTREAM: : make RESTMapper in client cluster-aware Signed-off-by: Dr. Stefan Schimanski --- examples/kcp/go.mod | 1 + go.mod | 1 + go.sum | 2 + pkg/client/client.go | 28 ++- pkg/client/client_rest_resources.go | 54 ++++-- pkg/client/metadata_client.go | 40 +++- pkg/client/typed_client.go | 22 +-- pkg/client/unstructured_client.go | 22 +-- pkg/client/watch.go | 6 +- pkg/kcp/kcp_suite_test.go | 35 ++++ pkg/kcp/kcp_test.go | 277 ++++++++++++++++++++++++++++ pkg/kcp/wrappers.go | 112 ++++++----- 12 files changed, 495 insertions(+), 105 deletions(-) create mode 100644 pkg/kcp/kcp_suite_test.go create mode 100644 pkg/kcp/kcp_test.go diff --git a/examples/kcp/go.mod b/examples/kcp/go.mod index db3f29bbaf..123278f6bf 100644 --- a/examples/kcp/go.mod +++ b/examples/kcp/go.mod @@ -50,6 +50,7 @@ require ( github.com/google/uuid v1.3.1 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/go.mod b/go.mod index c69cf43fd5..b7ebfbee80 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( ) require ( + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34 github.com/kcp-dev/logicalcluster/v3 v3.0.5 ) diff --git a/go.sum b/go.sum index 37bb1d64be..8f45cc4631 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= diff --git a/pkg/client/client.go b/pkg/client/client.go index c41b4f72f7..d021a9c3ec 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -23,6 +23,8 @@ import ( "net/http" "strings" + lru "github.com/hashicorp/golang-lru/v2" + "github.com/kcp-dev/logicalcluster/v3" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -47,6 +49,10 @@ type Options struct { // Mapper, if provided, will be used to map GroupVersionKinds to Resources Mapper meta.RESTMapper + // MapperWithContext, if provided, will be used to map GroupVersionKinds to Resources. + // This overrides Mapper if set. + MapperWithContext func(context.Context) (meta.RESTMapper, error) + // Cache, if provided, is used to read objects from the cache. Cache *CacheOptions @@ -153,16 +159,23 @@ func newClient(config *rest.Config, options Options) (*client, error) { } } + // Init a MapperWithContext if none provided + if options.MapperWithContext == nil { + options.MapperWithContext = func(context.Context) (meta.RESTMapper, error) { return options.Mapper, nil } + } + resources := &clientRestResources{ httpClient: options.HTTPClient, config: config, scheme: options.Scheme, - mapper: options.Mapper, + mapper: options.MapperWithContext, codecs: serializer.NewCodecFactory(options.Scheme), - - structuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta), - unstructuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta), } + cr, err := lru.New[logicalcluster.Path, clusterResources](1000) + if err != nil { + return nil, err + } + resources.clusterResources = cr rawMetaClient, err := metadata.NewForConfigAndClient(metadata.ConfigFor(config), options.HTTPClient) if err != nil { @@ -180,11 +193,16 @@ func newClient(config *rest.Config, options Options) (*client, error) { }, metadataClient: metadataClient{ client: rawMetaClient, - restMapper: options.Mapper, + restMapper: options.MapperWithContext, }, scheme: options.Scheme, mapper: options.Mapper, } + mapperCache, err := lru.New[logicalcluster.Name, meta.RESTMapper](1000) + if err != nil { + return nil, err + } + c.metadataClient.mapperCache = mapperCache if options.Cache == nil || options.Cache.Reader == nil { return c, nil } diff --git a/pkg/client/client_rest_resources.go b/pkg/client/client_rest_resources.go index 2d07879520..1247879e5e 100644 --- a/pkg/client/client_rest_resources.go +++ b/pkg/client/client_rest_resources.go @@ -17,10 +17,13 @@ limitations under the License. package client import ( + "context" "net/http" "strings" "sync" + lru "github.com/hashicorp/golang-lru/v2" + "github.com/kcp-dev/logicalcluster/v3" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -28,8 +31,18 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/kontext" ) +type clusterResources struct { + mapper meta.RESTMapper + + // structuredResourceByType stores structured type metadata + structuredResourceByType map[schema.GroupVersionKind]*resourceMeta + // unstructuredResourceByType stores unstructured type metadata + unstructuredResourceByType map[schema.GroupVersionKind]*resourceMeta +} + // clientRestResources creates and stores rest clients and metadata for Kubernetes types. type clientRestResources struct { // httpClient is the http client to use for requests @@ -42,21 +55,18 @@ type clientRestResources struct { scheme *runtime.Scheme // mapper maps GroupVersionKinds to Resources - mapper meta.RESTMapper + mapper func(ctx context.Context) (meta.RESTMapper, error) // codecs are used to create a REST client for a gvk codecs serializer.CodecFactory - // structuredResourceByType stores structured type metadata - structuredResourceByType map[schema.GroupVersionKind]*resourceMeta - // unstructuredResourceByType stores unstructured type metadata - unstructuredResourceByType map[schema.GroupVersionKind]*resourceMeta - mu sync.RWMutex + clusterResources *lru.Cache[logicalcluster.Path, clusterResources] + mu sync.RWMutex } // newResource maps obj to a Kubernetes Resource and constructs a client for that Resource. // If the object is a list, the resource represents the item's type instead. -func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool) (*resourceMeta, error) { +func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool, mapper meta.RESTMapper) (*resourceMeta, error) { if strings.HasSuffix(gvk.Kind, "List") && isList { // if this was a list, treat it as a request for the item's resource gvk.Kind = gvk.Kind[:len(gvk.Kind)-4] @@ -66,7 +76,7 @@ func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, i if err != nil { return nil, err } - mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) if err != nil { return nil, err } @@ -75,7 +85,7 @@ func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, i // getResource returns the resource meta information for the given type of object. // If the object is a list, the resource represents the item's type instead. -func (c *clientRestResources) getResource(obj runtime.Object) (*resourceMeta, error) { +func (c *clientRestResources) getResource(ctx context.Context, obj runtime.Object) (*resourceMeta, error) { gvk, err := apiutil.GVKForObject(obj, c.scheme) if err != nil { return nil, err @@ -86,9 +96,25 @@ func (c *clientRestResources) getResource(obj runtime.Object) (*resourceMeta, er // It's better to do creation work twice than to not let multiple // people make requests at once c.mu.RLock() - resourceByType := c.structuredResourceByType + cluster, _ := kontext.ClusterFrom(ctx) + cr, found := c.clusterResources.Get(cluster.Path()) + if !found { + m, err := c.mapper(ctx) + if err != nil { + c.mu.RUnlock() + return nil, err + } + cr = clusterResources{ + mapper: m, + structuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta), + unstructuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta), + } + c.clusterResources.Purge() + c.clusterResources.Add(cluster.Path(), cr) + } + resourceByType := cr.structuredResourceByType if isUnstructured { - resourceByType = c.unstructuredResourceByType + resourceByType = cr.unstructuredResourceByType } r, known := resourceByType[gvk] c.mu.RUnlock() @@ -100,7 +126,7 @@ func (c *clientRestResources) getResource(obj runtime.Object) (*resourceMeta, er // Initialize a new Client c.mu.Lock() defer c.mu.Unlock() - r, err = c.newResource(gvk, meta.IsListType(obj), isUnstructured) + r, err = c.newResource(gvk, meta.IsListType(obj), isUnstructured, cr.mapper) if err != nil { return nil, err } @@ -109,8 +135,8 @@ func (c *clientRestResources) getResource(obj runtime.Object) (*resourceMeta, er } // getObjMeta returns objMeta containing both type and object metadata and state. -func (c *clientRestResources) getObjMeta(obj runtime.Object) (*objMeta, error) { - r, err := c.getResource(obj) +func (c *clientRestResources) getObjMeta(ctx context.Context, obj runtime.Object) (*objMeta, error) { + r, err := c.getResource(ctx, obj) if err != nil { return nil, err } diff --git a/pkg/client/metadata_client.go b/pkg/client/metadata_client.go index d0c6b8e13a..8368035607 100644 --- a/pkg/client/metadata_client.go +++ b/pkg/client/metadata_client.go @@ -20,11 +20,15 @@ import ( "context" "fmt" "strings" + "sync" + lru "github.com/hashicorp/golang-lru/v2" + "github.com/kcp-dev/logicalcluster/v3" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/metadata" + "sigs.k8s.io/controller-runtime/pkg/kontext" ) // TODO(directxman12): we could rewrite this on top of the low-level REST @@ -34,12 +38,28 @@ import ( // metadataClient is a client that reads & writes metadata-only requests to/from the API server. type metadataClient struct { - client metadata.Interface - restMapper meta.RESTMapper + client metadata.Interface + restMapper func(ctx context.Context) (meta.RESTMapper, error) + mu sync.Mutex + mapperCache *lru.Cache[logicalcluster.Name, meta.RESTMapper] } -func (mc *metadataClient) getResourceInterface(gvk schema.GroupVersionKind, ns string) (metadata.ResourceInterface, error) { - mapping, err := mc.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version) +func (mc *metadataClient) getResourceInterface(ctx context.Context, gvk schema.GroupVersionKind, ns string) (metadata.ResourceInterface, error) { + cluster, _ := kontext.ClusterFrom(ctx) + mc.mu.Lock() + mapper, _ := mc.mapperCache.Get(cluster) + if mapper == nil { + var err error + mapper, err = mc.restMapper(ctx) + if err != nil { + mc.mu.Unlock() + return nil, err + } + mc.mapperCache.Add(cluster, mapper) + } + mc.mu.Unlock() + + mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) if err != nil { return nil, err } @@ -56,7 +76,7 @@ func (mc *metadataClient) Delete(ctx context.Context, obj Object, opts ...Delete return fmt.Errorf("metadata client did not understand object: %T", obj) } - resInt, err := mc.getResourceInterface(metadata.GroupVersionKind(), metadata.Namespace) + resInt, err := mc.getResourceInterface(ctx, metadata.GroupVersionKind(), metadata.Namespace) if err != nil { return err } @@ -77,7 +97,7 @@ func (mc *metadataClient) DeleteAllOf(ctx context.Context, obj Object, opts ...D deleteAllOfOpts := DeleteAllOfOptions{} deleteAllOfOpts.ApplyOptions(opts) - resInt, err := mc.getResourceInterface(metadata.GroupVersionKind(), deleteAllOfOpts.ListOptions.Namespace) + resInt, err := mc.getResourceInterface(ctx, metadata.GroupVersionKind(), deleteAllOfOpts.ListOptions.Namespace) if err != nil { return err } @@ -93,7 +113,7 @@ func (mc *metadataClient) Patch(ctx context.Context, obj Object, patch Patch, op } gvk := metadata.GroupVersionKind() - resInt, err := mc.getResourceInterface(gvk, metadata.Namespace) + resInt, err := mc.getResourceInterface(ctx, gvk, metadata.Namespace) if err != nil { return err } @@ -127,7 +147,7 @@ func (mc *metadataClient) Get(ctx context.Context, key ObjectKey, obj Object, op getOpts := GetOptions{} getOpts.ApplyOptions(opts) - resInt, err := mc.getResourceInterface(gvk, key.Namespace) + resInt, err := mc.getResourceInterface(ctx, gvk, key.Namespace) if err != nil { return err } @@ -154,7 +174,7 @@ func (mc *metadataClient) List(ctx context.Context, obj ObjectList, opts ...List listOpts := ListOptions{} listOpts.ApplyOptions(opts) - resInt, err := mc.getResourceInterface(gvk, listOpts.Namespace) + resInt, err := mc.getResourceInterface(ctx, gvk, listOpts.Namespace) if err != nil { return err } @@ -175,7 +195,7 @@ func (mc *metadataClient) PatchSubResource(ctx context.Context, obj Object, subR } gvk := metadata.GroupVersionKind() - resInt, err := mc.getResourceInterface(gvk, metadata.Namespace) + resInt, err := mc.getResourceInterface(ctx, gvk, metadata.Namespace) if err != nil { return err } diff --git a/pkg/client/typed_client.go b/pkg/client/typed_client.go index 92afd9a9c2..aff18d8468 100644 --- a/pkg/client/typed_client.go +++ b/pkg/client/typed_client.go @@ -32,7 +32,7 @@ type typedClient struct { // Create implements client.Client. func (c *typedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -51,7 +51,7 @@ func (c *typedClient) Create(ctx context.Context, obj Object, opts ...CreateOpti // Update implements client.Client. func (c *typedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -71,7 +71,7 @@ func (c *typedClient) Update(ctx context.Context, obj Object, opts ...UpdateOpti // Delete implements client.Client. func (c *typedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -90,7 +90,7 @@ func (c *typedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOpti // DeleteAllOf implements client.Client. func (c *typedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -109,7 +109,7 @@ func (c *typedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...Delet // Patch implements client.Client. func (c *typedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -134,7 +134,7 @@ func (c *typedClient) Patch(ctx context.Context, obj Object, patch Patch, opts . // Get implements client.Client. func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error { - r, err := c.resources.getResource(obj) + r, err := c.resources.getResource(ctx, obj) if err != nil { return err } @@ -149,7 +149,7 @@ func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts . // List implements client.Client. func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error { - r, err := c.resources.getResource(obj) + r, err := c.resources.getResource(ctx, obj) if err != nil { return err } @@ -166,7 +166,7 @@ func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOpti } func (c *typedClient) GetSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceGetOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -189,7 +189,7 @@ func (c *typedClient) GetSubResource(ctx context.Context, obj, subResourceObj Ob } func (c *typedClient) CreateSubResource(ctx context.Context, obj Object, subResourceObj Object, subResource string, opts ...SubResourceCreateOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -214,7 +214,7 @@ func (c *typedClient) CreateSubResource(ctx context.Context, obj Object, subReso // UpdateSubResource used by SubResourceWriter to write status. func (c *typedClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...SubResourceUpdateOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -249,7 +249,7 @@ func (c *typedClient) UpdateSubResource(ctx context.Context, obj Object, subReso // PatchSubResource used by SubResourceWriter to write subresource. func (c *typedClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...SubResourcePatchOption) error { - o, err := c.resources.getObjMeta(obj) + o, err := c.resources.getObjMeta(ctx, obj) if err != nil { return err } diff --git a/pkg/client/unstructured_client.go b/pkg/client/unstructured_client.go index 0d96951780..872f316077 100644 --- a/pkg/client/unstructured_client.go +++ b/pkg/client/unstructured_client.go @@ -41,7 +41,7 @@ func (uc *unstructuredClient) Create(ctx context.Context, obj Object, opts ...Cr gvk := u.GetObjectKind().GroupVersionKind() - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -70,7 +70,7 @@ func (uc *unstructuredClient) Update(ctx context.Context, obj Object, opts ...Up gvk := u.GetObjectKind().GroupVersionKind() - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -97,7 +97,7 @@ func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...De return fmt.Errorf("unstructured client did not understand object: %T", obj) } - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -120,7 +120,7 @@ func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts return fmt.Errorf("unstructured client did not understand object: %T", obj) } - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -143,7 +143,7 @@ func (uc *unstructuredClient) Patch(ctx context.Context, obj Object, patch Patch return fmt.Errorf("unstructured client did not understand object: %T", obj) } - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -178,7 +178,7 @@ func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object getOpts := GetOptions{} getOpts.ApplyOptions(opts) - r, err := uc.resources.getResource(obj) + r, err := uc.resources.getResource(ctx, obj) if err != nil { return err } @@ -206,7 +206,7 @@ func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ... gvk := u.GetObjectKind().GroupVersionKind() gvk.Kind = strings.TrimSuffix(gvk.Kind, "List") - r, err := uc.resources.getResource(obj) + r, err := uc.resources.getResource(ctx, obj) if err != nil { return err } @@ -235,7 +235,7 @@ func (uc *unstructuredClient) GetSubResource(ctx context.Context, obj, subResour subResourceObj.SetName(obj.GetName()) } - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -266,7 +266,7 @@ func (uc *unstructuredClient) CreateSubResource(ctx context.Context, obj, subRes subResourceObj.SetName(obj.GetName()) } - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -290,7 +290,7 @@ func (uc *unstructuredClient) UpdateSubResource(ctx context.Context, obj Object, return fmt.Errorf("unstructured client did not understand object: %T", obj) } - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } @@ -328,7 +328,7 @@ func (uc *unstructuredClient) PatchSubResource(ctx context.Context, obj Object, gvk := u.GetObjectKind().GroupVersionKind() - o, err := uc.resources.getObjMeta(obj) + o, err := uc.resources.getObjMeta(ctx, obj) if err != nil { return err } diff --git a/pkg/client/watch.go b/pkg/client/watch.go index 181b22a673..317c87ffc0 100644 --- a/pkg/client/watch.go +++ b/pkg/client/watch.go @@ -67,7 +67,7 @@ func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialO listOpts := w.listOpts(opts...) - resInt, err := w.client.metadataClient.getResourceInterface(gvk, listOpts.Namespace) + resInt, err := w.client.metadataClient.getResourceInterface(ctx, gvk, listOpts.Namespace) if err != nil { return nil, err } @@ -76,7 +76,7 @@ func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialO } func (w *watchingClient) unstructuredWatch(ctx context.Context, obj runtime.Unstructured, opts ...ListOption) (watch.Interface, error) { - r, err := w.client.unstructuredClient.resources.getResource(obj) + r, err := w.client.unstructuredClient.resources.getResource(ctx, obj) if err != nil { return nil, err } @@ -91,7 +91,7 @@ func (w *watchingClient) unstructuredWatch(ctx context.Context, obj runtime.Unst } func (w *watchingClient) typedWatch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error) { - r, err := w.client.typedClient.resources.getResource(obj) + r, err := w.client.typedClient.resources.getResource(ctx, obj) if err != nil { return nil, err } diff --git a/pkg/kcp/kcp_suite_test.go b/pkg/kcp/kcp_suite_test.go new file mode 100644 index 0000000000..b81a20199e --- /dev/null +++ b/pkg/kcp/kcp_suite_test.go @@ -0,0 +1,35 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kcp_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +func TestKCP(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "kcp Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) +}) diff --git a/pkg/kcp/kcp_test.go b/pkg/kcp/kcp_test.go new file mode 100644 index 0000000000..b2b8e3d13b --- /dev/null +++ b/pkg/kcp/kcp_test.go @@ -0,0 +1,277 @@ +package kcp + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/kontext" +) + +var _ = Describe("NewClusterAwareClient", Ordered, func() { + var ( + srv *httptest.Server + mu sync.Mutex + paths []string + cfg *rest.Config + ) + + BeforeAll(func() { + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + mu.Lock() + paths = append(paths, req.URL.Path) + mu.Unlock() + + switch req.URL.Path { + case "/api/v1", "/clusters/root/api/v1", "/clusters/*/api/v1": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"kind":"APIResourceList","groupVersion":"v1","resources":[{"name":"pods","singularName":"pod","namespaced":true,"kind":"Pod","verbs":["create","delete","deletecollection","get","list","patch","update","watch"],"shortNames":["po"],"categories":["all"],"storageVersionHash":"xPOwRZ+Yhw8="}]}`)) + case "/api/v1/pods", "/clusters/root/api/v1/pods", "/clusters/*/api/v1/pods": + if req.URL.Query().Get("watch") != "true" { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"kind": "PodList","apiVersion": "v1","metadata": {"resourceVersion": "184126176"}, "items": [{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"default","resourceVersion":"184126176"}}]}`)) + return + } + fallthrough + default: + _, _ = w.Write([]byte(fmt.Sprintf("Not found %q", req.RequestURI))) + w.WriteHeader(http.StatusNotFound) + } + })) + + cfg = &rest.Config{ + Host: srv.URL, + } + Expect(rest.SetKubernetesDefaults(cfg)).To(Succeed()) + }) + + BeforeEach(func() { + mu.Lock() + defer mu.Unlock() + paths = []string{} + }) + + AfterAll(func() { + srv.Close() + }) + + Describe("with typed list", func() { + It("should work with no cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &corev1.PodList{} + err = cl.List(ctx, pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods"})) + }) + + It("should work with a cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &corev1.PodList{} + err = cl.List(kontext.WithCluster(ctx, "root"), pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods"})) + }) + + It("should work with a wildcard cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &corev1.PodList{} + err = cl.List(kontext.WithCluster(ctx, "*"), pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/*/api/v1", "/clusters/*/api/v1/pods"})) + }) + }) + + Describe("with unstructured list", func() { + It("should work with no cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &unstructured.UnstructuredList{} + pods.SetAPIVersion("v1") + pods.SetKind("PodList") + err = cl.List(ctx, pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods"})) + }) + + It("should work with a cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &unstructured.UnstructuredList{} + pods.SetAPIVersion("v1") + pods.SetKind("PodList") + err = cl.List(kontext.WithCluster(ctx, "root"), pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods"})) + }) + + It("should work with a wildcard cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &unstructured.UnstructuredList{} + pods.SetAPIVersion("v1") + pods.SetKind("PodList") + err = cl.List(kontext.WithCluster(ctx, "*"), pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/*/api/v1", "/clusters/*/api/v1/pods"})) + }) + }) + + Describe("with a metadata object", func() { + It("should work with no cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &metav1.PartialObjectMetadataList{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "PodList"}} + err = cl.List(ctx, pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods"})) + }) + + It("should work with a cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &metav1.PartialObjectMetadataList{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "PodList"}} + err = cl.List(kontext.WithCluster(ctx, "root"), pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods"})) + }) + + It("should work with a wildcard cluster in the kontext", func(ctx context.Context) { + cl, err := NewClusterAwareClient(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + + pods := &metav1.PartialObjectMetadataList{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "PodList"}} + err = cl.List(kontext.WithCluster(ctx, "*"), pods) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/*/api/v1", "/clusters/*/api/v1/pods"})) + }) + }) +}) + +var _ = Describe("NewClusterAwareCache", Ordered, func() { + var ( + srv *httptest.Server + mu sync.Mutex + paths []string + cfg *rest.Config + ) + + BeforeAll(func() { + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + mu.Lock() + pth := req.URL.Path + if req.URL.Query().Get("watch") == "true" { + pth += "?watch=true" + } + paths = append(paths, pth) + mu.Unlock() + + switch { + case req.URL.Path == "/clusters/*/api/v1": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"kind":"APIResourceList","groupVersion":"v1","resources":[{"name":"pods","singularName":"pod","namespaced":true,"kind":"Pod","verbs":["create","delete","deletecollection","get","list","patch","update","watch"],"shortNames":["po"],"categories":["all"],"storageVersionHash":"xPOwRZ+Yhw8="}]}`)) + case req.URL.Path == "/clusters/*/api/v1/pods" && req.URL.Query().Get("watch") != "true": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"kind": "PodList","apiVersion": "v1","metadata": {"resourceVersion": "184126176"}, "items": [{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"default","resourceVersion":"184126176","annotations":{"kcp.io/cluster":"root"}}}]}`)) + case req.URL.Path == "/clusters/*/api/v1/pods" && req.URL.Query().Get("watch") == "true": + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Transfer-Encoding", "chunked") + w.WriteHeader(http.StatusOK) + if w, ok := w.(http.Flusher); ok { + w.Flush() + } + time.Sleep(1 * time.Second) + default: + w.WriteHeader(http.StatusNotFound) + _, _ = w.Write([]byte(fmt.Sprintf("Not found %q", req.RequestURI))) + } + })) + + cfg = &rest.Config{ + Host: srv.URL, + } + Expect(rest.SetKubernetesDefaults(cfg)).To(Succeed()) + }) + + BeforeEach(func() { + mu.Lock() + defer mu.Unlock() + paths = []string{} + }) + + AfterAll(func() { + srv.Close() + }) + + It("should always access wildcard clusters and serve other clusters from memory", func(ctx context.Context) { + c, err := NewClusterAwareCache(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + go func() { + if err := c.Start(ctx); err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + c.WaitForCacheSync(ctx) + + pod := &corev1.Pod{} + err = c.Get(kontext.WithCluster(ctx, "root"), types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + Expect(err).NotTo(HaveOccurred()) + + mu.Lock() + defer mu.Unlock() + Expect(paths).To(Equal([]string{"/clusters/*/api/v1", "/clusters/*/api/v1/pods", "/clusters/*/api/v1/pods?watch=true"})) + }) +}) diff --git a/pkg/kcp/wrappers.go b/pkg/kcp/wrappers.go index 597be213c2..e5d905ed72 100644 --- a/pkg/kcp/wrappers.go +++ b/pkg/kcp/wrappers.go @@ -17,6 +17,7 @@ limitations under the License. package kcp import ( + "context" "fmt" "net/http" "regexp" @@ -27,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" k8scache "k8s.io/client-go/tools/cache" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -56,11 +56,11 @@ func NewClusterAwareManager(cfg *rest.Config, options ctrl.Options) (manager.Man } if options.MapperProvider == nil { - options.MapperProvider = NewClusterAwareMapperProvider + options.MapperProvider = newWildcardClusterMapperProvider } cfg.Wrap(func(rt http.RoundTripper) http.RoundTripper { - return newClusterRoundTripper(rt) + return newClusterAwareRoundTripper(rt) }) return ctrl.NewManager(cfg, options) } @@ -77,7 +77,7 @@ func NewInformerWithClusterIndexes(lw k8scache.ListerWatcher, obj runtime.Object // NewClusterAwareCache returns a cache.Cache that handles multi-cluster watches. func NewClusterAwareCache(config *rest.Config, opts cache.Options) (cache.Cache, error) { c := rest.CopyConfig(config) - c.Host += "/clusters/*" + c.Host = strings.TrimSuffix(c.Host, "/") + "/clusters/*" opts.NewInformerFunc = NewInformerWithClusterIndexes return cache.New(c, opts) @@ -99,11 +99,16 @@ func NewClusterAwareCache(config *rest.Config, opts cache.Options) (cache.Cache, // ... // } func NewClusterAwareAPIReader(config *rest.Config, opts client.Options) (client.Reader, error) { - httpClient, err := ClusterAwareHTTPClient(config) - if err != nil { - return nil, err + if opts.HTTPClient == nil { + httpClient, err := NewClusterAwareHTTPClient(config) + if err != nil { + return nil, err + } + opts.HTTPClient = httpClient + } + if opts.Mapper == nil && opts.MapperWithContext == nil { + opts.MapperWithContext = NewClusterAwareMapperProvider(config, opts.HTTPClient) } - opts.HTTPClient = httpClient return client.NewAPIReader(config, opts) } @@ -122,52 +127,43 @@ func NewClusterAwareAPIReader(config *rest.Config, opts client.Options) (client. // ... // } func NewClusterAwareClient(config *rest.Config, opts client.Options) (client.Client, error) { - httpClient, err := ClusterAwareHTTPClient(config) - if err != nil { - return nil, err + if opts.HTTPClient == nil { + httpClient, err := NewClusterAwareHTTPClient(config) + if err != nil { + return nil, err + } + opts.HTTPClient = httpClient } - opts.HTTPClient = httpClient - return client.New(config, opts) -} - -// NewClusterAwareClientForConfig returns a client.Client that is configured to use the context to scope -// requests to the proper cluster. To scope requests, pass the request context with the cluster set. -// Example: -// -// import ( -// "context" -// kcpclient "github.com/kcp-dev/apimachinery/v2/pkg/client" -// ctrl "sigs.k8s.io/controller-runtime" -// ) -// func (r *reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { -// ctx = kcpclient.WithCluster(ctx, req.ObjectKey.Cluster) -// // from here on pass this context to all client calls -// ... -// } -func NewClusterAwareClientForConfig(config *rest.Config, httpClient *http.Client) (client.Client, error) { - restMapper, err := NewClusterAwareMapperProvider(config, httpClient) - if err != nil { - return nil, err + if opts.Mapper == nil && opts.MapperWithContext == nil { + opts.MapperWithContext = NewClusterAwareMapperProvider(config, opts.HTTPClient) } - return client.New(config, client.Options{ - Mapper: restMapper, - HTTPClient: httpClient, - }) + return client.New(config, opts) } -// ClusterAwareHTTPClient returns an http.Client with a cluster aware round tripper. -func ClusterAwareHTTPClient(config *rest.Config) (*http.Client, error) { +// NewClusterAwareHTTPClient returns an http.Client with a cluster aware round tripper. +func NewClusterAwareHTTPClient(config *rest.Config) (*http.Client, error) { httpClient, err := rest.HTTPClientFor(config) if err != nil { return nil, err } - httpClient.Transport = newClusterRoundTripper(httpClient.Transport) + httpClient.Transport = newClusterAwareRoundTripper(httpClient.Transport) return httpClient, nil } -// NewClusterAwareMapperProvider is a MapperProvider that returns a logical cluster aware meta.RESTMapper. -func NewClusterAwareMapperProvider(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error) { +// NewClusterAwareMapperProvider returns a function producing RESTMapper for the +// cluster specified in the context. +func NewClusterAwareMapperProvider(c *rest.Config, httpClient *http.Client) func(ctx context.Context) (meta.RESTMapper, error) { + return func(ctx context.Context) (meta.RESTMapper, error) { + cluster, _ := kontext.ClusterFrom(ctx) // intentionally ignoring second "found" return value + cl := *httpClient + cl.Transport = clusterRoundTripper{cluster: cluster.Path(), delegate: httpClient.Transport} + return apiutil.NewDynamicRESTMapper(c, &cl) + } +} + +// newWildcardClusterMapperProvider returns a RESTMapper that talks to the /clusters/* endpoint. +func newWildcardClusterMapperProvider(c *rest.Config, httpClient *http.Client) (meta.RESTMapper, error) { mapperCfg := rest.CopyConfig(c) if !strings.HasSuffix(mapperCfg.Host, "/clusters/*") { mapperCfg.Host += "/clusters/*" @@ -202,24 +198,38 @@ func ClusterAwareBuilderWithOptions(options cache.Options) cache.NewCacheFunc { } } -// clusterRoundTripper is a cluster aware wrapper around http.RoundTripper. -type clusterRoundTripper struct { +// clusterAwareRoundTripper is a cluster-aware wrapper around http.RoundTripper +// taking the cluster from the context. +type clusterAwareRoundTripper struct { delegate http.RoundTripper } -// newClusterRoundTripper creates a new cluster aware round tripper. -func newClusterRoundTripper(delegate http.RoundTripper) *clusterRoundTripper { - return &clusterRoundTripper{ +// newClusterAwareRoundTripper creates a new cluster aware round tripper. +func newClusterAwareRoundTripper(delegate http.RoundTripper) *clusterAwareRoundTripper { + return &clusterAwareRoundTripper{ delegate: delegate, } } -func (c *clusterRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { +func (c *clusterAwareRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { cluster, ok := kontext.ClusterFrom(req.Context()) - if ok { + if ok && !cluster.Empty() { + return clusterRoundTripper{cluster: cluster.Path(), delegate: c.delegate}.RoundTrip(req) + } + return c.delegate.RoundTrip(req) +} + +// clusterRoundTripper is static cluster-aware wrapper around http.RoundTripper. +type clusterRoundTripper struct { + cluster logicalcluster.Path + delegate http.RoundTripper +} + +func (c clusterRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + if !c.cluster.Empty() { req = req.Clone(req.Context()) - req.URL.Path = generatePath(req.URL.Path, cluster.Path()) - req.URL.RawPath = generatePath(req.URL.RawPath, cluster.Path()) + req.URL.Path = generatePath(req.URL.Path, c.cluster) + req.URL.RawPath = generatePath(req.URL.RawPath, c.cluster) } return c.delegate.RoundTrip(req) } From 3f5540fc607a26eef25e47b43265113836954ee4 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Fri, 30 Aug 2024 15:39:21 +0200 Subject: [PATCH 5/9] UPSTREAM: : .golang.ci.yml: disable goconst Signed-off-by: Dr. Stefan Schimanski --- .golangci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 4c43665e2b..6dcbfb64d6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,7 +13,6 @@ linters: - exhaustive - exportloopref - ginkgolinter - - goconst - gocritic - gocyclo - gofmt From e5ac6be10c22422a4d382eb361a389d200d3e527 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Sun, 1 Sep 2024 18:33:46 +0200 Subject: [PATCH 6/9] Address comments Signed-off-by: Dr. Stefan Schimanski --- pkg/client/client.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index d021a9c3ec..ab1e7bd3b5 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -58,6 +58,10 @@ type Options struct { // DryRun instructs the client to only perform dry run requests. DryRun *bool + + // KcpClusterDiscoveryCacheSize is the size of the cache for cluster discovery + // information backing the client's REST mapper. + KcpClusterDiscoveryCacheSize int } // CacheOptions are options for creating a cache-backed client. @@ -159,6 +163,10 @@ func newClient(config *rest.Config, options Options) (*client, error) { } } + if options.KcpClusterDiscoveryCacheSize == 0 { + options.KcpClusterDiscoveryCacheSize = 1000 + } + // Init a MapperWithContext if none provided if options.MapperWithContext == nil { options.MapperWithContext = func(context.Context) (meta.RESTMapper, error) { return options.Mapper, nil } @@ -171,7 +179,7 @@ func newClient(config *rest.Config, options Options) (*client, error) { mapper: options.MapperWithContext, codecs: serializer.NewCodecFactory(options.Scheme), } - cr, err := lru.New[logicalcluster.Path, clusterResources](1000) + cr, err := lru.New[logicalcluster.Path, clusterResources](options.KcpClusterDiscoveryCacheSize) if err != nil { return nil, err } @@ -198,7 +206,7 @@ func newClient(config *rest.Config, options Options) (*client, error) { scheme: options.Scheme, mapper: options.Mapper, } - mapperCache, err := lru.New[logicalcluster.Name, meta.RESTMapper](1000) + mapperCache, err := lru.New[logicalcluster.Name, meta.RESTMapper](options.KcpClusterDiscoveryCacheSize) if err != nil { return nil, err } From cb82102bf51687031556e4a61d6effa79d412861 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Sun, 1 Sep 2024 19:10:19 +0200 Subject: [PATCH 7/9] UPSTREAM: : more kcp tests Signed-off-by: Dr. Stefan Schimanski --- examples/kcp/go.mod | 102 +++++++++++++-------------- examples/kcp/go.sum | 164 +++++++++++++++++++++++++++++--------------- go.mod | 5 +- go.sum | 32 ++++----- pkg/kcp/kcp_test.go | 102 +++++++++++++++++++++------ 5 files changed, 260 insertions(+), 145 deletions(-) diff --git a/examples/kcp/go.mod b/examples/kcp/go.mod index 123278f6bf..e2d28b9015 100644 --- a/examples/kcp/go.mod +++ b/examples/kcp/go.mod @@ -11,45 +11,45 @@ require ( github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34 github.com/kcp-dev/kcp/sdk v0.24.0 github.com/kcp-dev/logicalcluster/v3 v3.0.5 - k8s.io/api v0.30.1 - k8s.io/apiextensions-apiserver v0.30.1 - k8s.io/apimachinery v0.30.1 - k8s.io/client-go v0.30.1 - k8s.io/klog/v2 v2.120.1 + k8s.io/api v0.31.0 + k8s.io/apiextensions-apiserver v0.31.0 + k8s.io/apimachinery v0.31.0 + k8s.io/client-go v0.31.0 + k8s.io/klog/v2 v2.130.1 sigs.k8s.io/controller-runtime v0.12.3 ) require ( github.com/NYTimes/gziphandler v1.1.1 // indirect - github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.17.8 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.1 // 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.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -60,53 +60,53 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/gomega v1.32.0 // indirect + github.com/onsi/gomega v1.33.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect github.com/stoewer/go-strcase v1.3.0 // indirect - go.etcd.io/etcd/api/v3 v3.5.10 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect - go.etcd.io/etcd/client/v3 v3.5.10 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 // indirect - go.opentelemetry.io/otel v1.20.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect - go.opentelemetry.io/otel/metric v1.20.0 // indirect - go.opentelemetry.io/otel/sdk v1.20.0 // indirect - go.opentelemetry.io/otel/trace v1.20.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.etcd.io/etcd/api/v3 v3.5.14 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect + go.etcd.io/etcd/client/v3 v3.5.14 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.30.1 // indirect - k8s.io/component-base v0.30.1 // indirect + k8s.io/apiserver v0.31.0 // indirect + k8s.io/component-base v0.31.0 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/examples/kcp/go.sum b/examples/kcp/go.sum index 005b3b06b0..da86acf532 100644 --- a/examples/kcp/go.sum +++ b/examples/kcp/go.sum @@ -1,27 +1,31 @@ -cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -32,8 +36,11 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -45,20 +52,20 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto= -github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -71,8 +78,24 @@ github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2 github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= 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/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/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= @@ -112,17 +135,23 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA= github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -137,44 +166,48 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/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.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k= -go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= -go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0= -go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= -go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4= -go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= -go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao= -go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= -go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM= -go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= -go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA= -go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc= -go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg= -go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0/go.mod h1:HyABWq60Uy1kjJSa2BVOxUVao8Cdick5AWSKPutqy6U= -go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= -go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= -go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA= -go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM= -go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM= -go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= -go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= -go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0= +go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU= +go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ= +go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= +go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8= +go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg= +go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= +go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= +go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M= +go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0= +go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA= +go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw= +go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok= +go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -184,6 +217,8 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -196,11 +231,12 @@ golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-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= @@ -226,6 +262,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -250,14 +294,22 @@ k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24 k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= +k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= +k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= 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.31.0 h1:KchILPfB1ZE+ka7223mpU5zeFNkmb45jl7RHnlImUaI= +k8s.io/kms v0.31.0/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/go.mod b/go.mod index b7ebfbee80..f085a49adf 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module sigs.k8s.io/controller-runtime go 1.22.0 require ( - github.com/evanphx/json-patch v5.6.0+incompatible // Using v4 to match upstream + github.com/evanphx/json-patch v5.6.0+incompatible // indirect; Using v4 to match upstream github.com/evanphx/json-patch/v5 v5.9.0 github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 @@ -38,7 +38,8 @@ require ( ) require ( - github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect diff --git a/go.sum b/go.sum index 8f45cc4631..70348a5eaa 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -44,11 +46,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= @@ -61,17 +60,18 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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= @@ -128,7 +128,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -222,11 +222,11 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= diff --git a/pkg/kcp/kcp_test.go b/pkg/kcp/kcp_test.go index b2b8e3d13b..a4bfce85cf 100644 --- a/pkg/kcp/kcp_test.go +++ b/pkg/kcp/kcp_test.go @@ -14,6 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -47,6 +48,10 @@ var _ = Describe("NewClusterAwareClient", Ordered, func() { return } fallthrough + case "/api/v1/namespaces/default/pods/foo", "/clusters/root/api/v1/namespaces/default/pods/foo": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"default","resourceVersion":"184126176"}}`)) default: _, _ = w.Write([]byte(fmt.Sprintf("Not found %q", req.RequestURI))) w.WriteHeader(http.StatusNotFound) @@ -78,9 +83,13 @@ var _ = Describe("NewClusterAwareClient", Ordered, func() { err = cl.List(ctx, pods) Expect(err).NotTo(HaveOccurred()) + pod := &corev1.Pod{} + err = cl.Get(ctx, types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + Expect(err).NotTo(HaveOccurred()) + mu.Lock() defer mu.Unlock() - Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods"})) + Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods", "/api/v1/namespaces/default/pods/foo"})) }) It("should work with a cluster in the kontext", func(ctx context.Context) { @@ -91,9 +100,13 @@ var _ = Describe("NewClusterAwareClient", Ordered, func() { err = cl.List(kontext.WithCluster(ctx, "root"), pods) Expect(err).NotTo(HaveOccurred()) + pod := &corev1.Pod{} + err = cl.Get(kontext.WithCluster(ctx, "root"), types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + Expect(err).NotTo(HaveOccurred()) + mu.Lock() defer mu.Unlock() - Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods"})) + Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods", "/clusters/root/api/v1/namespaces/default/pods/foo"})) }) It("should work with a wildcard cluster in the kontext", func(ctx context.Context) { @@ -121,9 +134,15 @@ var _ = Describe("NewClusterAwareClient", Ordered, func() { err = cl.List(ctx, pods) Expect(err).NotTo(HaveOccurred()) + pod := &unstructured.Unstructured{} + pod.SetAPIVersion("v1") + pod.SetKind("Pod") + err = cl.Get(ctx, types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + Expect(err).NotTo(HaveOccurred()) + mu.Lock() defer mu.Unlock() - Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods"})) + Expect(paths).To(Equal([]string{"/api/v1", "/api/v1/pods", "/api/v1/namespaces/default/pods/foo"})) }) It("should work with a cluster in the kontext", func(ctx context.Context) { @@ -136,9 +155,15 @@ var _ = Describe("NewClusterAwareClient", Ordered, func() { err = cl.List(kontext.WithCluster(ctx, "root"), pods) Expect(err).NotTo(HaveOccurred()) + pod := &unstructured.Unstructured{} + pod.SetAPIVersion("v1") + pod.SetKind("Pod") + err = cl.Get(kontext.WithCluster(ctx, "root"), types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + Expect(err).NotTo(HaveOccurred()) + mu.Lock() defer mu.Unlock() - Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods"})) + Expect(paths).To(Equal([]string{"/clusters/root/api/v1", "/clusters/root/api/v1/pods", "/clusters/root/api/v1/namespaces/default/pods/foo"})) }) It("should work with a wildcard cluster in the kontext", func(ctx context.Context) { @@ -201,13 +226,18 @@ var _ = Describe("NewClusterAwareClient", Ordered, func() { var _ = Describe("NewClusterAwareCache", Ordered, func() { var ( - srv *httptest.Server - mu sync.Mutex - paths []string - cfg *rest.Config + cancelCtx context.CancelFunc + srv *httptest.Server + mu sync.Mutex + paths []string + cfg *rest.Config + c cache.Cache ) BeforeAll(func() { + var ctx context.Context + ctx, cancelCtx = context.WithCancel(context.Background()) + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { mu.Lock() pth := req.URL.Path @@ -225,11 +255,16 @@ var _ = Describe("NewClusterAwareCache", Ordered, func() { case req.URL.Path == "/clusters/*/api/v1/pods" && req.URL.Query().Get("watch") != "true": w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(`{"kind": "PodList","apiVersion": "v1","metadata": {"resourceVersion": "184126176"}, "items": [{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"default","resourceVersion":"184126176","annotations":{"kcp.io/cluster":"root"}}}]}`)) + _, _ = w.Write([]byte(`{"kind": "PodList","apiVersion": "v1","metadata": {"resourceVersion": "184126176"}, "items": [ + {"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"default","resourceVersion":"184126176","annotations":{"kcp.io/cluster":"root"}}}, + {"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"default","resourceVersion":"184126093","annotations":{"kcp.io/cluster":"ws"}}} + ]}`)) case req.URL.Path == "/clusters/*/api/v1/pods" && req.URL.Query().Get("watch") == "true": w.Header().Set("Content-Type", "application/json") w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"type":"ADDED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"bar","namespace":"default","resourceVersion":"184126177","annotations":{"kcp.io/cluster":"root"}}}}`)) + _, _ = w.Write([]byte(`{"type":"ADDED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"bar","namespace":"default","resourceVersion":"184126178","annotations":{"kcp.io/cluster":"ws"}}}}`)) if w, ok := w.(http.Flusher); ok { w.Flush() } @@ -239,11 +274,25 @@ var _ = Describe("NewClusterAwareCache", Ordered, func() { _, _ = w.Write([]byte(fmt.Sprintf("Not found %q", req.RequestURI))) } })) + go func() { + <-ctx.Done() + srv.Close() + }() cfg = &rest.Config{ Host: srv.URL, } Expect(rest.SetKubernetesDefaults(cfg)).To(Succeed()) + + var err error + c, err = NewClusterAwareCache(cfg, cache.Options{}) + Expect(err).NotTo(HaveOccurred()) + go func() { + if err := c.Start(ctx); err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + c.WaitForCacheSync(ctx) }) BeforeEach(func() { @@ -253,25 +302,38 @@ var _ = Describe("NewClusterAwareCache", Ordered, func() { }) AfterAll(func() { - srv.Close() + cancelCtx() }) It("should always access wildcard clusters and serve other clusters from memory", func(ctx context.Context) { - c, err := NewClusterAwareCache(cfg, cache.Options{}) - Expect(err).NotTo(HaveOccurred()) - go func() { - if err := c.Start(ctx); err != nil { - Expect(err).NotTo(HaveOccurred()) - } - }() - c.WaitForCacheSync(ctx) - pod := &corev1.Pod{} - err = c.Get(kontext.WithCluster(ctx, "root"), types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + err := c.Get(kontext.WithCluster(ctx, "root"), types.NamespacedName{Namespace: "default", Name: "foo"}, pod) Expect(err).NotTo(HaveOccurred()) mu.Lock() defer mu.Unlock() Expect(paths).To(Equal([]string{"/clusters/*/api/v1", "/clusters/*/api/v1/pods", "/clusters/*/api/v1/pods?watch=true"})) }) + + It("should return only the pods from the requested cluster", func(ctx context.Context) { + pod := &corev1.Pod{} + err := c.Get(kontext.WithCluster(ctx, "root"), types.NamespacedName{Namespace: "default", Name: "foo"}, pod) + Expect(err).NotTo(HaveOccurred()) + Expect(pod.Annotations).To(HaveKeyWithValue("kcp.io/cluster", "root")) + + pods := &corev1.PodList{} + err = c.List(kontext.WithCluster(ctx, "root"), pods) + Expect(err).NotTo(HaveOccurred()) + Expect(pods.Items).To(HaveLen(2)) + Expect(pods.Items[0].Annotations).To(HaveKeyWithValue("kcp.io/cluster", "root")) + Expect(pods.Items[1].Annotations).To(HaveKeyWithValue("kcp.io/cluster", "root")) + Expect(sets.New(pods.Items[0].Name, pods.Items[1].Name)).To(Equal(sets.New("foo", "bar"))) + }) + + It("should return all pods from all clusters without cluster in context", func(ctx context.Context) { + pods := &corev1.PodList{} + err := c.List(ctx, pods) + Expect(err).NotTo(HaveOccurred()) + Expect(pods.Items).To(HaveLen(4)) + }) }) From 73cc71a24802aa5876307acf8fabdada5eee1b09 Mon Sep 17 00:00:00 2001 From: tnthornton <2375126+tnthornton@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:33:52 -0700 Subject: [PATCH 8/9] UPSTREAM: : logicalcluster paramerter rename Signed-off-by: tnthornton <2375126+tnthornton@users.noreply.github.com> --- examples/kcp/go.mod | 48 +++++++-------- examples/kcp/go.sum | 110 +++++++++++++++++------------------ go.mod | 1 - go.sum | 4 +- pkg/handler/enqueue_owner.go | 2 +- 5 files changed, 79 insertions(+), 86 deletions(-) diff --git a/examples/kcp/go.mod b/examples/kcp/go.mod index e2d28b9015..b6259b7ea5 100644 --- a/examples/kcp/go.mod +++ b/examples/kcp/go.mod @@ -8,15 +8,15 @@ replace sigs.k8s.io/controller-runtime => ../../ require ( github.com/google/go-cmp v0.6.0 - github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34 + github.com/kcp-dev/apimachinery/v2 v2.0.0 github.com/kcp-dev/kcp/sdk v0.24.0 github.com/kcp-dev/logicalcluster/v3 v3.0.5 - k8s.io/api v0.31.0 - k8s.io/apiextensions-apiserver v0.31.0 - k8s.io/apimachinery v0.31.0 - k8s.io/client-go v0.31.0 + k8s.io/api v0.31.1 + k8s.io/apiextensions-apiserver v0.31.1 + k8s.io/apimachinery v0.31.1 + k8s.io/client-go v0.31.1 k8s.io/klog/v2 v2.130.1 - sigs.k8s.io/controller-runtime v0.12.3 + sigs.k8s.io/controller-runtime v0.19.0 ) require ( @@ -30,7 +30,7 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -38,9 +38,9 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -51,7 +51,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.16 // indirect 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 @@ -84,15 +84,15 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect + golang.org/x/time v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect @@ -102,10 +102,10 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.31.0 // indirect - k8s.io/component-base v0.31.0 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + k8s.io/apiserver v0.31.1 // indirect + k8s.io/component-base v0.31.1 // indirect + k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 // indirect + k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/examples/kcp/go.sum b/examples/kcp/go.sum index da86acf532..230db29ac3 100644 --- a/examples/kcp/go.sum +++ b/examples/kcp/go.sum @@ -17,15 +17,14 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -43,13 +42,12 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -74,8 +72,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= 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.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -90,8 +88,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= @@ -100,19 +98,16 @@ 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.0-alpha.0.0.20230926071920-57d168bcbe34 h1:tom0JX5OmAeOOmkGv8LaYHDtA1xAKDiQL5U0vhYYgdM= -github.com/kcp-dev/apimachinery/v2 v2.0.0-alpha.0.0.20230926071920-57d168bcbe34/go.mod h1:cWoaYGHl1nlzdEM2xvMzIASkEZJZLSf5nhe17M7wDhw= +github.com/kcp-dev/apimachinery/v2 v2.0.0 h1:hQuhBBh+AvUYYMRG+nDzo1VXxNCdMAE95wSD2uB7nxw= +github.com/kcp-dev/apimachinery/v2 v2.0.0/go.mod h1:cXCx7fku8/rYK23PNEBRLQ5ByoABoA+CZeJNC81TO0g= github.com/kcp-dev/kcp/sdk v0.24.0 h1:ZTfStDOQshVU2cnrqjgMo9xb0VNblkmrgMRtl0PCQEY= github.com/kcp-dev/kcp/sdk v0.24.0/go.mod h1:Pd2xxw/qhgfF2xgHolVwheq9VOJwPtNrBmxgBlYmjfk= 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/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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -217,45 +212,45 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= 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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= 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= @@ -285,29 +280,28 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= -k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= +k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= +k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= 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.31.0 h1:KchILPfB1ZE+ka7223mpU5zeFNkmb45jl7RHnlImUaI= -k8s.io/kms v0.31.0/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kms v0.31.1 h1:cGLyV3cIwb0ovpP/jtyIe2mEuQ/MkbhmeBF2IYCA9Io= +k8s.io/kms v0.31.1/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= +k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= +k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/go.mod b/go.mod index f085a49adf..3aaf5814fe 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module sigs.k8s.io/controller-runtime go 1.22.0 require ( - github.com/evanphx/json-patch v5.6.0+incompatible // indirect; Using v4 to match upstream github.com/evanphx/json-patch/v5 v5.9.0 github.com/fsnotify/fsnotify v1.7.0 github.com/go-logr/logr v1.4.2 diff --git a/go.sum b/go.sum index 70348a5eaa..f5e4537922 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= diff --git a/pkg/handler/enqueue_owner.go b/pkg/handler/enqueue_owner.go index 12b32d1546..bf0aa43d64 100644 --- a/pkg/handler/enqueue_owner.go +++ b/pkg/handler/enqueue_owner.go @@ -184,7 +184,7 @@ func (e *enqueueRequestForOwner[object]) getOwnerReconcileRequest(obj metav1.Obj if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group { // Match found - add a Request for the object referred to in the OwnerReference request := reconcile.Request{ - ClusterName: logicalcluster.From(object).String(), + ClusterName: logicalcluster.From(obj).String(), NamespacedName: types.NamespacedName{ Name: ref.Name, }, From 0f609a894f8ff4f2fc2a30f193f0d042fc4627f0 Mon Sep 17 00:00:00 2001 From: tnthornton <2375126+tnthornton@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:46:26 -0700 Subject: [PATCH 9/9] UPSTREAM: : remove replace for old prom imports Signed-off-by: tnthornton <2375126+tnthornton@users.noreply.github.com> --- examples/kcp/go.mod | 6 ------ examples/kcp/go.sum | 12 ++++-------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/examples/kcp/go.mod b/examples/kcp/go.mod index b6259b7ea5..27002ea5cd 100644 --- a/examples/kcp/go.mod +++ b/examples/kcp/go.mod @@ -56,7 +56,6 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -111,8 +110,3 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) - -replace ( - github.com/prometheus/client_golang => github.com/prometheus/client_golang v1.16.0 - github.com/prometheus/common => github.com/prometheus/common v0.44.0 -) diff --git a/examples/kcp/go.sum b/examples/kcp/go.sum index 230db29ac3..8a043a3435 100644 --- a/examples/kcp/go.sum +++ b/examples/kcp/go.sum @@ -57,7 +57,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -112,8 +111,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/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= @@ -130,12 +127,12 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -226,7 +223,6 @@ golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=