diff --git a/README.md b/README.md index 84154c71..9b611f6f 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,12 @@ The table below marks known support of a kcp version in kcp-operator versions. -| kcp | `main` | 0.1.x | -| ------ | ------------------ | ------------------ | -| `main` | :warning: | :question: | -| 0.30.x | :white_check_mark: | :question: | -| 0.29.x | :white_check_mark: | :question: | -| 0.28.x | :white_check_mark: | :question: | -| 0.27.x | :question: | :white_check_mark: | +| kcp | `main` | 0.7.x | 0.6.x | 0.5.x | +| ------ | ------------------ | ------------------ | ------------------ | ------------------ | +| `main` | :warning: | :question: | :question: | :question: | +| 0.31.x | :white_check_mark: | :white_check_mark: | :white_check_mark: | :question: | +| 0.30.x | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| 0.29.x | :question: | :question: | :question: | :white_check_mark: | [^1]: While we try to support kcp's `main` branch, this support is best effort and should not be used for deploying actual kcp instances. diff --git a/cmd/operator/main.go b/cmd/operator/main.go index 762bdacf..0f22fe9d 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -25,7 +25,7 @@ import ( certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" "github.com/go-logr/zapr" - kcpcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" "github.com/spf13/pflag" "go.uber.org/zap/zapcore" diff --git a/config/crd/bases/operator.kcp.io_frontproxies.yaml b/config/crd/bases/operator.kcp.io_frontproxies.yaml index 0e088ce9..54807a9a 100644 --- a/config/crd/bases/operator.kcp.io_frontproxies.yaml +++ b/config/crd/bases/operator.kcp.io_frontproxies.yaml @@ -112,8 +112,10 @@ spec: type: string clientSecret: description: |- - Optionally provide the client secret for the OIDC client. This is not used by kcp itself, but is used to generate - a OIDC kubeconfig that can be shared with users to log in via the OIDC provider. + ClientSecret is the OIDC client secret configured on the issuer side for this kcp instance. + This is not used by kcp itself, but is used to generate a OIDC kubeconfig that can be + shared with users to log in via the OIDC provider. + Deprecated: kube OIDC is secretless. type: string groupsClaim: description: 'Experimental: Optionally provides a custom claim diff --git a/config/crd/bases/operator.kcp.io_rootshards.yaml b/config/crd/bases/operator.kcp.io_rootshards.yaml index e1bec84e..d53d2f61 100644 --- a/config/crd/bases/operator.kcp.io_rootshards.yaml +++ b/config/crd/bases/operator.kcp.io_rootshards.yaml @@ -174,8 +174,10 @@ spec: type: string clientSecret: description: |- - Optionally provide the client secret for the OIDC client. This is not used by kcp itself, but is used to generate - a OIDC kubeconfig that can be shared with users to log in via the OIDC provider. + ClientSecret is the OIDC client secret configured on the issuer side for this kcp instance. + This is not used by kcp itself, but is used to generate a OIDC kubeconfig that can be + shared with users to log in via the OIDC provider. + Deprecated: kube OIDC is secretless. type: string groupsClaim: description: 'Experimental: Optionally provides a custom claim diff --git a/config/crd/bases/operator.kcp.io_shards.yaml b/config/crd/bases/operator.kcp.io_shards.yaml index 8c245c5c..42ddf6f7 100644 --- a/config/crd/bases/operator.kcp.io_shards.yaml +++ b/config/crd/bases/operator.kcp.io_shards.yaml @@ -174,8 +174,10 @@ spec: type: string clientSecret: description: |- - Optionally provide the client secret for the OIDC client. This is not used by kcp itself, but is used to generate - a OIDC kubeconfig that can be shared with users to log in via the OIDC provider. + ClientSecret is the OIDC client secret configured on the issuer side for this kcp instance. + This is not used by kcp itself, but is used to generate a OIDC kubeconfig that can be + shared with users to log in via the OIDC provider. + Deprecated: kube OIDC is secretless. type: string groupsClaim: description: 'Experimental: Optionally provides a custom claim diff --git a/docs/content/README.md b/docs/content/README.md index a9991359..fa4f20f2 100644 --- a/docs/content/README.md +++ b/docs/content/README.md @@ -22,12 +22,12 @@ The table below marks known support of a kcp version in kcp-operator versions. -| kcp | `main` | 0.1.x | -| ------ | ------------------ | ------------------ | -| `main` | :warning: | :question: | -| 0.29.x | :white_check_mark: | :question: | -| 0.28.x | :white_check_mark: | :question: | -| 0.27.x | :question: | :white_check_mark: | +| kcp | `main` | 0.7.x | 0.6.x | 0.5.x | +| ------ | ------------------ | ------------------ | ------------------ | ------------------ | +| `main` | :warning: | :question: | :question: | :question: | +| 0.31.x | :white_check_mark: | :white_check_mark: | :white_check_mark: | :question: | +| 0.30.x | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| 0.29.x | :question: | :question: | :question: | :white_check_mark: | *Explanation*: diff --git a/docs/content/contributing/continuous-integration/index.md b/docs/content/contributing/continuous-integration/index.md index 343e51fd..83987178 100644 --- a/docs/content/contributing/continuous-integration/index.md +++ b/docs/content/contributing/continuous-integration/index.md @@ -13,6 +13,6 @@ Here are the most important links: In order to run the E2E tests locally, you will need to setup cert-manager with the sample clusterissuer: ```sh -helm upgrade --install --namespace cert-manager --create-namespace --version v1.19.3 --set crds.enabled=true cert-manager jetstack/cert-manager +helm upgrade --install --namespace cert-manager --create-namespace --version v1.20.2 --set crds.enabled=true cert-manager jetstack/cert-manager kubectl apply -n cert-manager --filename hack/ci/testdata/clusterissuer.yaml ``` diff --git a/docs/content/contributing/local-setup.md b/docs/content/contributing/local-setup.md index 4c423886..da63b949 100644 --- a/docs/content/contributing/local-setup.md +++ b/docs/content/contributing/local-setup.md @@ -31,7 +31,7 @@ kind create cluster Install cert-manager, it is required to create kcp's PKI: ```sh -kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.3/cert-manager.yaml +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.yaml ``` Set up two etcd instances, one for the root shard and one for a supplementary shard: diff --git a/docs/content/contributing/releasing.md b/docs/content/contributing/releasing.md index ae08128b..827c5024 100644 --- a/docs/content/contributing/releasing.md +++ b/docs/content/contributing/releasing.md @@ -5,39 +5,52 @@ description: > # Release Process -The guide describes how to release a new version of the kcp-operator. +This document describes the end-to-end process for publishing a new kcp-operator release. ## Prerequisites -1. Have all desired changes merged and/or cherrypicked into the appropriate - release branch. +- Push access to the `kcp-dev/kcp-operator` repository. +- All desired changes merged (and cherry-picked for patch releases). -## Minor Release +## 1. Bump the Default KCP Image Tag -Minor releases (0.x) are tagged directly on the `main` branch and the `v0.X.0` -tag represents where the corresponding `release/v0.X` branch branches off. +Update the compiled-in default image tag in `internal/resources/resources.go`: -1. Checkout the desired `main` branch commit. -1. Tag the main module: `git tag -m "version 0.X" v0.X.0` -1. Tag the SDK module: `git tag -m "SDK version 0.X" sdk/v0.X.0` -1. Push the tags: `git push upstream v0.X.0 sdk/v0.X.0` -1. Create the release branch: `git checkout -B release-0.X` -1. Push the release branch: `git push -u upstream release-0.X` +```go +const ( + ImageRepository = "ghcr.io/kcp-dev/kcp" + ImageTag = "v0.XX.Y" // <-- update this +) +``` -Once the tag and branch have been pushed, the documentation has to be manually -updated to include the new minor release. +Commit and merge the change before tagging. -1. Navigate to https://github.com/kcp-dev/kcp-operator/actions/workflows/docs-gen-and-push.yaml -1. Run the workflow manually on the new release branch. +## 2. Tag the Release -Within a few minutes of the action finishing, the new branch should show up on -https://docs.kcp.io/kcp-operator/. +### Minor release (v0.X.0) -## Patch Releases +```bash +git checkout main +git tag -m "version 0.X" v0.X.0 +git tag -m "SDK version 0.X" sdk/v0.X.0 +git push upstream v0.X.0 sdk/v0.X.0 -Patch releases (v0.x.y) are tagged with in a release branch. +# Create the release branch +git checkout -B release-0.X +git push -u upstream release-0.X +``` -1. Checkout the desired `release/v0.X` branch commit. -1. Tag the main module: `git tag -m "version 0.X.Y" v0.X.Y` -1. Tag the SDK module: `git tag -m "SDK version 0.X.Y" sdk/v0.X.Y` -1. Push the tags: `git push upstream v0.X.Y sdk/v0.X.Y` +### Patch release (v0.X.Y) + +```bash +git checkout release-0.X +git tag -m "version 0.X.Y" v0.X.Y +git tag -m "SDK version 0.X.Y" sdk/v0.X.Y +git push upstream v0.X.Y sdk/v0.X.Y +``` + +## 3. Publish Documentation + +1. Go to the [docs-gen-and-push workflow](https://github.com/kcp-dev/kcp-operator/actions/workflows/docs-gen-and-push.yaml). +2. Run the workflow manually on the release branch. +3. Verify the new version appears on [docs.kcp.io/kcp-operator](https://docs.kcp.io/kcp-operator/). diff --git a/go.mod b/go.mod index 806c8900..97e03a44 100644 --- a/go.mod +++ b/go.mod @@ -1,31 +1,30 @@ module github.com/kcp-dev/kcp-operator -go 1.24.0 +go 1.25.0 replace github.com/kcp-dev/kcp-operator/sdk => ./sdk require ( - github.com/Masterminds/semver/v3 v3.2.1 - github.com/cert-manager/cert-manager v1.18.5 + github.com/Masterminds/semver/v3 v3.4.0 + github.com/cert-manager/cert-manager v1.20.2 github.com/go-logr/logr v1.4.3 github.com/go-logr/zapr v1.3.0 - github.com/go-test/deep v1.1.0 - github.com/kcp-dev/kcp-operator/sdk v0.0.0-00010101000000-000000000000 - github.com/kcp-dev/kcp/sdk v0.28.3 + github.com/go-test/deep v1.1.1 + github.com/kcp-dev/kcp-operator/sdk v0.7.0 github.com/kcp-dev/logicalcluster/v3 v3.0.5 - github.com/kcp-dev/sdk v0.30.0 - github.com/prometheus/client_golang v1.22.0 - github.com/spf13/pflag v1.0.6 + github.com/kcp-dev/sdk v0.31.0 + github.com/prometheus/client_golang v1.23.2 + github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 k8c.io/reconciler v0.5.0 - k8s.io/api v0.34.2 - k8s.io/apimachinery v0.34.2 - k8s.io/client-go v0.34.2 - k8s.io/component-base v0.34.2 - k8s.io/klog/v2 v2.130.1 - k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 - sigs.k8s.io/controller-runtime v0.19.0 + k8s.io/api v0.35.4 + k8s.io/apimachinery v0.35.4 + k8s.io/client-go v0.35.4 + k8s.io/component-base v0.35.4 + k8s.io/klog/v2 v2.140.0 + k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 + sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/yaml v1.6.0 ) @@ -38,72 +37,79 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/egymgmbh/go-prefix-writer v0.0.0-20180609083313-7326ea162eca // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/cel-go v0.26.0 // indirect - github.com/google/gnostic-models v0.7.0 // indirect + github.com/go-openapi/jsonpointer v0.23.0 // indirect + github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/swag v0.26.0 // indirect + github.com/go-openapi/swag/cmdutils v0.26.0 // indirect + github.com/go-openapi/swag/conv v0.26.0 // indirect + github.com/go-openapi/swag/fileutils v0.26.0 // indirect + github.com/go-openapi/swag/jsonname v0.26.0 // indirect + github.com/go-openapi/swag/jsonutils v0.26.0 // indirect + github.com/go-openapi/swag/loading v0.26.0 // indirect + github.com/go-openapi/swag/mangling v0.26.0 // indirect + github.com/go-openapi/swag/netutils v0.26.0 // indirect + github.com/go-openapi/swag/stringutils v0.26.0 // indirect + github.com/go-openapi/swag/typeutils v0.26.0 // indirect + github.com/go-openapi/swag/yamlutils v0.26.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.28.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // 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.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/gomega v1.36.2 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/onsi/gomega v1.39.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.20.1 // indirect + github.com/spf13/cobra v1.10.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 // indirect - go.opentelemetry.io/otel v1.41.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 // indirect - go.opentelemetry.io/otel/metric v1.41.0 // indirect - go.opentelemetry.io/otel/sdk v1.41.0 // indirect - go.opentelemetry.io/otel/trace v1.41.0 // indirect - go.opentelemetry.io/proto/otlp v1.9.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/goleak v1.3.1-0.20251210191316-2b7fd8a0d244 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/term v0.40.0 // indirect - golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.14.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/term v0.42.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/time v0.15.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect google.golang.org/grpc v1.80.0 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.34.2 // indirect - k8s.io/apiserver v0.34.2 // indirect - k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect - sigs.k8s.io/gateway-api v1.1.0 // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + k8s.io/apiextensions-apiserver v0.35.4 // indirect + k8s.io/apiserver v0.35.4 // indirect + k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect + sigs.k8s.io/gateway-api v1.5.1 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.4.0 // indirect ) diff --git a/go.sum b/go.sum index 0fd54fcb..a1851a6e 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -10,8 +10,8 @@ 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/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/cert-manager/cert-manager v1.18.5 h1:Gx4FSpSPYcSC4MQf43QjbxDfyTEbwZgfZQs5Lq9QlBs= -github.com/cert-manager/cert-manager v1.18.5/go.mod h1:HbPSO5MW/44wu19t84eY/K4c4/WwyPB4bA3uffOH92s= +github.com/cert-manager/cert-manager v1.20.2 h1:CimnY00nLqB2lmxhoSuEC4GDMFDK7JCXqyjwMM9ndIQ= +github.com/cert-manager/cert-manager v1.20.2/go.mod h1:1g/+a/WK5zWH/dXPZa3dMD3aJQJNRXQu+PN17C6WrOw= 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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -21,18 +21,18 @@ 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/egymgmbh/go-prefix-writer v0.0.0-20180609083313-7326ea162eca h1:7oodhZp9MZW0DBkrZXyUsJWKQFy35SVxjZ8K4vHXnk8= github.com/egymgmbh/go-prefix-writer v0.0.0-20180609083313-7326ea162eca/go.mod h1:UhMFM+dnOcm1f0Pve8uqRaxAhEYki+/CuA2BTDp2T04= -github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= -github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.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/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= 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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= +github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -40,49 +40,71 @@ 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.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-openapi/jsonpointer v0.23.0 h1:c25HFTJ6uWGmoe5BQI6p72p4o7KnlWYsy1MeFlAumsw= +github.com/go-openapi/jsonpointer v0.23.0/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI= +github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0= +github.com/go-openapi/swag/cmdutils v0.26.0 h1:iowihOcvq7y4egO8cOq0dmfohz6wfeQ63U1EnuhO2TU= +github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= +github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= +github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= +github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= +github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= +github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= +github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= +github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= +github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko= +github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg= +github.com/go-openapi/swag/mangling v0.26.0 h1:Du2YC4YLA/Y5m/YKQd7AnY5qq0wRKSFZTTt8ktFaXcQ= +github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0= +github.com/go-openapi/swag/netutils v0.26.0 h1:CmZp+ZT7HrmFwrC3GdGsXBq2+42T1bjKBapcqVpIs3c= +github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo= +github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= +github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= +github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= +github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= +github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ= +github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= +github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= +github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= 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/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 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.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= -github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc= +github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.1-0.20210504230335-f78f29fc09ea h1:VcIYpAGBae3Z6BVncE0OnTE/ZjlDXqtYhOZky88neLM= github.com/google/gofuzz v1.2.1-0.20210504230335-f78f29fc09ea/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= +github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= 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.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= 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= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kcp-dev/kcp/sdk v0.28.3 h1:TS2nJOVBjenBd3fz1+y3aNrqZWqmakalNAIcQM9SukQ= -github.com/kcp-dev/kcp/sdk v0.28.3/go.mod h1:8oZpWxkoMu2TDpx5DgdIGDigByKHKkeqVMA4GiWneoI= github.com/kcp-dev/logicalcluster/v3 v3.0.5 h1:JbYakokb+5Uinz09oTXomSUJVQsqfxEvU4RyHUYxHOU= github.com/kcp-dev/logicalcluster/v3 v3.0.5/go.mod h1:EWBUBxdr49fUB1cLMO4nOdBWmYifLbP1LfoL20KkXYY= -github.com/kcp-dev/sdk v0.30.0 h1:BdDiKJ7SeVfzLIxueQwbADTrH7bfZ7b5ACYSrx6P93Y= -github.com/kcp-dev/sdk v0.30.0/go.mod h1:H3PkpM33QqwPMgGOOw3dfqbQ8dF2gu4NeIsufSlS5KE= -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/kcp-dev/sdk v0.31.0 h1:fLqgY6MHVmmBcttqR/V3h2Dnv7xmab84FTKTwV08+Bo= +github.com/kcp-dev/sdk v0.31.0/go.mod h1:GpNW9wPULY9+UuLVAaB5z/sJ8r0Jo/l0Jgy4BKnIJTE= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -91,8 +113,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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= 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= @@ -101,128 +121,97 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM= -github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc= +github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= +github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= 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/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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 h1:PnV4kVnw0zOmwwFkAzCN5O07fw1YOIQor120zrh0AVo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0/go.mod h1:ofAwF4uinaf8SXdVzzbL4OsxJ3VfeEg3f/F6CeF49/Y= -go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= -go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0= -go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= -go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= -go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= -go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= -go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= -go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= -go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= -go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= -go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= -go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/goleak v1.3.1-0.20251210191316-2b7fd8a0d244 h1:OdZ8e4E9yDUGiis9x2ta/Ec5yhMAKT6ZivRvakyxC7E= go.uber.org/goleak v1.3.1-0.20251210191316-2b7fd8a0d244/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec= +google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= @@ -230,44 +219,43 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.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.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= k8c.io/reconciler v0.5.0 h1:BHpelg1UfI/7oBFctqOq8sX6qzflXpl3SlvHe7e8wak= k8c.io/reconciler v0.5.0/go.mod h1:pT1+SVcVXJQeBJhpJBXQ5XW64QnKKeYTnVlQf0dGE0k= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= -k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= -k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE= -k8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= -k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= -k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= -k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= -sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM= -sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988= +k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU= +k8s.io/apiextensions-apiserver v0.35.4 h1:HeP+Upp7ItdvnyGmub0yoix+2z5+ev4M5cE5TCgtOUU= +k8s.io/apiextensions-apiserver v0.35.4/go.mod h1:ogQlk+stIE8mnoRthSYCwlOS12fVqgWFiErMwPaXA7c= +k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds= +k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= +k8s.io/apiserver v0.35.4 h1:vtuFqNFmF9bPRdHDL2lpK6qCTPWDreZJL4LRPwVM6ho= +k8s.io/apiserver v0.35.4/go.mod h1:JnBcb+J8kFXKpZkgcbcUnPBBHi4qgBii1I7dLxFY/oo= +k8s.io/client-go v0.35.4 h1:DN6fyaGuzK64UvnKO5fOA6ymSjvfGAnCAHAR0C66kD8= +k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY= +k8s.io/component-base v0.35.4 h1:6n1tNJ87johN0Hif0Fs8K2GMthsaUwMqCebUDLYyv7U= +k8s.io/component-base v0.35.4/go.mod h1:qaDJgz5c1KYKla9occFmlJEfPpkuA55s90G509R+PeY= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f h1:4Qiq0YAoQATdgmHALJWz9rJ4fj20pB3xebpB4CFNhYM= +k8s.io/kube-openapi v0.0.0-20260414162039-ec9c827d403f/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= +k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM= +k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= +sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= +sigs.k8s.io/gateway-api v1.5.1 h1:RqVRIlkhLhUO8wOHKTLnTJA6o/1un4po4/6M1nRzdd0= +sigs.k8s.io/gateway-api v1.5.1/go.mod h1:GvCETiaMAlLym5CovLxGjS0NysqFk3+Yuq3/rh6QL2o= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.4.0 h1:qmp2e3ZfFi1/jJbDGpD4mt3wyp6PE1NfKHCYLqgNQJo= +sigs.k8s.io/structured-merge-diff/v6 v6.4.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/hack/ci/run-e2e-tests.sh b/hack/ci/run-e2e-tests.sh index b2662086..b3203465 100755 --- a/hack/ci/run-e2e-tests.sh +++ b/hack/ci/run-e2e-tests.sh @@ -134,7 +134,7 @@ echo "Deploying cert-manager..." --install \ --namespace cert-manager \ --create-namespace \ - --version v1.19.3 \ + --version v1.20.2 \ --set crds.enabled=true \ cert-manager jetstack/cert-manager diff --git a/hack/run-e2e-tests.sh b/hack/run-e2e-tests.sh index 1d427f48..03ce138d 100755 --- a/hack/run-e2e-tests.sh +++ b/hack/run-e2e-tests.sh @@ -139,7 +139,7 @@ echo "Deploying cert-manager..." --install \ --namespace cert-manager \ --create-namespace \ - --version v1.19.3 \ + --version v1.20.2 \ --set crds.enabled=true \ --atomic \ cert-manager jetstack/cert-manager diff --git a/internal/client/clients.go b/internal/client/clients.go index 41a6421b..67a447f6 100644 --- a/internal/client/clients.go +++ b/internal/client/clients.go @@ -28,56 +28,57 @@ import ( "k8s.io/client-go/rest" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) // NewRootShardClient returns a new client for talking to the kcp root shard service directly. -func NewRootShardClient(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { - baseUrl := fmt.Sprintf("https://%s.%s.svc.cluster.local:6443", resources.GetRootShardServiceName(rootShard), rootShard.Namespace) +func NewRootShardClient(ctx context.Context, names naming.Scheme, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { + baseUrl := fmt.Sprintf("https://%s:6443", names.RootShardBaseHost(rootShard)) if !cluster.Empty() { baseUrl = fmt.Sprintf("%s/clusters/%s", baseUrl, cluster.String()) } - return newClient(ctx, c, baseUrl, scheme, rootShard) + return newClient(ctx, names, c, baseUrl, scheme, rootShard) } // NewRootShardClient returns a new client that connects to the operator's internal front-proxy. -func NewRootShardProxyClient(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { - baseUrl := fmt.Sprintf("https://%s.%s.svc.cluster.local:6443", resources.GetRootShardProxyServiceName(rootShard), rootShard.Namespace) +func NewRootShardProxyClient(ctx context.Context, names naming.Scheme, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { + baseUrl := fmt.Sprintf("https://%s:6443", names.RootShardProxyBaseHost(rootShard)) if !cluster.Empty() { baseUrl = fmt.Sprintf("%s/clusters/%s", baseUrl, cluster.String()) } - return newClient(ctx, c, baseUrl, scheme, rootShard) + return newClient(ctx, names, c, baseUrl, scheme, rootShard) } // NewShardClient returns a new client for talking to a kcp shard service directly. -func NewShardClient(ctx context.Context, c ctrlruntimeclient.Client, shard *operatorv1alpha1.Shard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { +func NewShardClient(ctx context.Context, names naming.Scheme, c ctrlruntimeclient.Client, shard *operatorv1alpha1.Shard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { rootShard, err := getRootShardForShard(ctx, c, shard) if err != nil { return nil, fmt.Errorf("failed to determine effective RootShard: %w", err) } - baseUrl := fmt.Sprintf("https://%s.%s.svc.cluster.local:6443", resources.GetShardServiceName(shard), shard.Namespace) + baseUrl := fmt.Sprintf("https://%s:6443", names.ShardBaseHost(shard)) if !cluster.Empty() { baseUrl = fmt.Sprintf("%s/clusters/%s", baseUrl, cluster.String()) } - return newClient(ctx, c, baseUrl, scheme, rootShard) + return newClient(ctx, names, c, baseUrl, scheme, rootShard) } func newClient( ctx context.Context, + names naming.Scheme, c ctrlruntimeclient.Client, url string, scheme *runtime.Scheme, rootShard *operatorv1alpha1.RootShard, ) (ctrlruntimeclient.Client, error) { - tlsConfig, err := getTLSConfig(ctx, c, rootShard) + tlsConfig, err := getTLSConfig(ctx, names, c, rootShard) if err != nil { return nil, fmt.Errorf("failed to determine TLS settings: %w", err) } @@ -93,11 +94,11 @@ func newClient( // +kubebuilder:rbac:groups=core,resources=secrets,verbs=get // getTLSConfig returns the CA and serving certificate for a RootShard. -func getTLSConfig(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard) (rest.TLSClientConfig, error) { +func getTLSConfig(ctx context.Context, names naming.Scheme, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard) (rest.TLSClientConfig, error) { // get the secret for the kcp-operator client cert key := types.NamespacedName{ Namespace: rootShard.Namespace, - Name: resources.GetRootShardCertificateName(rootShard, operatorv1alpha1.OperatorCertificate), + Name: names.RootShardCertificateName(rootShard, operatorv1alpha1.OperatorCertificate), } certSecret := &corev1.Secret{} diff --git a/internal/client/frontproxy.go b/internal/client/frontproxy.go index a708c601..1b2793bf 100644 --- a/internal/client/frontproxy.go +++ b/internal/client/frontproxy.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/types" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -39,6 +40,7 @@ import ( // does not drop groups/permissions). func NewInternalKubeconfigClient(ctx context.Context, c ctrlruntimeclient.Client, kubeconfig *operatorv1alpha1.Kubeconfig, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) { target := kubeconfig.Spec.Target + namingScheme := naming.NewVersion1() switch { case target.RootShardRef != nil: @@ -47,7 +49,7 @@ func NewInternalKubeconfigClient(ctx context.Context, c ctrlruntimeclient.Client return nil, fmt.Errorf("failed to get RootShard: %w", err) } - return NewRootShardClient(ctx, c, rootShard, cluster, scheme) + return NewRootShardClient(ctx, namingScheme, c, rootShard, cluster, scheme) case target.ShardRef != nil: shard := &operatorv1alpha1.Shard{} @@ -55,7 +57,7 @@ func NewInternalKubeconfigClient(ctx context.Context, c ctrlruntimeclient.Client return nil, fmt.Errorf("failed to get Shard: %w", err) } - return NewShardClient(ctx, c, shard, cluster, scheme) + return NewShardClient(ctx, namingScheme, c, shard, cluster, scheme) case target.FrontProxyRef != nil: frontProxy := &operatorv1alpha1.FrontProxy{} @@ -68,7 +70,7 @@ func NewInternalKubeconfigClient(ctx context.Context, c ctrlruntimeclient.Client return nil, fmt.Errorf("failed to get RootShard: %w", err) } - return NewRootShardProxyClient(ctx, c, rootShard, cluster, scheme) + return NewRootShardProxyClient(ctx, namingScheme, c, rootShard, cluster, scheme) default: return nil, errors.New("no valid target configured in Kubeconfig: neither rootShard, shard nor frontProxy ref set") diff --git a/internal/controller/bundle/controller.go b/internal/controller/bundle/controller.go index fae2f6e4..edb913bf 100644 --- a/internal/controller/bundle/controller.go +++ b/internal/controller/bundle/controller.go @@ -42,6 +42,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -159,6 +160,7 @@ func (r *BundleReconciler) reconcile(ctx context.Context, bundle *operatorv1alph // Determine which target the bundle is for and get the list of required objects target := bundle.Spec.Target + names := naming.NewVersion1() var ( targetExists bool targetName string @@ -178,7 +180,7 @@ func (r *BundleReconciler) reconcile(ctx context.Context, bundle *operatorv1alph }, rootShard) if err == nil { targetExists = true - requiredObjects = getBundleObjectsForRootShard(rootShard) + requiredObjects = getBundleObjectsForRootShard(rootShard, names) } else if ctrlruntimeclient.IgnoreNotFound(err) != nil { errs = append(errs, fmt.Errorf("failed to get RootShard %s: %w", targetName, err)) } @@ -200,7 +202,7 @@ func (r *BundleReconciler) reconcile(ctx context.Context, bundle *operatorv1alph Namespace: bundle.Namespace, }, rootShard) if err == nil { - requiredObjects = getBundleObjectsForShard(shard, rootShard.Name) + requiredObjects = getBundleObjectsForShard(shard, rootShard.Name, names) } else { errs = append(errs, fmt.Errorf("failed to get RootShard for Shard %s: %w", targetName, err)) } @@ -226,7 +228,7 @@ func (r *BundleReconciler) reconcile(ctx context.Context, bundle *operatorv1alph Namespace: bundle.Namespace, }, rootShard) if err == nil { - requiredObjects = getBundleObjectsForFrontProxy(frontProxy, rootShard.Name) + requiredObjects = getBundleObjectsForFrontProxy(frontProxy, rootShard, names) } else { errs = append(errs, fmt.Errorf("failed to get RootShard for FrontProxy %s: %w", targetName, err)) } diff --git a/internal/controller/bundle/helpers.go b/internal/controller/bundle/helpers.go index 0e2f19ac..b6424d55 100644 --- a/internal/controller/bundle/helpers.go +++ b/internal/controller/bundle/helpers.go @@ -29,19 +29,20 @@ import ( ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) // EnsureBundleForOwner ensures that a Bundle object exists for the given owner if it has the bundle annotation. // If the annotation is present and no Bundle exists, it creates one owned by the object. // Returns the Bundle object if it exists/was created, or nil if no bundle annotation is present. -func EnsureBundleForOwner(ctx context.Context, client ctrlruntimeclient.Client, scheme *runtime.Scheme, owner ctrlruntimeclient.Object) (*operatorv1alpha1.Bundle, error) { +func EnsureBundleForOwner(ctx context.Context, client ctrlruntimeclient.Client, scheme *runtime.Scheme, owner ctrlruntimeclient.Object, names naming.Scheme) (*operatorv1alpha1.Bundle, error) { annotations := owner.GetAnnotations() if annotations == nil || annotations[resources.BundleAnnotation] == "" { return nil, nil } - bundleName := resources.GetBundleName(owner.GetName()) + bundleName := names.BundleName(owner.GetName()) bundle := &operatorv1alpha1.Bundle{} err := client.Get(ctx, types.NamespacedName{ @@ -100,7 +101,7 @@ func buildBundleTarget(owner ctrlruntimeclient.Object) operatorv1alpha1.BundleTa // GetBundleReadyCondition checks if the Bundle associated with the owner is ready. // Returns a condition that can be added to the owner's status. -func GetBundleReadyCondition(ctx context.Context, client ctrlruntimeclient.Client, owner ctrlruntimeclient.Object, generation int64) metav1.Condition { +func GetBundleReadyCondition(ctx context.Context, client ctrlruntimeclient.Client, owner ctrlruntimeclient.Object, generation int64, names naming.Scheme) metav1.Condition { annotations := owner.GetAnnotations() if annotations == nil || annotations[resources.BundleAnnotation] == "" { // No bundle annotation, return ready condition @@ -113,7 +114,7 @@ func GetBundleReadyCondition(ctx context.Context, client ctrlruntimeclient.Clien } } - bundleName := resources.GetBundleName(owner.GetName()) + bundleName := names.BundleName(owner.GetName()) bundle := &operatorv1alpha1.Bundle{} err := client.Get(ctx, types.NamespacedName{ diff --git a/internal/controller/bundle/objects.go b/internal/controller/bundle/objects.go index 3f086848..56e6aa3a 100644 --- a/internal/controller/bundle/objects.go +++ b/internal/controller/bundle/objects.go @@ -17,11 +17,9 @@ limitations under the License. package bundle import ( - "fmt" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - + "github.com/kcp-dev/kcp-operator/internal/resources/bundling" + "github.com/kcp-dev/kcp-operator/internal/resources/frontproxy" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -33,186 +31,100 @@ type BundleObjectSpec struct { Objects []operatorv1alpha1.BundleObject } -var ( - // secretGVR is the GVR for Secret resources - secretGVR = schema.GroupVersionResource{ - Group: corev1.GroupName, - Version: "v1", - Resource: "secrets", - } - - // serviceGVR is the GVR for Service resources - serviceGVR = schema.GroupVersionResource{ - Group: corev1.GroupName, - Version: "v1", - Resource: "services", - } - - // configMapGVR is the GVR for ConfigMap resources - configMapGVR = schema.GroupVersionResource{ - Group: corev1.GroupName, - Version: "v1", - Resource: "configmaps", - } - - // deploymentGVR is the GVR for Deployment resources - deploymentGVR = schema.GroupVersionResource{ - Group: "apps", - Version: "v1", - Resource: "deployments", - } -) - // getBundleObjectsForShard returns the list of objects required for a Shard bundle -func getBundleObjectsForShard(shard *operatorv1alpha1.Shard, rootShardName string) []operatorv1alpha1.BundleObject { - shardName := shard.Name +func getBundleObjectsForShard(shard *operatorv1alpha1.Shard, rootShardName string, names naming.Scheme) []operatorv1alpha1.BundleObject { namespace := shard.Namespace + rootShard := &operatorv1alpha1.RootShard{} + rootShard.Name = rootShardName + rootShard.Namespace = namespace + objects := []operatorv1alpha1.BundleObject{ // CA certificates from RootShard (shared) - {GVR: secretGVR, Name: fmt.Sprintf("%s-front-proxy-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-requestheader-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-server-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-service-account-ca", rootShardName), Namespace: namespace}, + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.RootCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.RequestHeaderClientCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.ServiceAccountCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA), namespace), // Shard-specific certificates and secrets - {GVR: secretGVR, Name: fmt.Sprintf("%s-logical-cluster-admin", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-logical-cluster-admin-kubeconfig", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-client-kubeconfig", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-external-logical-cluster-admin", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-external-logical-cluster-admin-kubeconfig", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-virtual-workspaces", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-client", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-server", shardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-service-account", shardName), Namespace: namespace}, + bundling.NewSecret(names.ShardCertificateName(shard, operatorv1alpha1.LogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.ShardKubeconfigSecret(shard, operatorv1alpha1.LogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.ShardCertificateName(shard, operatorv1alpha1.ClientCertificate), namespace), + bundling.NewSecret(names.ShardKubeconfigSecret(shard, operatorv1alpha1.ClientCertificate), namespace), + bundling.NewSecret(names.ShardCertificateName(shard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.ShardKubeconfigSecret(shard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.ShardCertificateName(shard, operatorv1alpha1.ServerCertificate), namespace), + bundling.NewSecret(names.ShardCertificateName(shard, operatorv1alpha1.VirtualWorkspacesCertificate), namespace), + bundling.NewSecret(names.ShardCertificateName(shard, operatorv1alpha1.ServiceAccountCertificate), namespace), } // Add merged CA bundle only if CABundleSecretRef is configured if shard.Spec.CABundleSecretRef != nil { - objects = append(objects, operatorv1alpha1.BundleObject{ - GVR: secretGVR, - Name: fmt.Sprintf("%s-merged-ca-bundle", shardName), - Namespace: namespace, - }) + objects = append(objects, bundling.NewSecret(names.MergedCABundleName(shard.Name), namespace)) } // Deployment - objects = append(objects, operatorv1alpha1.BundleObject{ - GVR: deploymentGVR, - Name: fmt.Sprintf("%s-shard-kcp", shardName), - Namespace: namespace, - }) + objects = append(objects, bundling.NewDeployment(names.ShardDeploymentName(shard), namespace)) // Service - objects = append(objects, operatorv1alpha1.BundleObject{ - GVR: serviceGVR, - Name: fmt.Sprintf("%s-shard-kcp", shardName), - Namespace: namespace, - }) + objects = append(objects, bundling.NewService(names.ShardServiceName(shard), namespace)) return objects } // getBundleObjectsForRootShard returns the list of objects required for a RootShard bundle // TODO(mjudeikis): These are not yet tested. Need to double check if its full list. -func getBundleObjectsForRootShard(rootShard *operatorv1alpha1.RootShard) []operatorv1alpha1.BundleObject { - rootShardName := rootShard.Name +func getBundleObjectsForRootShard(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) []operatorv1alpha1.BundleObject { namespace := rootShard.Namespace objects := []operatorv1alpha1.BundleObject{ // Root CA and intermediate CAs - {GVR: secretGVR, Name: fmt.Sprintf("%s-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-server-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-requestheader-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-service-account-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-front-proxy-client-ca", rootShardName), Namespace: namespace}, + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.RootCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.RequestHeaderClientCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.ServiceAccountCA), namespace), + bundling.NewSecret(names.RootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA), namespace), // RootShard certificates - {GVR: secretGVR, Name: fmt.Sprintf("%s-server", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-service-account", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-virtual-workspaces", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-logical-cluster-admin", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-external-logical-cluster-admin", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-operator-client", rootShardName), Namespace: namespace}, + bundling.NewSecret(names.RootShardCertificateName(rootShard, operatorv1alpha1.ServerCertificate), namespace), + bundling.NewSecret(names.RootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), namespace), + bundling.NewSecret(names.RootShardCertificateName(rootShard, operatorv1alpha1.VirtualWorkspacesCertificate), namespace), + bundling.NewSecret(names.RootShardCertificateName(rootShard, operatorv1alpha1.LogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.RootShardCertificateName(rootShard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.RootShardCertificateName(rootShard, operatorv1alpha1.OperatorCertificate), namespace), // Kubeconfig secrets - {GVR: secretGVR, Name: fmt.Sprintf("%s-logical-cluster-admin-kubeconfig", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-external-logical-cluster-admin-kubeconfig", rootShardName), Namespace: namespace}, + bundling.NewSecret(names.RootShardKubeconfigSecret(rootShard, operatorv1alpha1.LogicalClusterAdminCertificate), namespace), + bundling.NewSecret(names.RootShardKubeconfigSecret(rootShard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), namespace), // Service - {GVR: serviceGVR, Name: fmt.Sprintf("%s-kcp", rootShardName), Namespace: namespace}, + bundling.NewService(names.RootShardServiceName(rootShard), namespace), } // Add merged CA bundle if configured if rootShard.Spec.CABundleSecretRef != nil { - objects = append(objects, operatorv1alpha1.BundleObject{ - GVR: secretGVR, - Name: fmt.Sprintf("%s-merged-ca-bundle", rootShardName), - Namespace: namespace, - }) + objects = append(objects, bundling.NewSecret(names.MergedCABundleName(rootShard.Name), namespace)) } // Add proxy-related objects objects = append(objects, []operatorv1alpha1.BundleObject{ - {GVR: secretGVR, Name: fmt.Sprintf("%s-proxy-dynamic-kubeconfig", rootShardName), Namespace: namespace}, - {GVR: configMapGVR, Name: fmt.Sprintf("%s-proxy-config", rootShardName), Namespace: namespace}, - {GVR: serviceGVR, Name: fmt.Sprintf("%s-proxy", rootShardName), Namespace: namespace}, - {GVR: deploymentGVR, Name: fmt.Sprintf("%s-proxy", rootShardName), Namespace: namespace}, + bundling.NewSecret(names.RootShardProxyDynamicKubeconfigName(rootShard), namespace), + bundling.NewConfigMap(names.RootShardProxyConfigName(rootShard), namespace), + bundling.NewService(names.RootShardProxyServiceName(rootShard), namespace), + bundling.NewDeployment(names.RootShardProxyDeploymentName(rootShard), namespace), }...) // Add rootshard deployment - objects = append(objects, operatorv1alpha1.BundleObject{ - GVR: deploymentGVR, - Name: fmt.Sprintf("%s-kcp", rootShardName), - Namespace: namespace, - }) + objects = append(objects, bundling.NewDeployment(names.RootShardDeploymentName(rootShard), namespace)) return objects } // getBundleObjectsForFrontProxy returns the list of objects required for a FrontProxy bundle // TODO(mjudeikis): These are not yet tested. Need to double check if its full list. -func getBundleObjectsForFrontProxy(frontProxy *operatorv1alpha1.FrontProxy, rootShardName string) []operatorv1alpha1.BundleObject { - frontProxyName := frontProxy.Name - namespace := frontProxy.Namespace - - return []operatorv1alpha1.BundleObject{ - // CA certificates from RootShard (shared) - {GVR: secretGVR, Name: fmt.Sprintf("%s-front-proxy-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-requestheader-client-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-server-ca", rootShardName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-ca", rootShardName), Namespace: namespace}, - - // FrontProxy-specific certificates - {GVR: secretGVR, Name: fmt.Sprintf("%s-%s-server", rootShardName, frontProxyName), Namespace: namespace}, - {GVR: secretGVR, Name: fmt.Sprintf("%s-%s-client", rootShardName, frontProxyName), Namespace: namespace}, - - // FrontProxy configuration and service - {GVR: secretGVR, Name: fmt.Sprintf("%s-%s-dynamic-kubeconfig", rootShardName, frontProxyName), Namespace: namespace}, - {GVR: configMapGVR, Name: fmt.Sprintf("%s-config", frontProxyName), Namespace: namespace}, - {GVR: serviceGVR, Name: fmt.Sprintf("%s-front-proxy", frontProxyName), Namespace: namespace}, - - // FrontProxy deployment - {GVR: deploymentGVR, Name: fmt.Sprintf("%s-front-proxy", frontProxyName), Namespace: namespace}, - } -} - -// GetBundleObjectsForTarget returns the list of objects required for a Bundle based on the target -func GetBundleObjectsForTarget(target operatorv1alpha1.BundleTarget, namespace string, shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, frontProxy *operatorv1alpha1.FrontProxy) []operatorv1alpha1.BundleObject { - switch { - case target.RootShardRef != nil && rootShard != nil: - return getBundleObjectsForRootShard(rootShard) - - case target.ShardRef != nil && shard != nil && rootShard != nil: - return getBundleObjectsForShard(shard, rootShard.Name) - - case target.FrontProxyRef != nil && frontProxy != nil && rootShard != nil: - return getBundleObjectsForFrontProxy(frontProxy, rootShard.Name) - - default: - return []operatorv1alpha1.BundleObject{} - } +func getBundleObjectsForFrontProxy(frontProxy *operatorv1alpha1.FrontProxy, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) []operatorv1alpha1.BundleObject { + return frontproxy.NewFrontProxy(frontProxy, rootShard, names).Bundle() } diff --git a/internal/controller/cacheserver/controller.go b/internal/controller/cacheserver/controller.go index 7599dd37..c9d8f30e 100644 --- a/internal/controller/cacheserver/controller.go +++ b/internal/controller/cacheserver/controller.go @@ -34,6 +34,7 @@ import ( "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/reconciling/modifier" "github.com/kcp-dev/kcp-operator/internal/resources/cacheserver" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -84,25 +85,27 @@ func (r *CacheServerReconciler) Reconcile(ctx context.Context, req ctrlruntime.R } func (r *CacheServerReconciler) reconcile(ctx context.Context, server *operatorv1alpha1.CacheServer) error { + namingScheme := naming.NewVersion1() + ownerRefWrapper := k8creconciling.OwnerRefWrapper(*metav1.NewControllerRef(server, operatorv1alpha1.SchemeGroupVersion.WithKind("CacheServer"))) revisionLabels := modifier.RelatedRevisionsLabels(ctx, r.Client) if err := reconciling.ReconcileCertificates(ctx, []reconciling.NamedCertificateReconcilerFactory{ - cacheserver.RootCACertificateReconciler(server), - cacheserver.ServerCertificateReconciler(server), - cacheserver.ClientCertificateReconciler(server), + cacheserver.RootCACertificateReconciler(server, namingScheme), + cacheserver.ServerCertificateReconciler(server, namingScheme), + cacheserver.ClientCertificateReconciler(server, namingScheme), }, server.Namespace, r.Client, ownerRefWrapper); err != nil { return err } if err := reconciling.ReconcileIssuers(ctx, []reconciling.NamedIssuerReconcilerFactory{ - cacheserver.RootCAIssuerReconciler(server), + cacheserver.RootCAIssuerReconciler(server, namingScheme), }, server.Namespace, r.Client, ownerRefWrapper); err != nil { return err } if err := k8creconciling.ReconcileSecrets(ctx, []k8creconciling.NamedSecretReconcilerFactory{ - cacheserver.KubeconfigReconciler(server), + cacheserver.KubeconfigReconciler(server, namingScheme), }, server.Namespace, r.Client, ownerRefWrapper); err != nil { return err } @@ -111,7 +114,7 @@ func (r *CacheServerReconciler) reconcile(ctx context.Context, server *operatorv // requeueing to eventually get there in the end. Importantly, reconciling Deployments has to happen // after all Secrets have been reconciled. if err := k8creconciling.ReconcileDeployments(ctx, []k8creconciling.NamedDeploymentReconcilerFactory{ - cacheserver.DeploymentReconciler(server), + cacheserver.DeploymentReconciler(server, namingScheme), }, server.Namespace, r.Client, ownerRefWrapper, revisionLabels); err != nil { // Swallow these errors and instead rely on us watching Secrets and re-reconciling whenever they change. if errors.Is(err, modifier.ErrMountNotFound) { @@ -121,7 +124,7 @@ func (r *CacheServerReconciler) reconcile(ctx context.Context, server *operatorv } if err := k8creconciling.ReconcileServices(ctx, []k8creconciling.NamedServiceReconcilerFactory{ - cacheserver.ServiceReconciler(server), + cacheserver.ServiceReconciler(server, namingScheme), }, server.Namespace, r.Client, ownerRefWrapper); err != nil { return err } diff --git a/internal/controller/frontproxy/controller.go b/internal/controller/frontproxy/controller.go index 9ccb21bf..7484f3d0 100644 --- a/internal/controller/frontproxy/controller.go +++ b/internal/controller/frontproxy/controller.go @@ -41,8 +41,8 @@ import ( bundlehelper "github.com/kcp-dev/kcp-operator/internal/controller/bundle" "github.com/kcp-dev/kcp-operator/internal/controller/util" "github.com/kcp-dev/kcp-operator/internal/metrics" - "github.com/kcp-dev/kcp-operator/internal/resources" "github.com/kcp-dev/kcp-operator/internal/resources/frontproxy" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -113,16 +113,18 @@ func (r *FrontProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } - conditions, recErr := r.reconcile(ctx, &frontProxy) + namingScheme := naming.NewVersion1() - if err := r.reconcileStatus(ctx, &frontProxy, conditions); err != nil { + conditions, recErr := r.reconcile(ctx, &frontProxy, namingScheme) + + if err := r.reconcileStatus(ctx, &frontProxy, conditions, namingScheme); err != nil { recErr = kerrors.NewAggregate([]error{recErr, err}) } return ctrl.Result{}, recErr } -func (r *FrontProxyReconciler) reconcile(ctx context.Context, frontProxy *operatorv1alpha1.FrontProxy) ([]metav1.Condition, error) { +func (r *FrontProxyReconciler) reconcile(ctx context.Context, frontProxy *operatorv1alpha1.FrontProxy, names naming.Scheme) ([]metav1.Condition, error) { var ( conditions []metav1.Condition errs []error @@ -133,7 +135,7 @@ func (r *FrontProxyReconciler) reconcile(ctx context.Context, frontProxy *operat } // Ensure Bundle object exists if annotation is present - if _, err := bundlehelper.EnsureBundleForOwner(ctx, r.Client, r.Scheme, frontProxy); err != nil { + if _, err := bundlehelper.EnsureBundleForOwner(ctx, r.Client, r.Scheme, frontProxy, names); err != nil { errs = append(errs, fmt.Errorf("failed to ensure bundle: %w", err)) } @@ -144,7 +146,7 @@ func (r *FrontProxyReconciler) reconcile(ctx context.Context, frontProxy *operat return conditions, nil } - fpReconciler := frontproxy.NewFrontProxy(frontProxy, rootShard) + fpReconciler := frontproxy.NewFrontProxy(frontProxy, rootShard, names) // Deployment will be scaled to 0 if bundle annotation is present if err := fpReconciler.Reconcile(ctx, r.Client, frontProxy.Namespace); err != nil { @@ -154,12 +156,12 @@ func (r *FrontProxyReconciler) reconcile(ctx context.Context, frontProxy *operat return conditions, kerrors.NewAggregate(errs) } -func (r *FrontProxyReconciler) reconcileStatus(ctx context.Context, oldFrontProxy *operatorv1alpha1.FrontProxy, conditions []metav1.Condition) error { +func (r *FrontProxyReconciler) reconcileStatus(ctx context.Context, oldFrontProxy *operatorv1alpha1.FrontProxy, conditions []metav1.Condition, names naming.Scheme) error { frontProxy := oldFrontProxy.DeepCopy() var errs []error // Add Bundle condition - bundleCond := bundlehelper.GetBundleReadyCondition(ctx, r.Client, frontProxy, frontProxy.Generation) + bundleCond := bundlehelper.GetBundleReadyCondition(ctx, r.Client, frontProxy, frontProxy.Generation, names) conditions = append(conditions, bundleCond) // Check if frontproxy is bundled (has bundle annotation with Ready bundle) @@ -167,7 +169,7 @@ func (r *FrontProxyReconciler) reconcileStatus(ctx context.Context, oldFrontProx // Only check deployment status if not bundled if !isBundled { - depKey := types.NamespacedName{Namespace: frontProxy.Namespace, Name: resources.GetFrontProxyDeploymentName(frontProxy)} + depKey := types.NamespacedName{Namespace: frontProxy.Namespace, Name: names.FrontProxyDeploymentName(frontProxy)} cond, err := util.GetDeploymentAvailableCondition(ctx, r.Client, depKey) if err != nil { errs = append(errs, err) diff --git a/internal/controller/kubeconfig/controller.go b/internal/controller/kubeconfig/controller.go index 3eb37fc9..f7cf8317 100644 --- a/internal/controller/kubeconfig/controller.go +++ b/internal/controller/kubeconfig/controller.go @@ -42,8 +42,8 @@ import ( "github.com/kcp-dev/kcp-operator/internal/controller/util" "github.com/kcp-dev/kcp-operator/internal/metrics" "github.com/kcp-dev/kcp-operator/internal/reconciling" - "github.com/kcp-dev/kcp-operator/internal/resources" "github.com/kcp-dev/kcp-operator/internal/resources/kubeconfig" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -128,6 +128,8 @@ func (r *KubeconfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) func (r *KubeconfigReconciler) reconcile(ctx context.Context, kc *operatorv1alpha1.Kubeconfig, req types.NamespacedName) ([]metav1.Condition, error) { var conditions []metav1.Condition + namingScheme := naming.NewVersion1() + rootShard := &operatorv1alpha1.RootShard{} shard := &operatorv1alpha1.Shard{} var frontProxy operatorv1alpha1.FrontProxy @@ -151,8 +153,8 @@ func (r *KubeconfigReconciler) reconcile(ctx context.Context, kc *operatorv1alph return conditions, err } - clientCertIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA) - serverCA = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) + clientCertIssuer = namingScheme.RootShardCAName(rootShard, operatorv1alpha1.ClientCA) + serverCA = namingScheme.RootShardCAName(rootShard, operatorv1alpha1.ServerCA) case kc.Spec.Target.ShardRef != nil: if err := r.Get(ctx, types.NamespacedName{Name: kc.Spec.Target.ShardRef.Name, Namespace: req.Namespace}, shard); err != nil { @@ -189,8 +191,8 @@ func (r *KubeconfigReconciler) reconcile(ctx context.Context, kc *operatorv1alph } // The client CA is shared among all shards and owned by the root shard. - clientCertIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA) - serverCA = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) + clientCertIssuer = namingScheme.RootShardCAName(rootShard, operatorv1alpha1.ClientCA) + serverCA = namingScheme.RootShardCAName(rootShard, operatorv1alpha1.ServerCA) case kc.Spec.Target.FrontProxyRef != nil: if err := r.Get(ctx, types.NamespacedName{Name: kc.Spec.Target.FrontProxyRef.Name, Namespace: req.Namespace}, &frontProxy); err != nil { @@ -226,8 +228,8 @@ func (r *KubeconfigReconciler) reconcile(ctx context.Context, kc *operatorv1alph return conditions, err } - clientCertIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA) - serverCA = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) + clientCertIssuer = namingScheme.RootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA) + serverCA = namingScheme.RootShardCAName(rootShard, operatorv1alpha1.ServerCA) if frontProxy.Spec.CABundleSecretRef != nil { caBundle = &corev1.Secret{} @@ -262,7 +264,7 @@ func (r *KubeconfigReconciler) reconcile(ctx context.Context, kc *operatorv1alph }) certReconcilers := []reconciling.NamedCertificateReconcilerFactory{ - kubeconfig.ClientCertificateReconciler(kc, clientCertIssuer), + kubeconfig.ClientCertificateReconciler(kc, clientCertIssuer, namingScheme), } if err := reconciling.ReconcileCertificates(ctx, certReconcilers, req.Namespace, r.Client); err != nil { @@ -307,7 +309,7 @@ func (r *KubeconfigReconciler) reconcile(ctx context.Context, kc *operatorv1alph return conditions, nil } - reconciler, err := kubeconfig.KubeconfigSecretReconciler(kc, rootShard, shard, frontProxy, serverCASecret, clientCertSecret, caBundle) + reconciler, err := kubeconfig.KubeconfigSecretReconciler(kc, rootShard, shard, frontProxy, serverCASecret, clientCertSecret, caBundle, namingScheme) if err != nil { return conditions, err } diff --git a/internal/controller/rootshard/controller.go b/internal/controller/rootshard/controller.go index 9efce18d..ffc99e0b 100644 --- a/internal/controller/rootshard/controller.go +++ b/internal/controller/rootshard/controller.go @@ -46,8 +46,8 @@ import ( "github.com/kcp-dev/kcp-operator/internal/metrics" "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/reconciling/modifier" - "github.com/kcp-dev/kcp-operator/internal/resources" "github.com/kcp-dev/kcp-operator/internal/resources/frontproxy" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/rootshard" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -146,9 +146,12 @@ func (r *RootShardReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - conditions, recErr := r.reconcile(ctx, &rootShard) + // Instantiate the naming scheme + namingScheme := naming.NewVersion1() - if err := r.reconcileStatus(ctx, &rootShard, conditions); err != nil { + conditions, recErr := r.reconcile(ctx, &rootShard, namingScheme) + + if err := r.reconcileStatus(ctx, &rootShard, conditions, namingScheme); err != nil { recErr = kerrors.NewAggregate([]error{recErr, err}) } @@ -156,7 +159,7 @@ func (r *RootShardReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } //nolint:unparam // Keep the controller working the same as all the others, even though currently it does always return nil conditions. -func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operatorv1alpha1.RootShard) ([]metav1.Condition, error) { +func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) ([]metav1.Condition, error) { var ( errs []error conditions []metav1.Condition @@ -167,7 +170,7 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator } // Ensure Bundle object exists if annotation is present - if _, err := bundlehelper.EnsureBundleForOwner(ctx, r.Client, r.Scheme, rootShard); err != nil { + if _, err := bundlehelper.EnsureBundleForOwner(ctx, r.Client, r.Scheme, rootShard, names); err != nil { errs = append(errs, fmt.Errorf("failed to ensure bundle: %w", err)) } @@ -175,17 +178,17 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator revisionLabels := modifier.RelatedRevisionsLabels(ctx, r.Client) issuerReconcilers := []reconciling.NamedIssuerReconcilerFactory{ - rootshard.RootCAIssuerReconciler(rootShard), + rootshard.RootCAIssuerReconciler(rootShard, names), } certReconcilers := []reconciling.NamedCertificateReconcilerFactory{ - rootshard.ServerCertificateReconciler(rootShard), - rootshard.ServiceAccountCertificateReconciler(rootShard), - rootshard.VirtualWorkspacesCertificateReconciler(rootShard), - rootshard.LogicalClusterAdminCertificateReconciler(rootShard), - rootshard.ExternalLogicalClusterAdminCertificateReconciler(rootShard), - rootshard.ClientCertificateReconciler(rootShard), - rootshard.OperatorClientCertificateReconciler(rootShard), + rootshard.ServerCertificateReconciler(rootShard, names), + rootshard.ServiceAccountCertificateReconciler(rootShard, names), + rootshard.VirtualWorkspacesCertificateReconciler(rootShard, names), + rootshard.LogicalClusterAdminCertificateReconciler(rootShard, names), + rootshard.ExternalLogicalClusterAdminCertificateReconciler(rootShard, names), + rootshard.ClientCertificateReconciler(rootShard, names), + rootshard.OperatorClientCertificateReconciler(rootShard, names), } // Intermediate CAs that we need to generate a certificate and an issuer for. @@ -198,11 +201,11 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator } for _, ca := range intermediateCAs { - certReconcilers = append(certReconcilers, rootshard.CACertificateReconciler(rootShard, ca)) - issuerReconcilers = append(issuerReconcilers, rootshard.CAIssuerReconciler(rootShard, ca)) + certReconcilers = append(certReconcilers, rootshard.CACertificateReconciler(rootShard, ca, names)) + issuerReconcilers = append(issuerReconcilers, rootshard.CAIssuerReconciler(rootShard, ca, names)) } if rootShard.Spec.Certificates.IssuerRef != nil { - certReconcilers = append(certReconcilers, rootshard.RootCACertificateReconciler(rootShard)) + certReconcilers = append(certReconcilers, rootshard.RootCACertificateReconciler(rootShard, names)) } if err := reconciling.ReconcileCertificates(ctx, certReconcilers, rootShard.Namespace, r.Client, ownerRefWrapper); err != nil { @@ -215,15 +218,15 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator if rootShard.Spec.CABundleSecretRef != nil { if err := k8creconciling.ReconcileSecrets(ctx, []k8creconciling.NamedSecretReconcilerFactory{ - rootshard.MergedCABundleSecretReconciler(ctx, rootShard, r.Client), + rootshard.MergedCABundleSecretReconciler(ctx, rootShard, r.Client, names), }, rootShard.Namespace, r.Client, ownerRefWrapper); err != nil { errs = append(errs, err) } } if err := k8creconciling.ReconcileSecrets(ctx, []k8creconciling.NamedSecretReconcilerFactory{ - rootshard.LogicalClusterAdminKubeconfigReconciler(rootShard), - rootshard.ExternalLogicalClusterAdminKubeconfigReconciler(rootShard), + rootshard.LogicalClusterAdminKubeconfigReconciler(rootShard, names), + rootshard.ExternalLogicalClusterAdminKubeconfigReconciler(rootShard, names), }, rootShard.Namespace, r.Client, ownerRefWrapper); err != nil { errs = append(errs, err) } @@ -246,7 +249,7 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator // Deployment will be scaled to 0 if bundle annotation is present if vwConfigValid { if err := k8creconciling.ReconcileDeployments(ctx, []k8creconciling.NamedDeploymentReconcilerFactory{ - rootshard.DeploymentReconciler(rootShard, kcpVW), + rootshard.DeploymentReconciler(rootShard, kcpVW, names), }, rootShard.Namespace, r.Client, ownerRefWrapper, revisionLabels); err != nil { // Swallow these errors and instead rely on us watching Secrets and re-reconciling whenever they change. if !errors.Is(err, modifier.ErrMountNotFound) { @@ -256,12 +259,12 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator } if err := k8creconciling.ReconcileServices(ctx, []k8creconciling.NamedServiceReconcilerFactory{ - rootshard.ServiceReconciler(rootShard), + rootshard.ServiceReconciler(rootShard, names), }, rootShard.Namespace, r.Client, ownerRefWrapper); err != nil { errs = append(errs, err) } - if err := frontproxy.NewRootShardProxy(rootShard).Reconcile(ctx, r.Client, rootShard.Namespace); err != nil { + if err := frontproxy.NewRootShardProxy(rootShard, names).Reconcile(ctx, r.Client, rootShard.Namespace); err != nil { errs = append(errs, fmt.Errorf("failed to reconcile proxy: %w", err)) } @@ -269,12 +272,12 @@ func (r *RootShardReconciler) reconcile(ctx context.Context, rootShard *operator } // reconcileStatus sets both phase and conditions on the reconciled RootShard object. -func (r *RootShardReconciler) reconcileStatus(ctx context.Context, oldRootShard *operatorv1alpha1.RootShard, conditions []metav1.Condition) error { +func (r *RootShardReconciler) reconcileStatus(ctx context.Context, oldRootShard *operatorv1alpha1.RootShard, conditions []metav1.Condition, names naming.Scheme) error { rootShard := oldRootShard.DeepCopy() var errs []error // Add Bundle condition - bundleCond := bundlehelper.GetBundleReadyCondition(ctx, r.Client, rootShard, rootShard.Generation) + bundleCond := bundlehelper.GetBundleReadyCondition(ctx, r.Client, rootShard, rootShard.Generation, names) conditions = append(conditions, bundleCond) // Check if rootshard is bundled (has bundle annotation with Ready bundle) @@ -282,7 +285,7 @@ func (r *RootShardReconciler) reconcileStatus(ctx context.Context, oldRootShard // Only check deployment status if not bundled if !isBundled { - depKey := types.NamespacedName{Namespace: rootShard.Namespace, Name: resources.GetRootShardDeploymentName(rootShard)} + depKey := types.NamespacedName{Namespace: rootShard.Namespace, Name: names.RootShardDeploymentName(rootShard)} cond, err := util.GetDeploymentAvailableCondition(ctx, r.Client, depKey) if err != nil { errs = append(errs, err) diff --git a/internal/controller/shard/controller.go b/internal/controller/shard/controller.go index ee98db43..c3f952c0 100644 --- a/internal/controller/shard/controller.go +++ b/internal/controller/shard/controller.go @@ -24,8 +24,8 @@ import ( "time" certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" - kcpcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" "github.com/kcp-dev/logicalcluster/v3" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" k8creconciling "k8c.io/reconciler/pkg/reconciling" appsv1 "k8s.io/api/apps/v1" @@ -51,7 +51,7 @@ import ( "github.com/kcp-dev/kcp-operator/internal/metrics" "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/reconciling/modifier" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/shard" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -143,23 +143,25 @@ func (r *ShardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res return ctrl.Result{}, nil } - conditions, recErr := r.reconcile(ctx, &s) + namingScheme := naming.NewVersion1() - if err := r.reconcileStatus(ctx, &s, conditions); err != nil { + conditions, recErr := r.reconcile(ctx, &s, namingScheme) + + if err := r.reconcileStatus(ctx, &s, conditions, namingScheme); err != nil { recErr = kerrors.NewAggregate([]error{recErr, err}) } return ctrl.Result{}, recErr } -func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Shard) ([]metav1.Condition, error) { +func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Shard, names naming.Scheme) ([]metav1.Condition, error) { var ( errs []error conditions []metav1.Condition ) if s.DeletionTimestamp != nil { - return r.handleDeletion(ctx, s) + return r.handleDeletion(ctx, s, names) } // Ensure finalizer before any other work @@ -170,7 +172,7 @@ func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Sha } // Ensure Bundle object exists if annotation is present - if _, err := bundlehelper.EnsureBundleForOwner(ctx, r.Client, r.Scheme, s); err != nil { + if _, err := bundlehelper.EnsureBundleForOwner(ctx, r.Client, r.Scheme, s, names); err != nil { errs = append(errs, fmt.Errorf("failed to ensure bundle: %w", err)) } @@ -185,12 +187,12 @@ func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Sha revisionLabels := modifier.RelatedRevisionsLabels(ctx, r.Client) certReconcilers := []reconciling.NamedCertificateReconcilerFactory{ - shard.ServerCertificateReconciler(s, rootShard), - shard.ServiceAccountCertificateReconciler(s, rootShard), - shard.VirtualWorkspacesCertificateReconciler(s, rootShard), - shard.RootShardClientCertificateReconciler(s, rootShard), - shard.LogicalClusterAdminCertificateReconciler(s, rootShard), - shard.ExternalLogicalClusterAdminCertificateReconciler(s, rootShard), + shard.ServerCertificateReconciler(s, rootShard, names), + shard.ServiceAccountCertificateReconciler(s, rootShard, names), + shard.VirtualWorkspacesCertificateReconciler(s, rootShard, names), + shard.RootShardClientCertificateReconciler(s, rootShard, names), + shard.LogicalClusterAdminCertificateReconciler(s, rootShard, names), + shard.ExternalLogicalClusterAdminCertificateReconciler(s, rootShard, names), } if err := reconciling.ReconcileCertificates(ctx, certReconcilers, s.Namespace, r.Client, ownerRefWrapper); err != nil { @@ -198,16 +200,16 @@ func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Sha } if err := k8creconciling.ReconcileSecrets(ctx, []k8creconciling.NamedSecretReconcilerFactory{ - shard.RootShardClientKubeconfigReconciler(s, rootShard), - shard.LogicalClusterAdminKubeconfigReconciler(s, rootShard), - shard.ExternalLogicalClusterAdminKubeconfigReconciler(s, rootShard), + shard.RootShardClientKubeconfigReconciler(s, rootShard, names), + shard.LogicalClusterAdminKubeconfigReconciler(s, rootShard, names), + shard.ExternalLogicalClusterAdminKubeconfigReconciler(s, rootShard, names), }, s.Namespace, r.Client, ownerRefWrapper); err != nil { errs = append(errs, err) } if s.Spec.CABundleSecretRef != nil { if err := k8creconciling.ReconcileSecrets(ctx, []k8creconciling.NamedSecretReconcilerFactory{ - shard.MergedCABundleSecretReconciler(ctx, s, r.Client), + shard.MergedCABundleSecretReconciler(ctx, s, r.Client, names), }, s.Namespace, r.Client, ownerRefWrapper); err != nil { errs = append(errs, err) } @@ -231,7 +233,7 @@ func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Sha // Deployment will be scaled to 0 if bundle annotation is present if vwConfigValid { if err := k8creconciling.ReconcileDeployments(ctx, []k8creconciling.NamedDeploymentReconcilerFactory{ - shard.DeploymentReconciler(s, rootShard, kcpVW), + shard.DeploymentReconciler(s, rootShard, kcpVW, names), }, s.Namespace, r.Client, ownerRefWrapper, revisionLabels); err != nil { // Swallow these errors and instead rely on us watching Secrets and re-reconciling whenever they change. if !errors.Is(err, modifier.ErrMountNotFound) { @@ -241,7 +243,7 @@ func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Sha } if err := k8creconciling.ReconcileServices(ctx, []k8creconciling.NamedServiceReconcilerFactory{ - shard.ServiceReconciler(s), + shard.ServiceReconciler(s, names), }, s.Namespace, r.Client, ownerRefWrapper); err != nil { errs = append(errs, err) } @@ -250,12 +252,12 @@ func (r *ShardReconciler) reconcile(ctx context.Context, s *operatorv1alpha1.Sha } // reconcileStatus sets both phase and conditions on the reconciled Shard object. -func (r *ShardReconciler) reconcileStatus(ctx context.Context, oldShard *operatorv1alpha1.Shard, conditions []metav1.Condition) error { +func (r *ShardReconciler) reconcileStatus(ctx context.Context, oldShard *operatorv1alpha1.Shard, conditions []metav1.Condition, names naming.Scheme) error { newShard := oldShard.DeepCopy() var errs []error // Add Bundle condition - bundleCond := bundlehelper.GetBundleReadyCondition(ctx, r.Client, newShard, newShard.Generation) + bundleCond := bundlehelper.GetBundleReadyCondition(ctx, r.Client, newShard, newShard.Generation, names) conditions = append(conditions, bundleCond) // Check if shard is bundled (has bundle annotation with Ready bundle) @@ -263,7 +265,7 @@ func (r *ShardReconciler) reconcileStatus(ctx context.Context, oldShard *operato // Only check deployment status if not bundled if !isBundled { - depKey := types.NamespacedName{Namespace: newShard.Namespace, Name: resources.GetShardDeploymentName(newShard)} + depKey := types.NamespacedName{Namespace: newShard.Namespace, Name: names.ShardDeploymentName(newShard)} cond, err := util.GetDeploymentAvailableCondition(ctx, r.Client, depKey) if err != nil { errs = append(errs, err) @@ -311,7 +313,7 @@ func (r *ShardReconciler) reconcileStatus(ctx context.Context, oldShard *operato return kerrors.NewAggregate(errs) } -func (r *ShardReconciler) handleDeletion(ctx context.Context, s *operatorv1alpha1.Shard) ([]metav1.Condition, error) { +func (r *ShardReconciler) handleDeletion(ctx context.Context, s *operatorv1alpha1.Shard, names naming.Scheme) ([]metav1.Condition, error) { logger := log.FromContext(ctx) if !slices.Contains(s.Finalizers, cleanupFinalizer) { @@ -330,7 +332,7 @@ func (r *ShardReconciler) handleDeletion(ctx context.Context, s *operatorv1alpha } // Create client to root shard - kcpClient, err := client.NewRootShardClient(ctx, r.Client, rootShard, logicalcluster.Name("root"), r.Scheme) + kcpClient, err := client.NewRootShardClient(ctx, names, r.Client, rootShard, logicalcluster.Name("root"), r.Scheme) if err != nil { return nil, fmt.Errorf("failed to create root shard client: %w", err) } diff --git a/internal/controller/virtualworkspace/controller.go b/internal/controller/virtualworkspace/controller.go index 2e26402a..354c92db 100644 --- a/internal/controller/virtualworkspace/controller.go +++ b/internal/controller/virtualworkspace/controller.go @@ -42,7 +42,7 @@ import ( "github.com/kcp-dev/kcp-operator/internal/metrics" "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/reconciling/modifier" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/virtualworkspace" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -104,18 +104,20 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. return ctrl.Result{}, nil } + namingScheme := naming.NewVersion1() + vwCopy := vw.DeepCopy() - conditions, recErr := r.reconcile(ctx, vwCopy) + conditions, recErr := r.reconcile(ctx, vwCopy, namingScheme) - if err := r.reconcileStatus(ctx, vw, vwCopy, conditions); err != nil { + if err := r.reconcileStatus(ctx, vw, vwCopy, conditions, namingScheme); err != nil { recErr = kerrors.NewAggregate([]error{recErr, err}) } return ctrl.Result{}, recErr } -func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.VirtualWorkspace) ([]metav1.Condition, error) { +func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.VirtualWorkspace, names naming.Scheme) ([]metav1.Condition, error) { var conditions []metav1.Condition var ( @@ -139,8 +141,8 @@ func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.Virtual return conditions, err } - clientCertIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA) - // serverCA = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) + clientCertIssuer = names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA) + // serverCA = names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA) case vw.Spec.Target.ShardRef != nil: shard = &operatorv1alpha1.Shard{} @@ -181,8 +183,8 @@ func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.Virtual } // The client CA is shared among all shards and owned by the root shard. - clientCertIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA) - // serverCA = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) + clientCertIssuer = names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA) + // serverCA = names.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) default: err := errors.New("no valid target for VirtualWorkspace found") @@ -206,14 +208,14 @@ func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.Virtual revisionLabels := modifier.RelatedRevisionsLabels(ctx, r.Client) if err := reconciling.ReconcileCertificates(ctx, []reconciling.NamedCertificateReconcilerFactory{ - virtualworkspace.ClientCertificateReconciler(vw, clientCertIssuer), - virtualworkspace.ServerCertificateReconciler(vw, rootShard), + virtualworkspace.ClientCertificateReconciler(vw, clientCertIssuer, names), + virtualworkspace.ServerCertificateReconciler(vw, rootShard, names), }, vw.Namespace, r.Client, ownerRefWrapper); err != nil { return conditions, err } if err := k8creconciling.ReconcileDeployments(ctx, []k8creconciling.NamedDeploymentReconcilerFactory{ - virtualworkspace.DeploymentReconciler(vw, rootShard, shard), + virtualworkspace.DeploymentReconciler(vw, rootShard, shard, names), }, vw.Namespace, r.Client, ownerRefWrapper, revisionLabels); err != nil { // Swallow these errors and instead rely on us watching Secrets and re-reconciling whenever they change. if errors.Is(err, modifier.ErrMountNotFound) { @@ -224,7 +226,7 @@ func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.Virtual } if err := k8creconciling.ReconcileServices(ctx, []k8creconciling.NamedServiceReconcilerFactory{ - virtualworkspace.ServiceReconciler(vw), + virtualworkspace.ServiceReconciler(vw, names), }, vw.Namespace, r.Client, ownerRefWrapper); err != nil { return conditions, err } @@ -232,9 +234,9 @@ func (r *Reconciler) reconcile(ctx context.Context, vw *operatorv1alpha1.Virtual return conditions, nil } -func (r *Reconciler) reconcileStatus(ctx context.Context, oldVW *operatorv1alpha1.VirtualWorkspace, vw *operatorv1alpha1.VirtualWorkspace, conditions []metav1.Condition) error { +func (r *Reconciler) reconcileStatus(ctx context.Context, oldVW *operatorv1alpha1.VirtualWorkspace, vw *operatorv1alpha1.VirtualWorkspace, conditions []metav1.Condition, names naming.Scheme) error { // Check deployment status - depKey := types.NamespacedName{Namespace: vw.Namespace, Name: resources.GetVirtualWorkspaceDeploymentName(vw)} + depKey := types.NamespacedName{Namespace: vw.Namespace, Name: names.VirtualWorkspaceDeploymentName(vw)} cond, err := util.GetDeploymentAvailableCondition(ctx, r.Client, depKey) if err != nil { return err @@ -277,35 +279,57 @@ func (r *Reconciler) mapIssuerToVirtualWorkspaces(ctx context.Context, obj ctrlr logger := log.FromContext(ctx).WithValues("issuer", obj.GetName()) logger.V(4).Info("Mapping Issuer to VirtualWorkspaces") - // Find all VirtualWorkspaces that use this Issuer for their client certificate + // Find all VirtualWorkspaces that use this Issuer for their server/client certificates var virtualWorkspaces operatorv1alpha1.VirtualWorkspaceList if err := r.List(ctx, &virtualWorkspaces, ctrlruntimeclient.InNamespace(obj.GetNamespace())); err != nil { logger.Error(err, "Failed to list VirtualWorkspaces") return []ctrl.Request{} } + allNamingSchemes := []naming.Scheme{ + naming.NewVersion1(), + } + var requests []ctrl.Request for _, vw := range virtualWorkspaces.Items { - var expectedIssuer string + var rootShard *operatorv1alpha1.RootShard + switch { case vw.Spec.Target.RootShardRef != nil: - rootShard := &operatorv1alpha1.RootShard{} - if err := r.Get(ctx, types.NamespacedName{Name: vw.Spec.Target.RootShardRef.Name, Namespace: vw.Namespace}, rootShard); err == nil { - expectedIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA) + rootShard = &operatorv1alpha1.RootShard{} + if err := r.Get(ctx, types.NamespacedName{Name: vw.Spec.Target.RootShardRef.Name, Namespace: vw.Namespace}, rootShard); err != nil { + logger.Error(err, "Failed to get RootShard") } case vw.Spec.Target.ShardRef != nil: shard := &operatorv1alpha1.Shard{} if err := r.Get(ctx, types.NamespacedName{Name: vw.Spec.Target.ShardRef.Name, Namespace: vw.Namespace}, shard); err == nil { if ref := shard.Spec.RootShard.Reference; ref != nil { - rootShard := &operatorv1alpha1.RootShard{} - if err := r.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: vw.Namespace}, rootShard); err == nil { - expectedIssuer = resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA) + rootShard = &operatorv1alpha1.RootShard{} + if err := r.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: vw.Namespace}, rootShard); err != nil { + logger.Error(err, "Failed to get RootShard") } } } } - if expectedIssuer == obj.GetName() { + // check name in case the .Get() call failed + if rootShard == nil || rootShard.Name == "" { + continue + } + + // Enqueue this issuer if it is being used by any existing naming scheme for either the ServerCA or Client CA. + issuerName := obj.GetName() + + var enqueue bool + for _, names := range allNamingSchemes { + // In the kcp-operator, the name of the Issuer is always identical to the name of the CA. + if issuerName == names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA) || issuerName == names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA) { + enqueue = true + break + } + } + + if enqueue { requests = append(requests, ctrl.Request{ NamespacedName: types.NamespacedName{ Name: vw.Name, diff --git a/internal/resources/bundling/objects.go b/internal/resources/bundling/objects.go new file mode 100644 index 00000000..6a2d712d --- /dev/null +++ b/internal/resources/bundling/objects.go @@ -0,0 +1,78 @@ +/* +Copyright 2026 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 bundling + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +var ( + // SecretGVR is the GVR for Secret resources + SecretGVR = schema.GroupVersionResource{ + Group: corev1.GroupName, + Version: "v1", + Resource: "secrets", + } + + // ServiceGVR is the GVR for Service resources + ServiceGVR = schema.GroupVersionResource{ + Group: corev1.GroupName, + Version: "v1", + Resource: "services", + } + + // ConfigMapGVR is the GVR for ConfigMap resources + ConfigMapGVR = schema.GroupVersionResource{ + Group: corev1.GroupName, + Version: "v1", + Resource: "configmaps", + } + + // DeploymentGVR is the GVR for Deployment resources + DeploymentGVR = schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", + } +) + +func NewObject(gvr schema.GroupVersionResource, name, namespace string) operatorv1alpha1.BundleObject { + return operatorv1alpha1.BundleObject{ + GVR: gvr, + Name: name, + Namespace: namespace, + } +} + +func NewSecret(name, namespace string) operatorv1alpha1.BundleObject { + return NewObject(SecretGVR, name, namespace) +} + +func NewService(name, namespace string) operatorv1alpha1.BundleObject { + return NewObject(ServiceGVR, name, namespace) +} + +func NewConfigMap(name, namespace string) operatorv1alpha1.BundleObject { + return NewObject(ConfigMapGVR, name, namespace) +} + +func NewDeployment(name, namespace string) operatorv1alpha1.BundleObject { + return NewObject(DeploymentGVR, name, namespace) +} diff --git a/internal/resources/cacheserver/certificates.go b/internal/resources/cacheserver/certificates.go index f0146b33..41237b74 100644 --- a/internal/resources/cacheserver/certificates.go +++ b/internal/resources/cacheserver/certificates.go @@ -22,13 +22,14 @@ import ( "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) // RootCACertificateReconciler creates a standalone CA just for a single cache-server. -func RootCACertificateReconciler(server *operatorv1alpha1.CacheServer) reconciling.NamedCertificateReconcilerFactory { - name := resources.GetCacheServerCAName(server.Name, operatorv1alpha1.RootCA) +func RootCACertificateReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { + name := names.CacheServerCAName(server.Name, operatorv1alpha1.RootCA) template := server.Spec.CertificateTemplates.CATemplate(operatorv1alpha1.RootCA) if server.Spec.Certificates.IssuerRef == nil { @@ -37,7 +38,7 @@ func RootCACertificateReconciler(server *operatorv1alpha1.CacheServer) reconcili return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetCacheServerResourceLabels(server)) + cert.SetLabels(names.CacheServerResourceLabels(server)) cert.Spec = certmanagerv1.CertificateSpec{ IsCA: true, @@ -56,7 +57,7 @@ func RootCACertificateReconciler(server *operatorv1alpha1.CacheServer) reconcili Size: 4096, }, - IssuerRef: certmanagermetav1.ObjectReference{ + IssuerRef: certmanagermetav1.IssuerReference{ Name: server.Spec.Certificates.IssuerRef.Name, Kind: server.Spec.Certificates.IssuerRef.Kind, Group: server.Spec.Certificates.IssuerRef.Group, @@ -70,13 +71,13 @@ func RootCACertificateReconciler(server *operatorv1alpha1.CacheServer) reconcili // ClientCertificateReconciler creates a client certificate for authenticating to the cache server. // This certificate is mounted by shards that connect to the cache server. -func ClientCertificateReconciler(server *operatorv1alpha1.CacheServer) reconciling.NamedCertificateReconcilerFactory { - name := resources.GetCacheServerClientCertificateName(server) +func ClientCertificateReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { + name := names.CacheServerClientCertificateName(server.Name) template := server.Spec.CertificateTemplates.CertificateTemplate(operatorv1alpha1.ClientCertificate) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetCacheServerResourceLabels(server)) + cert.SetLabels(names.CacheServerResourceLabels(server)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -105,8 +106,8 @@ func ClientCertificateReconciler(server *operatorv1alpha1.CacheServer) reconcili certmanagerv1.UsageDigitalSignature, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetCacheServerCAName(server.Name, operatorv1alpha1.RootCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.CacheServerCAName(server.Name, operatorv1alpha1.RootCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -117,15 +118,15 @@ func ClientCertificateReconciler(server *operatorv1alpha1.CacheServer) reconcili } } -func ServerCertificateReconciler(server *operatorv1alpha1.CacheServer) reconciling.NamedCertificateReconcilerFactory { +func ServerCertificateReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ServerCertificate - name := resources.GetCacheServerCertificateName(server, certKind) + name := names.CacheServerCertificateName(server, certKind) template := server.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetCacheServerResourceLabels(server)) + cert.SetLabels(names.CacheServerResourceLabels(server)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -150,11 +151,11 @@ func ServerCertificateReconciler(server *operatorv1alpha1.CacheServer) reconcili DNSNames: []string{ "localhost", - resources.GetCacheServerBaseHost(server), + names.CacheServerBaseHost(server), }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetCacheServerCAName(server.Name, operatorv1alpha1.RootCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.CacheServerCAName(server.Name, operatorv1alpha1.RootCA), Kind: "Issuer", Group: "cert-manager.io", }, diff --git a/internal/resources/cacheserver/deployment.go b/internal/resources/cacheserver/deployment.go index 43629b4c..48a084a5 100644 --- a/internal/resources/cacheserver/deployment.go +++ b/internal/resources/cacheserver/deployment.go @@ -31,6 +31,7 @@ import ( "k8s.io/utils/ptr" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -75,10 +76,10 @@ func getClientCertificateMountPath() string { return "/etc/cache-server/tls/client-certificate" } -func DeploymentReconciler(server *operatorv1alpha1.CacheServer) reconciling.NamedDeploymentReconcilerFactory { +func DeploymentReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) reconciling.NamedDeploymentReconcilerFactory { return func() (string, reconciling.DeploymentReconciler) { - return resources.GetCacheServerDeploymentName(server), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { - labels := resources.GetCacheServerResourceLabels(server) + return names.CacheServerDeploymentName(server), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { + labels := names.CacheServerResourceLabels(server) dep.SetLabels(labels) dep.Spec.Selector = &metav1.LabelSelector{ MatchLabels: labels, @@ -101,7 +102,7 @@ func DeploymentReconciler(server *operatorv1alpha1.CacheServer) reconciling.Name volumes, volumeMounts := getVolumeMounts(server) - for _, sm := range getSecretMounts(server, version) { + for _, sm := range getSecretMounts(server, version, names) { v, vm := sm.Build() volumes = append(volumes, v) volumeMounts = append(volumeMounts, vm) @@ -241,11 +242,11 @@ func getArgs(server *operatorv1alpha1.CacheServer, version *semver.Version) []st return args } -func getSecretMounts(server *operatorv1alpha1.CacheServer, version *semver.Version) []utils.SecretMount { +func getSecretMounts(server *operatorv1alpha1.CacheServer, version *semver.Version, names naming.Scheme) []utils.SecretMount { secretMounts := []utils.SecretMount{ { VolumeName: "serving-cert", - SecretName: resources.GetCacheServerCertificateName(server, operatorv1alpha1.ServerCertificate), + SecretName: names.CacheServerCertificateName(server, operatorv1alpha1.ServerCertificate), MountPath: getCertificateMountPath(operatorv1alpha1.ServerCertificate), }, } @@ -253,7 +254,7 @@ func getSecretMounts(server *operatorv1alpha1.CacheServer, version *semver.Versi if hasAuthenticatedCache(version) { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "client-ca", - SecretName: resources.GetCacheServerCAName(server.Name, operatorv1alpha1.RootCA), + SecretName: names.CacheServerCAName(server.Name, operatorv1alpha1.RootCA), MountPath: getCAMountPath(operatorv1alpha1.RootCA), }) } diff --git a/internal/resources/cacheserver/issuers.go b/internal/resources/cacheserver/issuers.go index f1441191..ff2712e3 100644 --- a/internal/resources/cacheserver/issuers.go +++ b/internal/resources/cacheserver/issuers.go @@ -20,12 +20,12 @@ import ( certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" "github.com/kcp-dev/kcp-operator/internal/reconciling" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func RootCAIssuerReconciler(server *operatorv1alpha1.CacheServer) reconciling.NamedIssuerReconcilerFactory { - name := resources.GetCacheServerCAName(server.Name, operatorv1alpha1.RootCA) +func RootCAIssuerReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) reconciling.NamedIssuerReconcilerFactory { + name := names.CacheServerCAName(server.Name, operatorv1alpha1.RootCA) secretName := name if server.Spec.Certificates.CASecretRef != nil { @@ -34,7 +34,7 @@ func RootCAIssuerReconciler(server *operatorv1alpha1.CacheServer) reconciling.Na return func() (string, reconciling.IssuerReconciler) { return name, func(issuer *certmanagerv1.Issuer) (*certmanagerv1.Issuer, error) { - issuer.SetLabels(resources.GetCacheServerResourceLabels(server)) + issuer.SetLabels(names.CacheServerResourceLabels(server)) issuer.Spec = certmanagerv1.IssuerSpec{ IssuerConfig: certmanagerv1.IssuerConfig{ CA: &certmanagerv1.CAIssuer{ diff --git a/internal/resources/cacheserver/kubeconfigs.go b/internal/resources/cacheserver/kubeconfigs.go index 90d8966b..3039fedf 100644 --- a/internal/resources/cacheserver/kubeconfigs.go +++ b/internal/resources/cacheserver/kubeconfigs.go @@ -23,18 +23,18 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func KubeconfigReconciler(server *operatorv1alpha1.CacheServer) k8creconciling.NamedSecretReconcilerFactory { +func KubeconfigReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { const ( serverName = "cache" contextName = "cache" ) return func() (string, k8creconciling.SecretReconciler) { - return resources.GetCacheServerKubeconfigName(server.Name), func(secret *corev1.Secret) (*corev1.Secret, error) { + return names.CacheServerKubeconfigName(server.Name), func(secret *corev1.Secret) (*corev1.Secret, error) { var config *clientcmdapi.Config if secret.Data == nil { secret.Data = make(map[string][]byte) @@ -43,7 +43,7 @@ func KubeconfigReconciler(server *operatorv1alpha1.CacheServer) k8creconciling.N config = &clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ serverName: { - Server: resources.GetCacheServerBaseURL(server), + Server: names.CacheServerBaseURL(server), CertificateAuthority: getCAMountPath(operatorv1alpha1.RootCA) + "/tls.crt", }, }, diff --git a/internal/resources/cacheserver/service.go b/internal/resources/cacheserver/service.go index 112d670b..a5de0ebe 100644 --- a/internal/resources/cacheserver/service.go +++ b/internal/resources/cacheserver/service.go @@ -23,15 +23,15 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func ServiceReconciler(server *operatorv1alpha1.CacheServer) reconciling.NamedServiceReconcilerFactory { +func ServiceReconciler(server *operatorv1alpha1.CacheServer, names naming.Scheme) reconciling.NamedServiceReconcilerFactory { return func() (string, reconciling.ServiceReconciler) { - return resources.GetCacheServerServiceName(server), func(svc *corev1.Service) (*corev1.Service, error) { - labels := resources.GetCacheServerResourceLabels(server) + return names.CacheServerServiceName(server), func(svc *corev1.Service) (*corev1.Service, error) { + labels := names.CacheServerResourceLabels(server) svc.SetLabels(labels) svc.Spec.Type = corev1.ServiceTypeClusterIP svc.Spec.Ports = []corev1.ServicePort{ diff --git a/internal/resources/frontproxy/ca_bundle.go b/internal/resources/frontproxy/ca_bundle.go index ea9f907f..b60cb75a 100644 --- a/internal/resources/frontproxy/ca_bundle.go +++ b/internal/resources/frontproxy/ca_bundle.go @@ -32,10 +32,12 @@ import ( ) func (r *reconciler) mergedClientCASecretName() string { + owner := r.names.RootShardProxyDeploymentName(r.rootShard) if r.frontProxy != nil { - return fmt.Sprintf("%s-merged-client-ca", r.frontProxy.Name) + owner = r.frontProxy.Name } - return fmt.Sprintf("%s-proxy-merged-client-ca", r.rootShard.Name) + + return r.names.MergedClientCAName(owner) } // mergedClientCASecretReconciler creates a single secret with the @@ -44,7 +46,7 @@ func (r *reconciler) mergedClientCASecretName() string { func (r *reconciler) mergedClientCASecretReconciler(ctx context.Context, kubeClient ctrlruntimeclient.Client) k8creconciling.NamedSecretReconcilerFactory { getCA := func(caType operatorv1alpha1.CA) ([]byte, error) { caSecret := &corev1.Secret{} - caSecretName := resources.GetRootShardCAName(r.rootShard, caType) + caSecretName := r.names.RootShardCAName(r.rootShard, caType) if err := kubeClient.Get(ctx, types.NamespacedName{ Namespace: r.rootShard.Namespace, Name: caSecretName, @@ -92,11 +94,12 @@ func (r *reconciler) mergedClientCASecretReconciler(ctx context.Context, kubeCli } func (r *reconciler) mergedCABundleSecretName() string { - // Validate whether called for frontProxy or rootShardFrontProxy + owner := r.names.RootShardProxyDeploymentName(r.rootShard) if r.frontProxy != nil { - return fmt.Sprintf("%s-merged-ca-bundle", r.frontProxy.Name) + owner = r.frontProxy.Name } - return fmt.Sprintf("%s-proxy-merged-ca-bundle", r.rootShard.Name) + + return r.names.MergedCABundleName(owner) } func (r *reconciler) mergedCABundleSecretReconciler(ctx context.Context, kubeClient ctrlruntimeclient.Client) k8creconciling.NamedSecretReconcilerFactory { @@ -108,7 +111,7 @@ func (r *reconciler) mergedCABundleSecretReconciler(ctx context.Context, kubeCli // Get ServerCA certificate from the rootshard serverCASecret := &corev1.Secret{} - serverCASecretName := resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.ServerCA) + serverCASecretName := r.names.RootShardCAName(r.rootShard, operatorv1alpha1.ServerCA) err := kubeClient.Get(ctx, types.NamespacedName{ Name: serverCASecretName, Namespace: r.rootShard.Namespace, diff --git a/internal/resources/frontproxy/certificates.go b/internal/resources/frontproxy/certificates.go index c61a048c..616b8172 100644 --- a/internal/resources/frontproxy/certificates.go +++ b/internal/resources/frontproxy/certificates.go @@ -42,17 +42,17 @@ func (r *reconciler) certSecretLabels() map[string]string { func (r *reconciler) certName(certKind operatorv1alpha1.Certificate) string { if r.frontProxy != nil { - return resources.GetFrontProxyCertificateName(r.rootShard, r.frontProxy, certKind) + return r.names.FrontProxyCertificateName(r.rootShard, r.frontProxy, certKind) } else { - return resources.GetRootShardProxyCertificateName(r.rootShard, certKind) + return r.names.RootShardProxyCertificateName(r.rootShard, certKind) } } func (r *reconciler) certCommonName() string { if r.frontProxy != nil { - return "kcp-front-proxy" + return resources.FrontProxyCommonName } else { - return "kcp-root-shard-proxy" + return resources.RootShardProxyCommonName } } @@ -74,10 +74,15 @@ func (r *reconciler) serverCertificateReconciler() reconciling.NamedCertificateR template := r.certTemplateMap().CertificateTemplate(certKind) fpService := r.serviceName() + clusterDomain := "cluster.local" + if cd := r.rootShard.Spec.ClusterDomain; cd != "" { + clusterDomain = cd + } + dnsNames := []string{ fpService, fmt.Sprintf("%s.%s", fpService, r.rootShard.Namespace), - fmt.Sprintf("%s.%s.svc.cluster.local", fpService, r.rootShard.Namespace), + fmt.Sprintf("%s.%s.svc.%s", fpService, r.rootShard.Namespace, clusterDomain), } if r.frontProxy != nil { @@ -120,8 +125,8 @@ func (r *reconciler) serverCertificateReconciler() reconciling.NamedCertificateR DNSNames: dnsNames, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.ServerCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: r.names.RootShardCAName(r.rootShard, operatorv1alpha1.ServerCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -165,8 +170,8 @@ func (r *reconciler) adminKubeconfigCertificateReconciler() reconciling.NamedCer Organizations: []string{"system:kcp:external-logical-cluster-admin"}, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.FrontProxyClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: r.names.RootShardCAName(r.rootShard, operatorv1alpha1.FrontProxyClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -209,8 +214,8 @@ func (r *reconciler) kubeconfigCertificateReconciler() reconciling.NamedCertific certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.ClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: r.names.RootShardCAName(r.rootShard, operatorv1alpha1.ClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -249,8 +254,8 @@ func (r *reconciler) requestHeaderCertificateReconciler() reconciling.NamedCerti certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.RequestHeaderClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: r.names.RootShardCAName(r.rootShard, operatorv1alpha1.RequestHeaderClientCA), Kind: "Issuer", Group: "cert-manager.io", }, diff --git a/internal/resources/frontproxy/configmap.go b/internal/resources/frontproxy/configmap.go index a0da91f9..89b95f3b 100644 --- a/internal/resources/frontproxy/configmap.go +++ b/internal/resources/frontproxy/configmap.go @@ -22,15 +22,14 @@ import ( corev1 "k8s.io/api/core/v1" "sigs.k8s.io/yaml" - "github.com/kcp-dev/kcp-operator/internal/resources" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) func (r *reconciler) pathMappingConfigMapName() string { if r.frontProxy != nil { - return resources.GetFrontProxyConfigName(r.frontProxy) + return r.names.FrontProxyConfigName(r.frontProxy) } else { - return resources.GetRootShardProxyConfigName(r.rootShard) + return r.names.RootShardProxyConfigName(r.rootShard) } } @@ -59,7 +58,7 @@ func (r *reconciler) pathMappingConfigMapReconciler() reconciling.NamedConfigMap // defaultPathMappings sets up default paths for a front-proxy func (r *reconciler) defaultPathMappings() []operatorv1alpha1.PathMappingEntry { - url := resources.GetRootShardBaseURL(r.rootShard) + url := r.names.RootShardBaseURL(r.rootShard) // Determine CA path based on CABundleSecretRef backendCA := kcpBasepath + "/tls/ca/tls.crt" diff --git a/internal/resources/frontproxy/configmap_test.go b/internal/resources/frontproxy/configmap_test.go index 55af6750..d1584210 100644 --- a/internal/resources/frontproxy/configmap_test.go +++ b/internal/resources/frontproxy/configmap_test.go @@ -24,6 +24,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -81,9 +82,11 @@ func TestDefaultPathMappings(t *testing.T) { }, } + version1 := naming.NewVersion1() + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - rec := NewFrontProxy(tt.frontProxy, tt.rootShard) + rec := NewFrontProxy(tt.frontProxy, tt.rootShard, version1) mappings := rec.defaultPathMappings() require.Len(t, mappings, 2) diff --git a/internal/resources/frontproxy/deployment.go b/internal/resources/frontproxy/deployment.go index 24668d49..378d732b 100644 --- a/internal/resources/frontproxy/deployment.go +++ b/internal/resources/frontproxy/deployment.go @@ -43,13 +43,13 @@ func (r *reconciler) deploymentReconciler() reconciling.NamedDeploymentReconcile ) if r.frontProxy != nil { - name = resources.GetFrontProxyDeploymentName(r.frontProxy) + name = r.names.FrontProxyDeploymentName(r.frontProxy) imageSpec = r.frontProxy.Spec.Image depResources = r.frontProxy.Spec.Resources template = r.frontProxy.Spec.DeploymentTemplate replicas = r.frontProxy.Spec.Replicas } else { - name = resources.GetRootShardProxyDeploymentName(r.rootShard) + name = r.names.RootShardProxyDeploymentName(r.rootShard) if r.rootShard.Spec.Proxy != nil { imageSpec = r.rootShard.Spec.Proxy.Image @@ -149,9 +149,9 @@ func (r *reconciler) deploymentReconciler() reconciling.NamedDeploymentReconcile { var secretName string if r.frontProxy != nil { - secretName = resources.GetFrontProxyDynamicKubeconfigName(r.rootShard, r.frontProxy) + secretName = r.names.FrontProxyDynamicKubeconfigName(r.rootShard, r.frontProxy) } else { - secretName = resources.GetRootShardProxyDynamicKubeconfigName(r.rootShard) + secretName = r.names.RootShardProxyDynamicKubeconfigName(r.rootShard) } // readonly=false because front-proxy updates the file to work with different shards @@ -168,7 +168,7 @@ func (r *reconciler) deploymentReconciler() reconciling.NamedDeploymentReconcile mountSecret(r.certName(operatorv1alpha1.RequestHeaderClientCertificate), frontProxyBasepath+"/requestheader-client", true) // kcp rootshard root ca - mountSecret(resources.GetRootShardCAName(r.rootShard, operatorv1alpha1.RootCA), kcpBasepath+"/tls/ca", true) + mountSecret(r.names.RootShardCAName(r.rootShard, operatorv1alpha1.RootCA), kcpBasepath+"/tls/ca", true) // If caBundleSecretRef is specified, mount the merged CA bundle secret. // This secret contains both kcp root CA and user-provided CA bundle merged together. @@ -216,7 +216,7 @@ func (r *reconciler) deploymentReconciler() reconciling.NamedDeploymentReconcile dep = utils.ApplyDeploymentTemplate(dep, template) if r.frontProxy != nil { - dep = utils.ApplyFrontProxyAuthConfiguration(dep, r.frontProxy.Spec.Auth, r.rootShard) + dep = utils.ApplyFrontProxyAuthConfiguration(dep, r.frontProxy.Spec.Auth, r.rootShard, r.names) // If frontproxy has bundle annotation, store desired replicas in annotation then scale deployment to 0 locally if r.frontProxy.Annotations != nil && r.frontProxy.Annotations[resources.BundleAnnotation] != "" { diff --git a/internal/resources/frontproxy/deployment_test.go b/internal/resources/frontproxy/deployment_test.go index 86f10c79..839cbe62 100644 --- a/internal/resources/frontproxy/deployment_test.go +++ b/internal/resources/frontproxy/deployment_test.go @@ -28,11 +28,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) func TestDeploymentReconciler(t *testing.T) { + version1 := naming.NewVersion1() tests := []struct { name string frontProxy *operatorv1alpha1.FrontProxy @@ -59,7 +60,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -77,13 +78,13 @@ func TestDeploymentReconciler(t *testing.T) { } expectedMounts := []string{ - resources.GetFrontProxyDynamicKubeconfigName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}), - resources.GetFrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.KubeconfigCertificate), - resources.GetFrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.ServerCertificate), - resources.GetFrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.RequestHeaderClientCertificate), - resources.GetFrontProxyConfigName(&operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}), - resources.GetMergedClientCAName("test-front-proxy"), - resources.GetRootShardCAName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.RootCA), + version1.FrontProxyDynamicKubeconfigName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}), + version1.FrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.KubeconfigCertificate), + version1.FrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.ServerCertificate), + version1.FrontProxyCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, &operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}, operatorv1alpha1.RequestHeaderClientCertificate), + version1.FrontProxyConfigName(&operatorv1alpha1.FrontProxy{ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}}), + version1.MergedClientCAName("test-front-proxy"), + version1.RootShardCAName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.RootCA), } for _, expectedMount := range expectedMounts { @@ -121,7 +122,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -151,7 +152,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -181,7 +182,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -228,7 +229,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -280,7 +281,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -337,7 +338,7 @@ func TestDeploymentReconciler(t *testing.T) { }, }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -369,10 +370,10 @@ func TestDeploymentReconciler(t *testing.T) { foundShard2Volume := false for _, volume := range dep.Spec.Template.Spec.Volumes { - if volume.Name == resources.GetRootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) { + if volume.Name == version1.RootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) { foundShard1Volume = true } - if volume.Name == resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "test-shard-2"}}, operatorv1alpha1.ServiceAccountCertificate) { + if volume.Name == version1.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "test-shard-2"}}, operatorv1alpha1.ServiceAccountCertificate) { foundShard2Volume = true } } @@ -384,10 +385,10 @@ func TestDeploymentReconciler(t *testing.T) { foundShard2VolumeMount := false for _, volumeMount := range container.VolumeMounts { - if volumeMount.Name == resources.GetRootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) { + if volumeMount.Name == version1.RootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) { foundShard1VolumeMount = true } - if volumeMount.Name == resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "test-shard-2"}}, operatorv1alpha1.ServiceAccountCertificate) { + if volumeMount.Name == version1.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "test-shard-2"}}, operatorv1alpha1.ServiceAccountCertificate) { foundShard2VolumeMount = true } } @@ -418,7 +419,7 @@ func TestDeploymentReconciler(t *testing.T) { Name: "test-root-shard", }, }, - expectedName: resources.GetFrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ + expectedName: version1.FrontProxyDeploymentName(&operatorv1alpha1.FrontProxy{ ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, }), validateDeploy: func(t *testing.T, dep *appsv1.Deployment) { @@ -454,7 +455,7 @@ func TestDeploymentReconciler(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - fpReconciler := NewFrontProxy(tt.frontProxy, tt.rootShard) + fpReconciler := NewFrontProxy(tt.frontProxy, tt.rootShard, version1) name, reconcilerFunc := fpReconciler.deploymentReconciler()() assert.Equal(t, tt.expectedName, name) @@ -566,11 +567,14 @@ func TestGetArgs(t *testing.T) { }, } + version1 := naming.NewVersion1() + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rec := NewFrontProxy( &operatorv1alpha1.FrontProxy{Spec: *tt.spec}, &operatorv1alpha1.RootShard{}, + version1, ) result := rec.getArgs() diff --git a/internal/resources/frontproxy/reconciler.go b/internal/resources/frontproxy/reconciler.go index 10dc0c44..65072698 100644 --- a/internal/resources/frontproxy/reconciler.go +++ b/internal/resources/frontproxy/reconciler.go @@ -29,7 +29,8 @@ import ( "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/reconciling/modifier" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/bundling" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -37,9 +38,10 @@ type reconciler struct { frontProxy *operatorv1alpha1.FrontProxy rootShard *operatorv1alpha1.RootShard resourceLabels map[string]string + names naming.Scheme } -func NewFrontProxy(frontProxy *operatorv1alpha1.FrontProxy, rootShard *operatorv1alpha1.RootShard) *reconciler { +func NewFrontProxy(frontProxy *operatorv1alpha1.FrontProxy, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) *reconciler { if frontProxy == nil { panic("Use NewRootShardProxy instead.") } @@ -47,14 +49,16 @@ func NewFrontProxy(frontProxy *operatorv1alpha1.FrontProxy, rootShard *operatorv return &reconciler{ frontProxy: frontProxy, rootShard: rootShard, - resourceLabels: resources.GetFrontProxyResourceLabels(frontProxy), + resourceLabels: names.FrontProxyResourceLabels(frontProxy), + names: names, } } -func NewRootShardProxy(rootShard *operatorv1alpha1.RootShard) *reconciler { +func NewRootShardProxy(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) *reconciler { return &reconciler{ rootShard: rootShard, - resourceLabels: resources.GetRootShardProxyResourceLabels(rootShard), + resourceLabels: names.RootShardProxyResourceLabels(rootShard), + names: names, } } @@ -139,3 +143,31 @@ func (r *reconciler) Reconcile(ctx context.Context, client ctrlruntimeclient.Cli return kerrors.NewAggregate(errs) } + +func (r *reconciler) Bundle() []operatorv1alpha1.BundleObject { + namespace := r.frontProxy.Namespace + + objects := []operatorv1alpha1.BundleObject{ + // CA certificates from RootShard (shared) (Secret names are identical to Cert names) + bundling.NewSecret(r.names.RootShardCAName(r.rootShard, operatorv1alpha1.FrontProxyClientCA), namespace), + bundling.NewSecret(r.names.RootShardCAName(r.rootShard, operatorv1alpha1.RequestHeaderClientCA), namespace), + bundling.NewSecret(r.names.RootShardCAName(r.rootShard, operatorv1alpha1.ServerCA), namespace), + bundling.NewSecret(r.names.RootShardCAName(r.rootShard, operatorv1alpha1.RootCA), namespace), + + // FrontProxy-specific certificates + bundling.NewSecret(r.certName(operatorv1alpha1.ServerCertificate), namespace), + bundling.NewSecret(r.certName(operatorv1alpha1.AdminKubeconfigClientCertificate), namespace), + bundling.NewSecret(r.certName(operatorv1alpha1.KubeconfigCertificate), namespace), + bundling.NewSecret(r.certName(operatorv1alpha1.RequestHeaderClientCertificate), namespace), + + // FrontProxy configuration and service + bundling.NewSecret(r.names.FrontProxyDynamicKubeconfigName(r.rootShard, r.frontProxy), namespace), + bundling.NewConfigMap(r.pathMappingConfigMapName(), namespace), + bundling.NewService(r.names.FrontProxyServiceName(r.frontProxy), namespace), + + // FrontProxy deployment + bundling.NewDeployment(r.names.FrontProxyDeploymentName(r.frontProxy), namespace), + } + + return objects +} diff --git a/internal/resources/frontproxy/secrets.go b/internal/resources/frontproxy/secrets.go index 5d945ec0..2828501f 100644 --- a/internal/resources/frontproxy/secrets.go +++ b/internal/resources/frontproxy/secrets.go @@ -22,8 +22,6 @@ import ( corev1 "k8s.io/api/core/v1" clientcmdv1 "k8s.io/client-go/tools/clientcmd/api/v1" "sigs.k8s.io/yaml" - - "github.com/kcp-dev/kcp-operator/internal/resources" ) const ( @@ -33,16 +31,17 @@ const ( kubeconfigCAPath = "/etc/kcp/tls/ca/tls.crt" ) -func (r *reconciler) dynamicKubeconfigSecretReconciler() reconciling.NamedSecretReconcilerFactory { - var name string +func (r *reconciler) dynamicKubeconfigSecretName() string { if r.frontProxy != nil { - name = resources.GetFrontProxyDynamicKubeconfigName(r.rootShard, r.frontProxy) + return r.names.FrontProxyDynamicKubeconfigName(r.rootShard, r.frontProxy) } else { - name = resources.GetRootShardProxyDynamicKubeconfigName(r.rootShard) + return r.names.RootShardProxyDynamicKubeconfigName(r.rootShard) } +} +func (r *reconciler) dynamicKubeconfigSecretReconciler() reconciling.NamedSecretReconcilerFactory { return func() (string, reconciling.SecretReconciler) { - return name, func(obj *corev1.Secret) (*corev1.Secret, error) { + return r.dynamicKubeconfigSecretName(), func(obj *corev1.Secret) (*corev1.Secret, error) { obj.SetLabels(r.resourceLabels) kubeconfig, err := r.dynamicKubeconfig() @@ -66,7 +65,7 @@ func (r *reconciler) dynamicKubeconfig() ([]byte, error) { Name: "system:admin", Cluster: clientcmdv1.Cluster{ CertificateAuthority: kubeconfigCAPath, - Server: resources.GetRootShardBaseURL(r.rootShard), + Server: r.names.RootShardBaseURL(r.rootShard), }, }, }, diff --git a/internal/resources/frontproxy/service.go b/internal/resources/frontproxy/service.go index b645de49..2d36c966 100644 --- a/internal/resources/frontproxy/service.go +++ b/internal/resources/frontproxy/service.go @@ -23,21 +23,33 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "github.com/kcp-dev/kcp-operator/internal/resources" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) func (r *reconciler) serviceName() string { if r.frontProxy != nil { - return resources.GetFrontProxyServiceName(r.frontProxy) + return r.names.FrontProxyServiceName(r.frontProxy) } else { - return resources.GetRootShardProxyServiceName(r.rootShard) + return r.names.RootShardProxyServiceName(r.rootShard) } } +func (r *reconciler) getExternalPort() int { + // We're reconciling a regular FrontProxy. + if r.frontProxy != nil { + return utils.GetFrontProxyExternalPort(r.frontProxy, r.rootShard) + } + + // We're reconciling the rootshard's internal proxy. + // This proxy is not meant to be exposed, but only used internally by + // the kcp-operator, so we never allow to customize its port. + return 6443 +} + func (r *reconciler) serviceReconciler() reconciling.NamedServiceReconcilerFactory { var tpl *operatorv1alpha1.ServiceTemplate + switch { case r.frontProxy != nil: tpl = r.frontProxy.Spec.ServiceTemplate @@ -45,6 +57,8 @@ func (r *reconciler) serviceReconciler() reconciling.NamedServiceReconcilerFacto tpl = r.rootShard.Spec.Proxy.ServiceTemplate } + portNumber := r.getExternalPort() + return func() (string, reconciling.ServiceReconciler) { return r.serviceName(), func(svc *corev1.Service) (*corev1.Service, error) { svc.SetLabels(r.resourceLabels) @@ -57,7 +71,7 @@ func (r *reconciler) serviceReconciler() reconciling.NamedServiceReconcilerFacto port.Name = "https" port.Protocol = corev1.ProtocolTCP - port.Port = 6443 + port.Port = int32(portNumber) port.TargetPort = intstr.FromInt32(6443) port.AppProtocol = ptr.To("https") diff --git a/internal/resources/frontproxy/service_test.go b/internal/resources/frontproxy/service_test.go new file mode 100644 index 00000000..65bb9f0d --- /dev/null +++ b/internal/resources/frontproxy/service_test.go @@ -0,0 +1,226 @@ +/* +Copyright 2026 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 frontproxy + +import ( + "testing" + + "github.com/stretchr/testify/require" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kcp-dev/kcp-operator/internal/resources/naming" + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +func TestGetExternalPort(t *testing.T) { + tests := []struct { + name string + frontProxy *operatorv1alpha1.FrontProxy + rootShard *operatorv1alpha1.RootShard + expectedPort int + }{ + { + name: "FrontProxy with explicit External.Port", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + External: operatorv1alpha1.ExternalConfig{ + Port: 8443, + }, + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 6443, + }, + }, + }, + expectedPort: 8443, + }, + { + name: "FrontProxy with deprecated ExternalHostname including port", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + ExternalHostname: "kcp.example.com:9443", + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 6443, + }, + }, + }, + expectedPort: 9443, + }, + { + name: "FrontProxy External.Port takes precedence over deprecated ExternalHostname", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + External: operatorv1alpha1.ExternalConfig{ + Port: 8443, + }, + ExternalHostname: "kcp.example.com:9443", + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 6443, + }, + }, + }, + expectedPort: 8443, + }, + { + name: "FrontProxy falls back to RootShard External.Port", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 7443, + }, + }, + }, + expectedPort: 7443, + }, + { + name: "FrontProxy with ExternalHostname without a port defaults to 6443", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + ExternalHostname: "kcp.example.com", + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 7443, + }, + }, + }, + expectedPort: 6443, + }, + { + name: "FrontProxy with non-numeric port in ExternalHostname defaults to 6443", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + ExternalHostname: "kcp.example.com:invalid", + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 7443, + }, + }, + }, + expectedPort: 6443, + }, + { + name: "FrontProxy with no port configuration uses default 6443", + frontProxy: &operatorv1alpha1.FrontProxy{ + ObjectMeta: metav1.ObjectMeta{Name: "test-front-proxy"}, + Spec: operatorv1alpha1.FrontProxySpec{ + RootShard: operatorv1alpha1.RootShardConfig{ + Reference: &corev1.LocalObjectReference{Name: "test-root-shard"}, + }, + }, + }, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 0, + }, + }, + }, + expectedPort: 6443, + }, + { + name: "RootShard internal proxy always uses default 6443", + frontProxy: nil, + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}, + Spec: operatorv1alpha1.RootShardSpec{ + External: operatorv1alpha1.ExternalConfig{ + Hostname: "kcp.example.com", + Port: 8443, + }, + }, + }, + expectedPort: 6443, + }, + } + + namingScheme := naming.NewVersion1() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var rec *reconciler + if tt.frontProxy != nil { + rec = NewFrontProxy(tt.frontProxy, tt.rootShard, namingScheme) + } else { + rec = NewRootShardProxy(tt.rootShard, namingScheme) + } + + actualPort := rec.getExternalPort() + require.Equal(t, tt.expectedPort, actualPort) + }) + } +} diff --git a/internal/resources/kubeconfig/certificate.go b/internal/resources/kubeconfig/certificate.go index 65545d86..d0ec6ed8 100644 --- a/internal/resources/kubeconfig/certificate.go +++ b/internal/resources/kubeconfig/certificate.go @@ -24,11 +24,12 @@ import ( "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func ClientCertificateReconciler(kubeConfig *operatorv1alpha1.Kubeconfig, issuerName string) reconciling.NamedCertificateReconcilerFactory { +func ClientCertificateReconciler(kubeConfig *operatorv1alpha1.Kubeconfig, issuerName string, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { orgs := sets.New(kubeConfig.Spec.Groups...) orgs.Insert(KubeconfigGroup(kubeConfig)) @@ -58,7 +59,7 @@ func ClientCertificateReconciler(kubeConfig *operatorv1alpha1.Kubeconfig, issuer Organizations: sets.List(orgs), }, - IssuerRef: certmanagermetav1.ObjectReference{ + IssuerRef: certmanagermetav1.IssuerReference{ Name: issuerName, Kind: "Issuer", Group: "cert-manager.io", diff --git a/internal/resources/kubeconfig/rbac.go b/internal/resources/kubeconfig/rbac.go index e957de6c..f3f3e2d3 100644 --- a/internal/resources/kubeconfig/rbac.go +++ b/internal/resources/kubeconfig/rbac.go @@ -24,12 +24,13 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "github.com/kcp-dev/kcp-operator/internal/kubernetes" + "github.com/kcp-dev/kcp-operator/internal/resources" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) func OwnerLabels(owner *operatorv1alpha1.Kubeconfig) map[string]string { return map[string]string{ - "operator.kcp.io/kubeconfig": string(owner.UID), + resources.KubeconfigUIDLabel: string(owner.UID), } } diff --git a/internal/resources/kubeconfig/secret.go b/internal/resources/kubeconfig/secret.go index 906de270..08ee2601 100644 --- a/internal/resources/kubeconfig/secret.go +++ b/internal/resources/kubeconfig/secret.go @@ -18,6 +18,7 @@ package kubeconfig import ( "fmt" + "net" "net/url" "k8c.io/reconciler/pkg/reconciling" @@ -26,7 +27,7 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -45,6 +46,7 @@ func KubeconfigSecretReconciler( caSecret *corev1.Secret, certSecret *corev1.Secret, caBundle *corev1.Secret, // can be nil + names naming.Scheme, ) (reconciling.NamedSecretReconcilerFactory, error) { if caBundle != nil && caBundle.Data["tls.crt"] == nil { return nil, fmt.Errorf("the CA bundle secret %s/%s does not contain a `tls.crt` key", caBundle.Namespace, caBundle.Name) @@ -85,7 +87,7 @@ func KubeconfigSecretReconciler( panic("RootShard must be provided when kubeconfig targets one.") } - serverURL := resources.GetRootShardBaseURL(rootShard) + serverURL := names.RootShardBaseURL(rootShard) defaultURL, err := url.JoinPath(serverURL, "clusters", "root") if err != nil { return nil, err @@ -103,7 +105,7 @@ func KubeconfigSecretReconciler( panic("Shard must be provided when kubeconfig targets one.") } - serverURL := resources.GetShardBaseURL(shard) + serverURL := names.ShardBaseURL(shard) defaultURL, err := url.JoinPath(serverURL, "clusters", "root") if err != nil { return nil, err @@ -122,11 +124,17 @@ func KubeconfigSecretReconciler( } var serverURL string - // New flow: - if frontProxy.Spec.External.Hostname != "" { + switch { + case frontProxy.Spec.External.Hostname != "": serverURL = fmt.Sprintf("https://%s:%d", frontProxy.Spec.External.Hostname, frontProxy.Spec.External.Port) - } else { - // Old flow: + case frontProxy.Spec.ExternalHostname != "": + _, _, err := net.SplitHostPort(frontProxy.Spec.ExternalHostname) + if err == nil { + serverURL = fmt.Sprintf("https://%s", frontProxy.Spec.ExternalHostname) + } else { + serverURL = fmt.Sprintf("https://%s:6443", frontProxy.Spec.ExternalHostname) + } + default: serverURL = fmt.Sprintf("https://%s:%d", rootShard.Spec.External.Hostname, rootShard.Spec.External.Port) } diff --git a/internal/resources/naming/scheme.go b/internal/resources/naming/scheme.go new file mode 100644 index 00000000..e1c5dad3 --- /dev/null +++ b/internal/resources/naming/scheme.go @@ -0,0 +1,102 @@ +/* +Copyright 2026 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 naming + +import ( + "fmt" + + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +const ( + appNameLabel = "app.kubernetes.io/name" + appInstanceLabel = "app.kubernetes.io/instance" + appManagedByLabel = "app.kubernetes.io/managed-by" + appComponentLabel = "app.kubernetes.io/component" + + defaultClusterDomain = "cluster.local" +) + +type Scheme interface { + // RootShard naming + RootShardDeploymentName(r *operatorv1alpha1.RootShard) string + RootShardProxyDeploymentName(r *operatorv1alpha1.RootShard) string + RootShardServiceName(r *operatorv1alpha1.RootShard) string + RootShardResourceLabels(r *operatorv1alpha1.RootShard) map[string]string + RootShardProxyResourceLabels(r *operatorv1alpha1.RootShard) map[string]string + RootShardBaseHost(r *operatorv1alpha1.RootShard) string + RootShardBaseURL(r *operatorv1alpha1.RootShard) string + RootShardCertificateName(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string + RootShardProxyCertificateName(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string + RootShardCAName(r *operatorv1alpha1.RootShard, ca operatorv1alpha1.CA) string + RootShardProxyDynamicKubeconfigName(r *operatorv1alpha1.RootShard) string + RootShardProxyConfigName(r *operatorv1alpha1.RootShard) string + RootShardProxyServiceName(r *operatorv1alpha1.RootShard) string + RootShardProxyBaseHost(r *operatorv1alpha1.RootShard) string + RootShardKubeconfigSecret(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string + + // Shard naming + ShardDeploymentName(s *operatorv1alpha1.Shard) string + ShardServiceName(s *operatorv1alpha1.Shard) string + ShardResourceLabels(s *operatorv1alpha1.Shard) map[string]string + ShardBaseHost(s *operatorv1alpha1.Shard) string + ShardBaseURL(s *operatorv1alpha1.Shard) string + ShardCertificateName(s *operatorv1alpha1.Shard, cert operatorv1alpha1.Certificate) string + ShardKubeconfigSecret(s *operatorv1alpha1.Shard, cert operatorv1alpha1.Certificate) string + + // CacheServer naming + CacheServerDeploymentName(c *operatorv1alpha1.CacheServer) string + CacheServerServiceName(c *operatorv1alpha1.CacheServer) string + CacheServerResourceLabels(c *operatorv1alpha1.CacheServer) map[string]string + CacheServerBaseHost(c *operatorv1alpha1.CacheServer) string + CacheServerBaseURL(c *operatorv1alpha1.CacheServer) string + CacheServerCertificateName(c *operatorv1alpha1.CacheServer, cert operatorv1alpha1.Certificate) string + CacheServerCAName(cacheServerName string, ca operatorv1alpha1.CA) string + // deprecated, use CacheServerCertificateName instead + CacheServerClientCertificateName(cacheServerName string) string + CacheServerKubeconfigName(cacheServerName string) string + + // VirtualWorkspace naming + VirtualWorkspaceDeploymentName(vw *operatorv1alpha1.VirtualWorkspace) string + VirtualWorkspaceServiceName(vw *operatorv1alpha1.VirtualWorkspace) string + VirtualWorkspaceResourceLabels(vw *operatorv1alpha1.VirtualWorkspace) map[string]string + VirtualWorkspaceBaseHost(vw *operatorv1alpha1.VirtualWorkspace) string + VirtualWorkspaceBaseURL(vw *operatorv1alpha1.VirtualWorkspace) string + VirtualWorkspaceCertificateName(vw *operatorv1alpha1.VirtualWorkspace, cert operatorv1alpha1.Certificate) string + + // FrontProxy naming + FrontProxyResourceLabels(fp *operatorv1alpha1.FrontProxy) map[string]string + FrontProxyDeploymentName(fp *operatorv1alpha1.FrontProxy) string + FrontProxyCertificateName(r *operatorv1alpha1.RootShard, fp *operatorv1alpha1.FrontProxy, cert operatorv1alpha1.Certificate) string + FrontProxyDynamicKubeconfigName(r *operatorv1alpha1.RootShard, fp *operatorv1alpha1.FrontProxy) string + FrontProxyConfigName(fp *operatorv1alpha1.FrontProxy) string + FrontProxyServiceName(fp *operatorv1alpha1.FrontProxy) string + FrontProxyBaseHost(fp *operatorv1alpha1.FrontProxy, r *operatorv1alpha1.RootShard) string + + // Bundle naming + BundleName(ownerName string) string + MergedCABundleName(ownerName string) string + MergedClientCAName(ownerName string) string +} + +func fqService(svcName, namespace, clusterDomain string) string { + if clusterDomain == "" { + clusterDomain = defaultClusterDomain + } + + return fmt.Sprintf("%s.%s.svc.%s", svcName, namespace, clusterDomain) +} diff --git a/internal/resources/naming/v1.go b/internal/resources/naming/v1.go new file mode 100644 index 00000000..32088334 --- /dev/null +++ b/internal/resources/naming/v1.go @@ -0,0 +1,251 @@ +/* +Copyright 2026 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 naming + +import ( + "fmt" + + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +type version1 struct{} + +func NewVersion1() Scheme { + return &version1{} +} + +func (v *version1) getResourceLabels(instance, component string) map[string]string { + return map[string]string{ + appManagedByLabel: "kcp-operator", + appNameLabel: "kcp", + appInstanceLabel: instance, + appComponentLabel: component, + } +} + +// RootShard naming + +func (v *version1) RootShardDeploymentName(r *operatorv1alpha1.RootShard) string { + return fmt.Sprintf("%s-kcp", r.Name) +} + +func (v *version1) RootShardProxyDeploymentName(r *operatorv1alpha1.RootShard) string { + return fmt.Sprintf("%s-proxy", r.Name) +} + +func (v *version1) RootShardServiceName(r *operatorv1alpha1.RootShard) string { + return fmt.Sprintf("%s-kcp", r.Name) +} + +func (v *version1) RootShardResourceLabels(r *operatorv1alpha1.RootShard) map[string]string { + return v.getResourceLabels(r.Name, "rootshard") +} + +func (v *version1) RootShardProxyResourceLabels(r *operatorv1alpha1.RootShard) map[string]string { + return v.getResourceLabels(r.Name, "rootshard-proxy") +} + +func (v *version1) RootShardBaseHost(r *operatorv1alpha1.RootShard) string { + return fqService(v.RootShardServiceName(r), r.Namespace, r.Spec.ClusterDomain) +} + +func (v *version1) RootShardBaseURL(r *operatorv1alpha1.RootShard) string { + if r.Spec.ShardBaseURL != "" { + return r.Spec.ShardBaseURL + } + return fmt.Sprintf("https://%s:6443", v.RootShardBaseHost(r)) +} + +func (v *version1) RootShardCertificateName(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s", r.Name, cert) +} + +func (v *version1) RootShardProxyCertificateName(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-proxy-%s", r.Name, cert) +} + +func (v *version1) RootShardCAName(r *operatorv1alpha1.RootShard, ca operatorv1alpha1.CA) string { + if ca == operatorv1alpha1.RootCA { + return fmt.Sprintf("%s-ca", r.Name) + } + return fmt.Sprintf("%s-%s-ca", r.Name, ca) +} + +func (v *version1) RootShardProxyDynamicKubeconfigName(r *operatorv1alpha1.RootShard) string { + return fmt.Sprintf("%s-proxy-dynamic-kubeconfig", r.Name) +} + +func (v *version1) RootShardProxyConfigName(r *operatorv1alpha1.RootShard) string { + return fmt.Sprintf("%s-proxy-config", r.Name) +} + +func (v *version1) RootShardProxyServiceName(r *operatorv1alpha1.RootShard) string { + return fmt.Sprintf("%s-proxy", r.Name) +} + +func (v *version1) RootShardProxyBaseHost(r *operatorv1alpha1.RootShard) string { + return fqService(v.RootShardProxyServiceName(r), r.Namespace, r.Spec.ClusterDomain) +} + +func (v *version1) RootShardKubeconfigSecret(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s-kubeconfig", r.Name, cert) +} + +// Shard naming + +func (v *version1) ShardDeploymentName(s *operatorv1alpha1.Shard) string { + return fmt.Sprintf("%s-shard-kcp", s.Name) +} + +func (v *version1) ShardServiceName(s *operatorv1alpha1.Shard) string { + return fmt.Sprintf("%s-shard-kcp", s.Name) +} + +func (v *version1) ShardResourceLabels(s *operatorv1alpha1.Shard) map[string]string { + return v.getResourceLabels(s.Name, "shard") +} + +func (v *version1) ShardBaseHost(s *operatorv1alpha1.Shard) string { + return fqService(v.ShardServiceName(s), s.Namespace, s.Spec.ClusterDomain) +} + +func (v *version1) ShardBaseURL(s *operatorv1alpha1.Shard) string { + if s.Spec.ShardBaseURL != "" { + return s.Spec.ShardBaseURL + } + return fmt.Sprintf("https://%s:6443", v.ShardBaseHost(s)) +} + +func (v *version1) ShardCertificateName(s *operatorv1alpha1.Shard, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s", s.Name, cert) +} + +func (v *version1) ShardKubeconfigSecret(s *operatorv1alpha1.Shard, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s-kubeconfig", s.Name, cert) +} + +// CacheServer naming + +func (v *version1) CacheServerDeploymentName(c *operatorv1alpha1.CacheServer) string { + return fmt.Sprintf("%s-cache-server", c.Name) +} + +func (v *version1) CacheServerServiceName(c *operatorv1alpha1.CacheServer) string { + return fmt.Sprintf("%s-cache-server", c.Name) +} + +func (v *version1) CacheServerResourceLabels(c *operatorv1alpha1.CacheServer) map[string]string { + return v.getResourceLabels(c.Name, "cache-server") +} + +func (v *version1) CacheServerBaseHost(c *operatorv1alpha1.CacheServer) string { + return fqService(v.CacheServerServiceName(c), c.Namespace, c.Spec.ClusterDomain) +} + +func (v *version1) CacheServerBaseURL(c *operatorv1alpha1.CacheServer) string { + return fmt.Sprintf("https://%s:6443", v.CacheServerBaseHost(c)) +} + +func (v *version1) CacheServerCertificateName(c *operatorv1alpha1.CacheServer, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s", c.Name, cert) +} + +func (v *version1) CacheServerCAName(cacheServerName string, ca operatorv1alpha1.CA) string { + if ca == operatorv1alpha1.RootCA { + return fmt.Sprintf("%s-ca", cacheServerName) + } + return fmt.Sprintf("%s-%s-ca", cacheServerName, ca) +} + +func (v *version1) CacheServerClientCertificateName(cacheServerName string) string { + // This being a different naming scheme than CacheServerCertificateName() is fixed in v2. + return fmt.Sprintf("%s-client-certificate", cacheServerName) +} + +func (v *version1) CacheServerKubeconfigName(cacheServerName string) string { + return fmt.Sprintf("%s-kubeconfig", cacheServerName) +} + +// VirtualWorkspace naming + +func (v *version1) VirtualWorkspaceDeploymentName(vw *operatorv1alpha1.VirtualWorkspace) string { + return fmt.Sprintf("%s-virtual-workspace", vw.Name) +} + +func (v *version1) VirtualWorkspaceServiceName(vw *operatorv1alpha1.VirtualWorkspace) string { + return fmt.Sprintf("%s-virtual-workspace", vw.Name) +} + +func (v *version1) VirtualWorkspaceResourceLabels(vw *operatorv1alpha1.VirtualWorkspace) map[string]string { + return v.getResourceLabels(vw.Name, "virtual-workspace") +} + +func (v *version1) VirtualWorkspaceBaseHost(vw *operatorv1alpha1.VirtualWorkspace) string { + return fqService(v.VirtualWorkspaceServiceName(vw), vw.Namespace, vw.Spec.ClusterDomain) +} + +func (v *version1) VirtualWorkspaceBaseURL(vw *operatorv1alpha1.VirtualWorkspace) string { + return fmt.Sprintf("https://%s:6443", v.VirtualWorkspaceBaseHost(vw)) +} + +func (v *version1) VirtualWorkspaceCertificateName(vw *operatorv1alpha1.VirtualWorkspace, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s", vw.Name, cert) +} + +// FrontProxy naming + +func (v *version1) FrontProxyResourceLabels(fp *operatorv1alpha1.FrontProxy) map[string]string { + return v.getResourceLabels(fp.Name, "front-proxy") +} + +func (v *version1) FrontProxyDeploymentName(fp *operatorv1alpha1.FrontProxy) string { + return fmt.Sprintf("%s-front-proxy", fp.Name) +} + +func (v *version1) FrontProxyCertificateName(r *operatorv1alpha1.RootShard, fp *operatorv1alpha1.FrontProxy, cert operatorv1alpha1.Certificate) string { + return fmt.Sprintf("%s-%s-%s", r.Name, fp.Name, cert) +} + +func (v *version1) FrontProxyDynamicKubeconfigName(r *operatorv1alpha1.RootShard, fp *operatorv1alpha1.FrontProxy) string { + return fmt.Sprintf("%s-%s-dynamic-kubeconfig", r.Name, fp.Name) +} + +func (v *version1) FrontProxyConfigName(fp *operatorv1alpha1.FrontProxy) string { + return fmt.Sprintf("%s-config", fp.Name) +} + +func (v *version1) FrontProxyServiceName(fp *operatorv1alpha1.FrontProxy) string { + return fmt.Sprintf("%s-front-proxy", fp.Name) +} + +func (v *version1) FrontProxyBaseHost(fp *operatorv1alpha1.FrontProxy, r *operatorv1alpha1.RootShard) string { + return fqService(v.FrontProxyServiceName(fp), fp.Namespace, r.Spec.ClusterDomain) +} + +// Bundle naming + +func (v *version1) BundleName(ownerName string) string { + return fmt.Sprintf("%s-bundle", ownerName) +} + +func (v *version1) MergedCABundleName(ownerName string) string { + return fmt.Sprintf("%s-merged-ca-bundle", ownerName) +} + +func (v *version1) MergedClientCAName(ownerName string) string { + return fmt.Sprintf("%s-merged-client-ca", ownerName) +} diff --git a/internal/resources/resources.go b/internal/resources/resources.go index 24cfe15b..9e18f6a8 100644 --- a/internal/resources/resources.go +++ b/internal/resources/resources.go @@ -27,6 +27,12 @@ import ( ) const ( + // FrontProxyCommonName is the CommonName used in the requestheader client certificate for a FrontProxy. + FrontProxyCommonName = "kcp-front-proxy" + + // RootShardProxyCommonName is the CommonName used in the requestheader client certificate for a RootShard's built-in proxy. + RootShardProxyCommonName = "kcp-root-shard-proxy" + ImageRepository = "ghcr.io/kcp-dev/kcp" // ImageTag is the default tag to be used for any kcp component. @@ -35,11 +41,6 @@ const ( // the .prow.yaml accordingly and shift the jobs. ImageTag = "v0.31.0" - appNameLabel = "app.kubernetes.io/name" - appInstanceLabel = "app.kubernetes.io/instance" - appManagedByLabel = "app.kubernetes.io/managed-by" - appComponentLabel = "app.kubernetes.io/component" - // RootShardLabel is placed on Secrets created for Certificates so that // the Secrets can be more easily mapped to their RootShards. RootShardLabel = "operator.kcp.io/rootshard" @@ -49,6 +50,10 @@ const ( CacheServerLabel = "operator.kcp.io/cache-server" VirtualWorkspaceLabel = "operator.kcp.io/virtual-workspace" + // KubeconfigUIDLabel is used inside kcp workspaces to link RBAC resources + // to their owning Kubeconfig object on the kcp operator cluster. + KubeconfigUIDLabel = "operator.kcp.io/kubeconfig" + // BundleAnnotation is placed on RootShard, Shard, or FrontProxy objects to trigger automatic Bundle creation BundleAnnotation = "operator.kcp.io/bundle" @@ -60,8 +65,6 @@ const ( // the certificate also has system:masters as an organization, which is what ultimately // grants the operator its permissions. OperatorUsername = "system:kcp-operator" - - defaultClusterDomain = "cluster.local" ) func GetImageSettings(imageSpec *operatorv1alpha1.ImageSpec) (string, []corev1.LocalObjectReference, *semver.Version) { @@ -85,216 +88,3 @@ func GetImageSettings(imageSpec *operatorv1alpha1.ImageSpec) (string, []corev1.L return fmt.Sprintf("%s:%s", repository, tag), imagePullSecrets, version } - -func GetRootShardDeploymentName(r *operatorv1alpha1.RootShard) string { - return fmt.Sprintf("%s-kcp", r.Name) -} - -func GetRootShardProxyDeploymentName(r *operatorv1alpha1.RootShard) string { - return fmt.Sprintf("%s-proxy", r.Name) -} - -func GetShardDeploymentName(s *operatorv1alpha1.Shard) string { - return fmt.Sprintf("%s-shard-kcp", s.Name) -} - -func GetCacheServerDeploymentName(s *operatorv1alpha1.CacheServer) string { - return fmt.Sprintf("%s-cache-server", s.Name) -} - -func GetVirtualWorkspaceDeploymentName(vw *operatorv1alpha1.VirtualWorkspace) string { - return fmt.Sprintf("%s-virtual-workspace", vw.Name) -} - -func GetRootShardServiceName(r *operatorv1alpha1.RootShard) string { - return fmt.Sprintf("%s-kcp", r.Name) -} - -func GetShardServiceName(s *operatorv1alpha1.Shard) string { - return fmt.Sprintf("%s-shard-kcp", s.Name) -} - -func GetCacheServerServiceName(s *operatorv1alpha1.CacheServer) string { - return fmt.Sprintf("%s-cache-server", s.Name) -} - -func getResourceLabels(instance, component string) map[string]string { - return map[string]string{ - appManagedByLabel: "kcp-operator", - appNameLabel: "kcp", - appInstanceLabel: instance, - appComponentLabel: component, - } -} - -func GetRootShardResourceLabels(r *operatorv1alpha1.RootShard) map[string]string { - return getResourceLabels(r.Name, "rootshard") -} - -func GetRootShardProxyResourceLabels(r *operatorv1alpha1.RootShard) map[string]string { - return getResourceLabels(r.Name, "rootshard-proxy") -} - -func GetShardResourceLabels(s *operatorv1alpha1.Shard) map[string]string { - return getResourceLabels(s.Name, "shard") -} - -func GetCacheServerResourceLabels(s *operatorv1alpha1.CacheServer) map[string]string { - return getResourceLabels(s.Name, "cache-server") -} - -func GetVirtualWorkspaceResourceLabels(vw *operatorv1alpha1.VirtualWorkspace) map[string]string { - return getResourceLabels(vw.Name, "virtual-workspace") -} - -func GetRootShardBaseHost(r *operatorv1alpha1.RootShard) string { - clusterDomain := r.Spec.ClusterDomain - if clusterDomain == "" { - clusterDomain = defaultClusterDomain - } - - return fmt.Sprintf("%s-kcp.%s.svc.%s", r.Name, r.Namespace, clusterDomain) -} - -func GetRootShardBaseURL(r *operatorv1alpha1.RootShard) string { - if r.Spec.ShardBaseURL != "" { - return r.Spec.ShardBaseURL - } - return fmt.Sprintf("https://%s:6443", GetRootShardBaseHost(r)) -} - -func GetShardBaseHost(s *operatorv1alpha1.Shard) string { - clusterDomain := s.Spec.ClusterDomain - if clusterDomain == "" { - clusterDomain = defaultClusterDomain - } - - return fmt.Sprintf("%s-shard-kcp.%s.svc.%s", s.Name, s.Namespace, clusterDomain) -} - -func GetShardBaseURL(s *operatorv1alpha1.Shard) string { - if s.Spec.ShardBaseURL != "" { - return s.Spec.ShardBaseURL - } - return fmt.Sprintf("https://%s:6443", GetShardBaseHost(s)) -} - -func GetCacheServerBaseHost(s *operatorv1alpha1.CacheServer) string { - clusterDomain := s.Spec.ClusterDomain - if clusterDomain == "" { - clusterDomain = defaultClusterDomain - } - - return fmt.Sprintf("%s-cache-server.%s.svc.%s", s.Name, s.Namespace, clusterDomain) -} - -func GetCacheServerBaseURL(s *operatorv1alpha1.CacheServer) string { - return fmt.Sprintf("https://%s:6443", GetCacheServerBaseHost(s)) -} - -func GetVirtualWorkspaceBaseHost(s *operatorv1alpha1.VirtualWorkspace) string { - clusterDomain := s.Spec.ClusterDomain - if clusterDomain == "" { - clusterDomain = defaultClusterDomain - } - - return fmt.Sprintf("%s-virtual-workspace.%s.svc.%s", s.Name, s.Namespace, clusterDomain) -} - -func GetVirtualWorkspaceBaseURL(s *operatorv1alpha1.VirtualWorkspace) string { - return fmt.Sprintf("https://%s:6443", GetVirtualWorkspaceBaseHost(s)) -} - -func GetRootShardCertificateName(r *operatorv1alpha1.RootShard, certName operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s", r.Name, certName) -} - -func GetRootShardProxyCertificateName(r *operatorv1alpha1.RootShard, certName operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-proxy-%s", r.Name, certName) -} - -func GetShardCertificateName(s *operatorv1alpha1.Shard, certName operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s", s.Name, certName) -} - -func GetCacheServerCertificateName(s *operatorv1alpha1.CacheServer, certName operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s", s.Name, certName) -} - -func GetVirtualWorkspaceCertificateName(vw *operatorv1alpha1.VirtualWorkspace, certName operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s", vw.Name, certName) -} - -func GetRootShardCAName(r *operatorv1alpha1.RootShard, caName operatorv1alpha1.CA) string { - if caName == operatorv1alpha1.RootCA { - return fmt.Sprintf("%s-ca", r.Name) - } - return fmt.Sprintf("%s-%s-ca", r.Name, caName) -} - -func GetCacheServerCAName(cacheServerName string, caName operatorv1alpha1.CA) string { - if caName == operatorv1alpha1.RootCA { - return fmt.Sprintf("%s-ca", cacheServerName) - } - return fmt.Sprintf("%s-%s-ca", cacheServerName, caName) -} - -func GetFrontProxyResourceLabels(f *operatorv1alpha1.FrontProxy) map[string]string { - return getResourceLabels(f.Name, "front-proxy") -} - -func GetFrontProxyDeploymentName(f *operatorv1alpha1.FrontProxy) string { - return fmt.Sprintf("%s-front-proxy", f.Name) -} - -func GetFrontProxyCertificateName(r *operatorv1alpha1.RootShard, f *operatorv1alpha1.FrontProxy, certName operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s-%s", r.Name, f.Name, certName) -} - -func GetRootShardProxyDynamicKubeconfigName(r *operatorv1alpha1.RootShard) string { - return fmt.Sprintf("%s-proxy-dynamic-kubeconfig", r.Name) -} - -func GetFrontProxyDynamicKubeconfigName(r *operatorv1alpha1.RootShard, f *operatorv1alpha1.FrontProxy) string { - return fmt.Sprintf("%s-%s-dynamic-kubeconfig", r.Name, f.Name) -} - -func GetCacheServerClientCertificateName(s *operatorv1alpha1.CacheServer) string { - return fmt.Sprintf("%s-client-certificate", s.Name) -} - -func GetCacheServerKubeconfigName(cacheServerName string) string { - return fmt.Sprintf("%s-kubeconfig", cacheServerName) -} - -func GetRootShardProxyConfigName(r *operatorv1alpha1.RootShard) string { - return fmt.Sprintf("%s-proxy-config", r.Name) -} - -func GetFrontProxyConfigName(f *operatorv1alpha1.FrontProxy) string { - return fmt.Sprintf("%s-config", f.Name) -} - -func GetFrontProxyServiceName(f *operatorv1alpha1.FrontProxy) string { - return fmt.Sprintf("%s-front-proxy", f.Name) -} - -func GetRootShardProxyServiceName(r *operatorv1alpha1.RootShard) string { - return fmt.Sprintf("%s-proxy", r.Name) -} - -func GetRootShardKubeconfigSecret(r *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s-kubeconfig", r.Name, cert) -} - -func GetShardKubeconfigSecret(shard *operatorv1alpha1.Shard, cert operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s-kubeconfig", shard.Name, cert) -} - -func GetBundleName(ownerName string) string { - return fmt.Sprintf("%s-bundle", ownerName) -} - -func GetMergedClientCAName(ownerName string) string { - return fmt.Sprintf("%s-merged-client-ca", ownerName) -} diff --git a/internal/resources/rootshard/ca_bundle.go b/internal/resources/rootshard/ca_bundle.go index 18278fae..e1a51eb2 100644 --- a/internal/resources/rootshard/ca_bundle.go +++ b/internal/resources/rootshard/ca_bundle.go @@ -27,12 +27,13 @@ import ( ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func MergedCABundleSecretReconciler(ctx context.Context, rootShard *operatorv1alpha1.RootShard, kubeClient ctrlruntimeclient.Client) k8creconciling.NamedSecretReconcilerFactory { +func MergedCABundleSecretReconciler(ctx context.Context, rootShard *operatorv1alpha1.RootShard, kubeClient ctrlruntimeclient.Client, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { return func() (string, k8creconciling.SecretReconciler) { - secretName := fmt.Sprintf("%s-merged-ca-bundle", rootShard.Name) + secretName := names.MergedCABundleName(rootShard.Name) return secretName, func(secret *corev1.Secret) (*corev1.Secret, error) { if secret.Data == nil { secret.Data = make(map[string][]byte) @@ -40,7 +41,7 @@ func MergedCABundleSecretReconciler(ctx context.Context, rootShard *operatorv1al // Get ServerCA certificate serverCASecret := &corev1.Secret{} - serverCASecretName := resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA) + serverCASecretName := names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA) err := kubeClient.Get(ctx, types.NamespacedName{ Name: serverCASecretName, Namespace: rootShard.Namespace, diff --git a/internal/resources/rootshard/ca_certificates.go b/internal/resources/rootshard/ca_certificates.go index 18a776ba..1c09ccdb 100644 --- a/internal/resources/rootshard/ca_certificates.go +++ b/internal/resources/rootshard/ca_certificates.go @@ -22,13 +22,14 @@ import ( "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) // RootCACertificateReconciler creates the central CA used for the kcp setup around a specific RootShard. This shouldn't be called if the RootShard is configured to use a BYO CA certificate. -func RootCACertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { - name := resources.GetRootShardCAName(rootShard, operatorv1alpha1.RootCA) +func RootCACertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { + name := names.RootShardCAName(rootShard, operatorv1alpha1.RootCA) template := rootShard.Spec.CertificateTemplates.CATemplate(operatorv1alpha1.RootCA) if rootShard.Spec.Certificates.IssuerRef == nil { @@ -37,7 +38,7 @@ func RootCACertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ IsCA: true, @@ -56,7 +57,7 @@ func RootCACertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil Size: 4096, }, - IssuerRef: certmanagermetav1.ObjectReference{ + IssuerRef: certmanagermetav1.IssuerReference{ Name: rootShard.Spec.Certificates.IssuerRef.Name, Kind: rootShard.Spec.Certificates.IssuerRef.Kind, Group: rootShard.Spec.Certificates.IssuerRef.Group, @@ -68,13 +69,13 @@ func RootCACertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil } } -func CACertificateReconciler(rootShard *operatorv1alpha1.RootShard, ca operatorv1alpha1.CA) reconciling.NamedCertificateReconcilerFactory { - name := resources.GetRootShardCAName(rootShard, ca) +func CACertificateReconciler(rootShard *operatorv1alpha1.RootShard, ca operatorv1alpha1.CA, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { + name := names.RootShardCAName(rootShard, ca) template := rootShard.Spec.CertificateTemplates.CATemplate(ca) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ IsCA: true, CommonName: name, @@ -92,8 +93,8 @@ func CACertificateReconciler(rootShard *operatorv1alpha1.RootShard, ca operatorv Size: 4096, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.RootCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.RootCA), Kind: "Issuer", Group: "cert-manager.io", }, diff --git a/internal/resources/rootshard/certificates.go b/internal/resources/rootshard/certificates.go index b9b8e099..8d3fd8b0 100644 --- a/internal/resources/rootshard/certificates.go +++ b/internal/resources/rootshard/certificates.go @@ -20,21 +20,24 @@ import ( certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" certmanagermetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func ServerCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ServerCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ServerCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -56,13 +59,10 @@ func ServerCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil certmanagerv1.UsageDigitalSignature, }, - DNSNames: []string{ - resources.GetRootShardBaseHost(rootShard), - rootShard.Spec.External.Hostname, - }, + DNSNames: buildRootShardDNSNames(rootShard, names), - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -73,15 +73,15 @@ func ServerCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil } } -func VirtualWorkspacesCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func VirtualWorkspacesCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.VirtualWorkspacesCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -105,8 +105,8 @@ func VirtualWorkspacesCertificateReconciler(rootShard *operatorv1alpha1.RootShar rootShard.Spec.External.Hostname, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -117,15 +117,15 @@ func VirtualWorkspacesCertificateReconciler(rootShard *operatorv1alpha1.RootShar } } -func ServiceAccountCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ServiceAccountCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ServiceAccountCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ CommonName: name, SecretName: name, @@ -147,8 +147,8 @@ func ServiceAccountCertificateReconciler(rootShard *operatorv1alpha1.RootShard) Size: 4096, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServiceAccountCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServiceAccountCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -159,15 +159,15 @@ func ServiceAccountCertificateReconciler(rootShard *operatorv1alpha1.RootShard) } } -func LogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func LogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.LogicalClusterAdminCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ CommonName: "logical-cluster-admin", SecretName: name, @@ -192,8 +192,8 @@ func LogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha1.RootSh certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -204,15 +204,15 @@ func LogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha1.RootSh } } -func ExternalLogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ExternalLogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ExternalLogicalClusterAdminCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ CommonName: "external-logical-cluster-admin", SecretName: name, @@ -237,8 +237,8 @@ func ExternalLogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -249,15 +249,15 @@ func ExternalLogicalClusterAdminCertificateReconciler(rootShard *operatorv1alpha } } -func OperatorClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func OperatorClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.OperatorCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ CommonName: resources.OperatorUsername, SecretName: name, @@ -282,12 +282,12 @@ func OperatorClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard) certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ + IssuerRef: certmanagermetav1.IssuerReference{ // This shard is meant to be used against shards directly (cluster-local) or with // the internal root shard proxy (also cluster-local). All of these are configured // to use the regular client CA (not like "normal" front-proxies, which have their // own CA). - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA), + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -300,15 +300,15 @@ func OperatorClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard) // ClientCertificateReconciler generates a client certificate that is used by the root shard to // connect to an external kcp-virtual-workspaces pod. -func ClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ClientCertificate - name := resources.GetRootShardCertificateName(rootShard, certKind) + name := names.RootShardCertificateName(rootShard, certKind) template := rootShard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + cert.SetLabels(names.RootShardResourceLabels(rootShard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -334,8 +334,8 @@ func ClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -345,3 +345,22 @@ func ClientCertificateReconciler(rootShard *operatorv1alpha1.RootShard) reconcil } } } + +// buildRootShardDNSNames builds the list of DNS names for the root shard server certificate. +// It includes the internal Kubernetes service host, the external hostname, and if shardBaseURL +// is set, it also extracts and includes the hostname from that URL. +func buildRootShardDNSNames(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) []string { + dnsNames := sets.New( + names.RootShardBaseHost(rootShard), + rootShard.Spec.External.Hostname, + ) + + // If shardBaseURL is set, extract and add its hostname + if rootShard.Spec.ShardBaseURL != "" { + if hostname := utils.ExtractHostnameFromURL(rootShard.Spec.ShardBaseURL); hostname != "" { + dnsNames.Insert(hostname) + } + } + + return sets.List(dnsNames) +} diff --git a/internal/resources/rootshard/certificates_test.go b/internal/resources/rootshard/certificates_test.go new file mode 100644 index 00000000..7e5ad4b4 --- /dev/null +++ b/internal/resources/rootshard/certificates_test.go @@ -0,0 +1,156 @@ +/* +Copyright 2026 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 rootshard + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kcp-dev/kcp-operator/internal/resources/naming" + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +func TestBuildRootShardDNSNames(t *testing.T) { + tests := []struct { + name string + rootShard *operatorv1alpha1.RootShard + expected []string + }{ + { + name: "without shardBaseURL", + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-shard", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.RootShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ClusterDomain: "cluster.local", + }, + External: operatorv1alpha1.ExternalConfig{ + Hostname: "api.example.com", + }, + }, + }, + expected: []string{ + "test-shard-kcp.kcp.svc.cluster.local", + "api.example.com", + }, + }, + { + name: "with shardBaseURL matching external hostname", + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-shard", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.RootShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ClusterDomain: "cluster.local", + ShardBaseURL: "https://api.example.com:6443", + }, + External: operatorv1alpha1.ExternalConfig{ + Hostname: "api.example.com", + }, + }, + }, + expected: []string{ + "test-shard-kcp.kcp.svc.cluster.local", + "api.example.com", + }, + }, + { + name: "with shardBaseURL different from external hostname", + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "root", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.RootShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ClusterDomain: "cluster.local", + ShardBaseURL: "https://root.shard.kcp.example.com:6443", + }, + External: operatorv1alpha1.ExternalConfig{ + Hostname: "api.kcp.example.com", + }, + }, + }, + expected: []string{ + "root-kcp.kcp.svc.cluster.local", + "api.kcp.example.com", + "root.shard.kcp.example.com", + }, + }, + { + name: "with invalid shardBaseURL", + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-shard", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.RootShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ClusterDomain: "cluster.local", + ShardBaseURL: "not-a-valid-url", + }, + External: operatorv1alpha1.ExternalConfig{ + Hostname: "api.example.com", + }, + }, + }, + expected: []string{ + "test-shard-kcp.kcp.svc.cluster.local", + "api.example.com", + }, + }, + { + name: "with shardBaseURL containing subdomain", + rootShard: &operatorv1alpha1.RootShard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prod", + Namespace: "kcp-system", + }, + Spec: operatorv1alpha1.RootShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ShardBaseURL: "https://prod.shard.kcp.devsecops.dev.codefabric.cloud:6443", + }, + External: operatorv1alpha1.ExternalConfig{ + Hostname: "api.kcp.devsecops.dev.codefabric.cloud", + }, + }, + }, + expected: []string{ + "prod-kcp.kcp-system.svc.cluster.local", + "api.kcp.devsecops.dev.codefabric.cloud", + "prod.shard.kcp.devsecops.dev.codefabric.cloud", + }, + }, + } + + namingScheme := naming.NewVersion1() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := buildRootShardDNSNames(tt.rootShard, namingScheme) + assert.ElementsMatch(t, tt.expected, result) + }) + } +} diff --git a/internal/resources/rootshard/deployment.go b/internal/resources/rootshard/deployment.go index d3d118c7..31799213 100644 --- a/internal/resources/rootshard/deployment.go +++ b/internal/resources/rootshard/deployment.go @@ -29,6 +29,7 @@ import ( "k8s.io/utils/ptr" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -76,10 +77,10 @@ func getCacheServerClientCertMountPath() string { return "/etc/cache-server/tls/client-certificate" } -func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace) reconciling.NamedDeploymentReconcilerFactory { +func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace, names naming.Scheme) reconciling.NamedDeploymentReconcilerFactory { return func() (string, reconciling.DeploymentReconciler) { - return resources.GetRootShardDeploymentName(rootShard), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { - labels := resources.GetRootShardResourceLabels(rootShard) + return names.RootShardDeploymentName(rootShard), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { + labels := names.RootShardResourceLabels(rootShard) dep.SetLabels(labels) dep.Spec.Selector = &metav1.LabelSelector{ MatchLabels: labels, @@ -89,11 +90,11 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator secretMounts := []utils.SecretMount{{ VolumeName: "kcp-ca", - SecretName: resources.GetRootShardCAName(rootShard, operatorv1alpha1.RootCA), + SecretName: names.RootShardCAName(rootShard, operatorv1alpha1.RootCA), MountPath: getCAMountPath(operatorv1alpha1.RootCA), }} - args := getArgs(rootShard, kcpVW) + args := getArgs(rootShard, kcpVW, names) for _, cert := range []operatorv1alpha1.Certificate{ // requires server CA and the logical-cluster-admin cert to be mounted @@ -103,7 +104,7 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-kubeconfig", cert), - SecretName: resources.GetRootShardKubeconfigSecret(rootShard, cert), + SecretName: names.RootShardKubeconfigSecret(rootShard, cert), MountPath: getKubeconfigMountPath(cert), }) } @@ -116,7 +117,7 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-ca", ca), - SecretName: resources.GetRootShardCAName(rootShard, ca), + SecretName: names.RootShardCAName(rootShard, ca), MountPath: getCAMountPath(ca), }) } @@ -130,7 +131,7 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-cert", cert), - SecretName: resources.GetRootShardCertificateName(rootShard, cert), + SecretName: names.RootShardCertificateName(rootShard, cert), MountPath: getCertificateMountPath(cert), }) } @@ -142,7 +143,7 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator if rootShard.Spec.CABundleSecretRef != nil { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "ca-bundle", - SecretName: fmt.Sprintf("%s-merged-ca-bundle", rootShard.Name), + SecretName: names.MergedCABundleName(rootShard.Name), MountPath: getCAMountPath(operatorv1alpha1.CABundleCA), }) } @@ -152,19 +153,19 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator if ref := rootShard.Spec.Cache.Reference; ref != nil { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "cache-server-kubeconfig", - SecretName: resources.GetCacheServerKubeconfigName(ref.Name), + SecretName: names.CacheServerKubeconfigName(ref.Name), MountPath: getCacheServerKubeconfigMountPath(), }) secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "cache-server-ca", - SecretName: resources.GetCacheServerCAName(ref.Name, operatorv1alpha1.RootCA), + SecretName: names.CacheServerCAName(ref.Name, operatorv1alpha1.RootCA), MountPath: getCacheServerCAMountPath(operatorv1alpha1.RootCA), }) secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "cache-server-client-cert", - SecretName: fmt.Sprintf("%s-client-certificate", ref.Name), + SecretName: names.CacheServerClientCertificateName(ref.Name), MountPath: getCacheServerClientCertMountPath(), }) } @@ -210,7 +211,7 @@ func DeploymentReconciler(rootShard *operatorv1alpha1.RootShard, kcpVW *operator } } -func getArgs(rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace) []string { +func getArgs(rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace, names naming.Scheme) []string { args := []string{ // CA configuration. fmt.Sprintf("--root-ca-file=%s/tls.crt", getCAMountPath(operatorv1alpha1.RootCA)), @@ -218,6 +219,7 @@ func getArgs(rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.Virt // Requestheader configuration. fmt.Sprintf("--requestheader-client-ca-file=%s/tls.crt", getCAMountPath(operatorv1alpha1.RequestHeaderClientCA)), + fmt.Sprintf("--requestheader-allowed-names=%s,%s", resources.FrontProxyCommonName, resources.RootShardProxyCommonName), "--requestheader-username-headers=X-Remote-User", "--requestheader-group-headers=X-Remote-Group", "--requestheader-extra-headers-prefix=X-Remote-Extra-", @@ -230,7 +232,7 @@ func getArgs(rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.Virt "--service-account-lookup=false", // General shard configuration. - fmt.Sprintf("--shard-base-url=%s", resources.GetRootShardBaseURL(rootShard)), + fmt.Sprintf("--shard-base-url=%s", names.RootShardBaseURL(rootShard)), fmt.Sprintf("--shard-external-url=https://%s:%d", rootShard.Spec.External.Hostname, rootShard.Spec.External.Port), fmt.Sprintf("--logical-cluster-admin-kubeconfig=%s/kubeconfig", getKubeconfigMountPath(operatorv1alpha1.LogicalClusterAdminCertificate)), fmt.Sprintf("--external-logical-cluster-admin-kubeconfig=%s/kubeconfig", getKubeconfigMountPath(operatorv1alpha1.ExternalLogicalClusterAdminCertificate)), diff --git a/internal/resources/rootshard/issuers.go b/internal/resources/rootshard/issuers.go index 26fc5f5c..c6da189a 100644 --- a/internal/resources/rootshard/issuers.go +++ b/internal/resources/rootshard/issuers.go @@ -20,12 +20,12 @@ import ( certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" "github.com/kcp-dev/kcp-operator/internal/reconciling" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func RootCAIssuerReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedIssuerReconcilerFactory { - name := resources.GetRootShardCAName(rootShard, operatorv1alpha1.RootCA) +func RootCAIssuerReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedIssuerReconcilerFactory { + name := names.RootShardCAName(rootShard, operatorv1alpha1.RootCA) secretName := name if rootShard.Spec.Certificates.CASecretRef != nil { @@ -34,7 +34,7 @@ func RootCAIssuerReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.N return func() (string, reconciling.IssuerReconciler) { return name, func(issuer *certmanagerv1.Issuer) (*certmanagerv1.Issuer, error) { - issuer.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + issuer.SetLabels(names.RootShardResourceLabels(rootShard)) issuer.Spec = certmanagerv1.IssuerSpec{ IssuerConfig: certmanagerv1.IssuerConfig{ CA: &certmanagerv1.CAIssuer{ @@ -48,12 +48,12 @@ func RootCAIssuerReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.N } } -func CAIssuerReconciler(rootShard *operatorv1alpha1.RootShard, ca operatorv1alpha1.CA) reconciling.NamedIssuerReconcilerFactory { - name := resources.GetRootShardCAName(rootShard, ca) +func CAIssuerReconciler(rootShard *operatorv1alpha1.RootShard, ca operatorv1alpha1.CA, names naming.Scheme) reconciling.NamedIssuerReconcilerFactory { + name := names.RootShardCAName(rootShard, ca) return func() (string, reconciling.IssuerReconciler) { return name, func(issuer *certmanagerv1.Issuer) (*certmanagerv1.Issuer, error) { - issuer.SetLabels(resources.GetRootShardResourceLabels(rootShard)) + issuer.SetLabels(names.RootShardResourceLabels(rootShard)) issuer.Spec = certmanagerv1.IssuerSpec{ IssuerConfig: certmanagerv1.IssuerConfig{ CA: &certmanagerv1.CAIssuer{ @@ -67,13 +67,13 @@ func CAIssuerReconciler(rootShard *operatorv1alpha1.RootShard, ca operatorv1alph } } -func ClientCAIssuerReconciler(rootshard *operatorv1alpha1.RootShard) reconciling.NamedIssuerReconcilerFactory { - name := resources.GetRootShardCAName(rootshard, operatorv1alpha1.ClientCA) +func ClientCAIssuerReconciler(rootshard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedIssuerReconcilerFactory { + name := names.RootShardCAName(rootshard, operatorv1alpha1.ClientCA) secretName := name return func() (string, reconciling.IssuerReconciler) { return name, func(issuer *certmanagerv1.Issuer) (*certmanagerv1.Issuer, error) { - issuer.SetLabels(resources.GetRootShardResourceLabels(rootshard)) + issuer.SetLabels(names.RootShardResourceLabels(rootshard)) issuer.Spec = certmanagerv1.IssuerSpec{ IssuerConfig: certmanagerv1.IssuerConfig{ CA: &certmanagerv1.CAIssuer{ @@ -87,13 +87,13 @@ func ClientCAIssuerReconciler(rootshard *operatorv1alpha1.RootShard) reconciling } } -func FrontProxyClientCAIssuerReconciler(rootshard *operatorv1alpha1.RootShard) reconciling.NamedIssuerReconcilerFactory { - name := resources.GetRootShardCAName(rootshard, operatorv1alpha1.FrontProxyClientCA) +func FrontProxyClientCAIssuerReconciler(rootshard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedIssuerReconcilerFactory { + name := names.RootShardCAName(rootshard, operatorv1alpha1.FrontProxyClientCA) secretName := name return func() (string, reconciling.IssuerReconciler) { return name, func(issuer *certmanagerv1.Issuer) (*certmanagerv1.Issuer, error) { - issuer.SetLabels(resources.GetRootShardResourceLabels(rootshard)) + issuer.SetLabels(names.RootShardResourceLabels(rootshard)) issuer.Spec = certmanagerv1.IssuerSpec{ IssuerConfig: certmanagerv1.IssuerConfig{ CA: &certmanagerv1.CAIssuer{ diff --git a/internal/resources/rootshard/kubeconfigs.go b/internal/resources/rootshard/kubeconfigs.go index f907780e..bbad282c 100644 --- a/internal/resources/rootshard/kubeconfigs.go +++ b/internal/resources/rootshard/kubeconfigs.go @@ -25,15 +25,11 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func kubeconfigSecret(rootShard *operatorv1alpha1.RootShard, cert operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s-kubeconfig", rootShard.Name, cert) -} - -func LogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootShard) k8creconciling.NamedSecretReconcilerFactory { +func LogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { const ( serverName = "root-shard" contextName = "shard-base" // hardcoded in kcp @@ -41,7 +37,7 @@ func LogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootSha ) return func() (string, k8creconciling.SecretReconciler) { - return kubeconfigSecret(rootShard, operatorv1alpha1.LogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { + return names.RootShardKubeconfigSecret(rootShard, operatorv1alpha1.LogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { var config *clientcmdapi.Config if secret.Data == nil { @@ -51,7 +47,7 @@ func LogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootSha config = &clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ serverName: { - Server: resources.GetRootShardBaseURL(rootShard), + Server: names.RootShardBaseURL(rootShard), CertificateAuthority: getCAMountPath(operatorv1alpha1.ServerCA) + "/tls.crt", }, }, @@ -82,7 +78,7 @@ func LogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootSha } } -func ExternalLogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootShard) k8creconciling.NamedSecretReconcilerFactory { +func ExternalLogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { const ( serverName = "root-shard" contextName = "shard-base" // hardcoded in kcp @@ -90,7 +86,7 @@ func ExternalLogicalClusterAdminKubeconfigReconciler(rootShard *operatorv1alpha1 ) return func() (string, k8creconciling.SecretReconciler) { - return kubeconfigSecret(rootShard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { + return names.RootShardKubeconfigSecret(rootShard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { var config *clientcmdapi.Config if secret.Data == nil { diff --git a/internal/resources/rootshard/service.go b/internal/resources/rootshard/service.go index 639a1a4d..396de47d 100644 --- a/internal/resources/rootshard/service.go +++ b/internal/resources/rootshard/service.go @@ -23,33 +23,36 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func ServiceReconciler(rootShard *operatorv1alpha1.RootShard) reconciling.NamedServiceReconcilerFactory { +func ServiceReconciler(rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedServiceReconcilerFactory { return func() (string, reconciling.ServiceReconciler) { - return resources.GetRootShardServiceName(rootShard), func(svc *corev1.Service) (*corev1.Service, error) { - labels := resources.GetRootShardResourceLabels(rootShard) - svc.SetLabels(labels) - svc.Spec.Type = corev1.ServiceTypeClusterIP - svc.Spec.Ports = []corev1.ServicePort{ - { - Name: "https", - Protocol: corev1.ProtocolTCP, - Port: 6443, - TargetPort: intstr.FromInt32(6443), - AppProtocol: ptr.To("https"), - }, - { + return names.RootShardServiceName(rootShard), func(svc *corev1.Service) (*corev1.Service, error) { + ports := []corev1.ServicePort{{ + Name: "https", + Protocol: corev1.ProtocolTCP, + Port: 6443, + TargetPort: intstr.FromInt32(6443), + AppProtocol: ptr.To("https"), + }} + + if rootShard.Spec.KCPVirtualWorkspace == nil { + ports = append(ports, corev1.ServicePort{ Name: "https-virtual-workspaces", Protocol: corev1.ProtocolTCP, Port: 6444, TargetPort: intstr.FromInt32(6444), AppProtocol: ptr.To("https"), - }, + }) } + + labels := names.RootShardResourceLabels(rootShard) + svc.SetLabels(labels) + svc.Spec.Type = corev1.ServiceTypeClusterIP + svc.Spec.Ports = ports svc.Spec.Selector = labels return utils.ApplyServiceTemplate(svc, rootShard.Spec.ServiceTemplate), nil diff --git a/internal/resources/shard/ca_bundle.go b/internal/resources/shard/ca_bundle.go index f6bc9df2..3b09d1fd 100644 --- a/internal/resources/shard/ca_bundle.go +++ b/internal/resources/shard/ca_bundle.go @@ -27,12 +27,13 @@ import ( ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func MergedCABundleSecretReconciler(ctx context.Context, shard *operatorv1alpha1.Shard, kubeClient ctrlruntimeclient.Client) k8creconciling.NamedSecretReconcilerFactory { +func MergedCABundleSecretReconciler(ctx context.Context, shard *operatorv1alpha1.Shard, kubeClient ctrlruntimeclient.Client, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { return func() (string, k8creconciling.SecretReconciler) { - secretName := fmt.Sprintf("%s-merged-ca-bundle", shard.Name) + secretName := names.MergedCABundleName(shard.Name) return secretName, func(secret *corev1.Secret) (*corev1.Secret, error) { if secret.Data == nil { secret.Data = make(map[string][]byte) @@ -40,7 +41,7 @@ func MergedCABundleSecretReconciler(ctx context.Context, shard *operatorv1alpha1 // Get ServerCA certificate serverCASecret := &corev1.Secret{} - serverCASecretName := resources.GetShardCertificateName(shard, operatorv1alpha1.ServerCertificate) + serverCASecretName := names.ShardCertificateName(shard, operatorv1alpha1.ServerCertificate) err := kubeClient.Get(ctx, types.NamespacedName{ Name: serverCASecretName, Namespace: shard.Namespace, diff --git a/internal/resources/shard/certificates.go b/internal/resources/shard/certificates.go index 734bfaac..6b579bca 100644 --- a/internal/resources/shard/certificates.go +++ b/internal/resources/shard/certificates.go @@ -22,21 +22,24 @@ import ( certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" certmanagermetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func ServerCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ServerCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ServerCertificate - name := resources.GetShardCertificateName(shard, certKind) + name := names.ShardCertificateName(shard, certKind) template := shard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetShardResourceLabels(shard)) + cert.SetLabels(names.ShardResourceLabels(shard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -60,13 +63,10 @@ func ServerCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *opera certmanagerv1.UsageDigitalSignature, }, - DNSNames: []string{ - "localhost", - resources.GetShardBaseHost(shard), - }, + DNSNames: buildShardDNSNames(shard, names), - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -77,15 +77,15 @@ func ServerCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *opera } } -func VirtualWorkspacesCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func VirtualWorkspacesCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.VirtualWorkspacesCertificate - name := resources.GetShardCertificateName(shard, certKind) + name := names.ShardCertificateName(shard, certKind) template := shard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetShardResourceLabels(shard)) + cert.SetLabels(names.ShardResourceLabels(shard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -108,11 +108,11 @@ func VirtualWorkspacesCertificateReconciler(shard *operatorv1alpha1.Shard, rootS }, DNSNames: []string{ - resources.GetShardBaseHost(shard), + names.ShardBaseHost(shard), }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -123,15 +123,15 @@ func VirtualWorkspacesCertificateReconciler(shard *operatorv1alpha1.Shard, rootS } } -func ServiceAccountCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ServiceAccountCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ServiceAccountCertificate - name := resources.GetShardCertificateName(shard, certKind) + name := names.ShardCertificateName(shard, certKind) template := shard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetShardResourceLabels(shard)) + cert.SetLabels(names.ShardResourceLabels(shard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -155,8 +155,8 @@ func ServiceAccountCertificateReconciler(shard *operatorv1alpha1.Shard, rootShar Size: 4096, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServiceAccountCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServiceAccountCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -167,15 +167,15 @@ func ServiceAccountCertificateReconciler(shard *operatorv1alpha1.Shard, rootShar } } -func RootShardClientCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func RootShardClientCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ClientCertificate - name := resources.GetShardCertificateName(shard, certKind) + name := names.ShardCertificateName(shard, certKind) template := shard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetShardResourceLabels(shard)) + cert.SetLabels(names.ShardResourceLabels(shard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -202,8 +202,8 @@ func RootShardClientCertificateReconciler(shard *operatorv1alpha1.Shard, rootSha certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -214,15 +214,15 @@ func RootShardClientCertificateReconciler(shard *operatorv1alpha1.Shard, rootSha } } -func LogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func LogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.LogicalClusterAdminCertificate - name := resources.GetShardCertificateName(shard, certKind) + name := names.ShardCertificateName(shard, certKind) template := shard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetShardResourceLabels(shard)) + cert.SetLabels(names.ShardResourceLabels(shard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -249,8 +249,8 @@ func LogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Shard, roo certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -261,15 +261,15 @@ func LogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Shard, roo } } -func ExternalLogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ExternalLogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ExternalLogicalClusterAdminCertificate - name := resources.GetShardCertificateName(shard, certKind) + name := names.ShardCertificateName(shard, certKind) template := shard.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetShardResourceLabels(shard)) + cert.SetLabels(names.ShardResourceLabels(shard)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -296,8 +296,8 @@ func ExternalLogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Sh certmanagerv1.UsageClientAuth, }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.FrontProxyClientCA), Kind: "Issuer", Group: "cert-manager.io", }, @@ -307,3 +307,22 @@ func ExternalLogicalClusterAdminCertificateReconciler(shard *operatorv1alpha1.Sh } } } + +// buildShardDNSNames builds the list of DNS names for the shard server certificate. +// It includes localhost, the internal Kubernetes service host, and if shardBaseURL +// is set, it also extracts and includes the hostname from that URL. +func buildShardDNSNames(shard *operatorv1alpha1.Shard, names naming.Scheme) []string { + dnsNames := sets.New( + "localhost", + names.ShardBaseHost(shard), + ) + + // If shardBaseURL is set, extract and add its hostname + if shard.Spec.ShardBaseURL != "" { + if hostname := utils.ExtractHostnameFromURL(shard.Spec.ShardBaseURL); hostname != "" { + dnsNames.Insert(hostname) + } + } + + return sets.List(dnsNames) +} diff --git a/internal/resources/shard/certificates_test.go b/internal/resources/shard/certificates_test.go new file mode 100644 index 00000000..2edfebba --- /dev/null +++ b/internal/resources/shard/certificates_test.go @@ -0,0 +1,120 @@ +/* +Copyright 2026 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 shard + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kcp-dev/kcp-operator/internal/resources/naming" + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +func TestBuildShardDNSNames(t *testing.T) { + tests := []struct { + name string + shard *operatorv1alpha1.Shard + expected []string + }{ + { + name: "without shardBaseURL", + shard: &operatorv1alpha1.Shard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-shard", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.ShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ClusterDomain: "cluster.local", + }, + }, + }, + expected: []string{ + "localhost", + "test-shard-shard-kcp.kcp.svc.cluster.local", + }, + }, + { + name: "with shardBaseURL", + shard: &operatorv1alpha1.Shard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "worker-1", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.ShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ClusterDomain: "cluster.local", + ShardBaseURL: "https://worker-1.shard.kcp.example.com:6443", + }, + }, + }, + expected: []string{ + "localhost", + "worker-1-shard-kcp.kcp.svc.cluster.local", + "worker-1.shard.kcp.example.com", + }, + }, + { + name: "with shardBaseURL matching localhost", + shard: &operatorv1alpha1.Shard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "local-shard", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.ShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ShardBaseURL: "https://localhost:6443", + }, + }, + }, + expected: []string{ + "localhost", + "local-shard-shard-kcp.kcp.svc.cluster.local", + }, + }, + { + name: "with invalid shardBaseURL", + shard: &operatorv1alpha1.Shard{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-shard", + Namespace: "kcp", + }, + Spec: operatorv1alpha1.ShardSpec{ + CommonShardSpec: operatorv1alpha1.CommonShardSpec{ + ShardBaseURL: "not-a-valid-url", + }, + }, + }, + expected: []string{ + "localhost", + "test-shard-shard-kcp.kcp.svc.cluster.local", + }, + }, + } + + namingScheme := naming.NewVersion1() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := buildShardDNSNames(tt.shard, namingScheme) + assert.ElementsMatch(t, tt.expected, result) + }) + } +} diff --git a/internal/resources/shard/deployment.go b/internal/resources/shard/deployment.go index 97b5a793..72206466 100644 --- a/internal/resources/shard/deployment.go +++ b/internal/resources/shard/deployment.go @@ -29,6 +29,7 @@ import ( "k8s.io/utils/ptr" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -88,10 +89,10 @@ func getEffectiveCacheRef(shard *operatorv1alpha1.Shard, rootShard *operatorv1al return "" } -func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace) reconciling.NamedDeploymentReconcilerFactory { +func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace, names naming.Scheme) reconciling.NamedDeploymentReconcilerFactory { return func() (string, reconciling.DeploymentReconciler) { - return resources.GetShardDeploymentName(shard), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { - labels := resources.GetShardResourceLabels(shard) + return names.ShardDeploymentName(shard), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { + labels := names.ShardResourceLabels(shard) dep.SetLabels(labels) dep.Spec.Selector = &metav1.LabelSelector{ MatchLabels: labels, @@ -101,11 +102,11 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al secretMounts := []utils.SecretMount{{ VolumeName: "kcp-ca", - SecretName: resources.GetRootShardCAName(rootShard, operatorv1alpha1.RootCA), + SecretName: names.RootShardCAName(rootShard, operatorv1alpha1.RootCA), MountPath: getCAMountPath(operatorv1alpha1.RootCA), }} - args := getArgs(shard, rootShard, kcpVW) + args := getArgs(shard, rootShard, kcpVW, names) for _, cert := range []operatorv1alpha1.Certificate{ // requires server CA and the shard client cert to be mounted @@ -115,7 +116,7 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-kubeconfig", cert), - SecretName: resources.GetShardKubeconfigSecret(shard, cert), + SecretName: names.ShardKubeconfigSecret(shard, cert), MountPath: getKubeconfigMountPath(cert), }) } @@ -129,7 +130,7 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-ca", ca), - SecretName: resources.GetRootShardCAName(rootShard, ca), + SecretName: names.RootShardCAName(rootShard, ca), MountPath: getCAMountPath(ca), }) } @@ -143,7 +144,7 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-cert", cert), - SecretName: resources.GetShardCertificateName(shard, cert), + SecretName: names.ShardCertificateName(shard, cert), MountPath: getCertificateMountPath(cert), }) } @@ -155,7 +156,7 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al if shard.Spec.CABundleSecretRef != nil { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "ca-bundle", - SecretName: fmt.Sprintf("%s-merged-ca-bundle", shard.Name), + SecretName: names.MergedCABundleName(shard.Name), MountPath: getCAMountPath(operatorv1alpha1.CABundleCA), }) } @@ -166,15 +167,15 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "cache-server-kubeconfig", - SecretName: resources.GetCacheServerKubeconfigName(cacheRef), + SecretName: names.CacheServerKubeconfigName(cacheRef), MountPath: getCacheServerKubeconfigMountPath(), }, utils.SecretMount{ VolumeName: "cache-server-ca", - SecretName: resources.GetCacheServerCAName(cacheRef, operatorv1alpha1.RootCA), + SecretName: names.CacheServerCAName(cacheRef, operatorv1alpha1.RootCA), MountPath: getCacheServerCAMountPath(operatorv1alpha1.RootCA), }, utils.SecretMount{ VolumeName: "cache-server-client-cert", - SecretName: fmt.Sprintf("%s-client-certificate", cacheRef), + SecretName: names.CacheServerClientCertificateName(cacheRef), MountPath: getCacheServerClientCertMountPath(), }, ) @@ -221,7 +222,7 @@ func DeploymentReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1al } } -func getArgs(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace) []string { +func getArgs(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, kcpVW *operatorv1alpha1.VirtualWorkspace, names naming.Scheme) []string { // Configure the cache kubeconfig to point either to an explicitly configured cache (maybe on the // shard, maybe on the root shard), or the root shard itself (in case no external cache is configured). var cacheKubeconfigMount string @@ -238,6 +239,7 @@ func getArgs(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShar // Requestheader configuration. fmt.Sprintf("--requestheader-client-ca-file=%s/tls.crt", getCAMountPath(operatorv1alpha1.RequestHeaderClientCA)), + fmt.Sprintf("--requestheader-allowed-names=%s,%s", resources.FrontProxyCommonName, resources.RootShardProxyCommonName), "--requestheader-username-headers=X-Remote-User", "--requestheader-group-headers=X-Remote-Group", "--requestheader-extra-headers-prefix=X-Remote-Extra-", @@ -254,7 +256,7 @@ func getArgs(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShar // General shard configuration. fmt.Sprintf("--shard-name=%s", shard.Name), - fmt.Sprintf("--shard-base-url=%s", resources.GetShardBaseURL(shard)), + fmt.Sprintf("--shard-base-url=%s", names.ShardBaseURL(shard)), fmt.Sprintf("--shard-external-url=https://%s:%d", rootShard.Spec.External.Hostname, rootShard.Spec.External.Port), fmt.Sprintf("--external-hostname=%s", rootShard.Spec.External.Hostname), diff --git a/internal/resources/shard/kubeconfigs.go b/internal/resources/shard/kubeconfigs.go index 58037af7..02c1200b 100644 --- a/internal/resources/shard/kubeconfigs.go +++ b/internal/resources/shard/kubeconfigs.go @@ -25,15 +25,11 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func kubeconfigSecret(shard *operatorv1alpha1.Shard, cert operatorv1alpha1.Certificate) string { - return fmt.Sprintf("%s-%s-kubeconfig", shard.Name, cert) -} - -func RootShardClientKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) k8creconciling.NamedSecretReconcilerFactory { +func RootShardClientKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { const ( serverName = "root-shard" contextName = "shard-base" // hardcoded in kcp @@ -41,7 +37,7 @@ func RootShardClientKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShar ) return func() (string, k8creconciling.SecretReconciler) { - return kubeconfigSecret(shard, operatorv1alpha1.ClientCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { + return names.ShardKubeconfigSecret(shard, operatorv1alpha1.ClientCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { var config *clientcmdapi.Config if secret.Data == nil { secret.Data = make(map[string][]byte) @@ -50,7 +46,7 @@ func RootShardClientKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShar config = &clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ serverName: { - Server: resources.GetRootShardBaseURL(rootShard), + Server: names.RootShardBaseURL(rootShard), CertificateAuthority: getCAMountPath(operatorv1alpha1.ServerCA) + "/tls.crt", }, }, @@ -81,7 +77,7 @@ func RootShardClientKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShar } } -func LogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) k8creconciling.NamedSecretReconcilerFactory { +func LogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { const ( serverName = "logical-cluster:admin" contextName = "logical-cluster" @@ -89,7 +85,7 @@ func LogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, root ) return func() (string, k8creconciling.SecretReconciler) { - return kubeconfigSecret(shard, operatorv1alpha1.LogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { + return names.ShardKubeconfigSecret(shard, operatorv1alpha1.LogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { var config *clientcmdapi.Config if secret.Data == nil { @@ -99,7 +95,7 @@ func LogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, root config = &clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ serverName: { - Server: resources.GetShardBaseURL(shard), + Server: names.ShardBaseURL(shard), CertificateAuthority: getCAMountPath(operatorv1alpha1.ServerCA) + "/tls.crt", }, }, @@ -130,7 +126,7 @@ func LogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, root } } -func ExternalLogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard) k8creconciling.NamedSecretReconcilerFactory { +func ExternalLogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Shard, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) k8creconciling.NamedSecretReconcilerFactory { const ( serverName = "external-logical-cluster:admin" contextName = "external-logical-cluster" @@ -138,7 +134,7 @@ func ExternalLogicalClusterAdminKubeconfigReconciler(shard *operatorv1alpha1.Sha ) return func() (string, k8creconciling.SecretReconciler) { - return kubeconfigSecret(shard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { + return names.ShardKubeconfigSecret(shard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate), func(secret *corev1.Secret) (*corev1.Secret, error) { var config *clientcmdapi.Config if secret.Data == nil { diff --git a/internal/resources/shard/service.go b/internal/resources/shard/service.go index e94336be..6a83e6c1 100644 --- a/internal/resources/shard/service.go +++ b/internal/resources/shard/service.go @@ -23,33 +23,36 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func ServiceReconciler(shard *operatorv1alpha1.Shard) reconciling.NamedServiceReconcilerFactory { +func ServiceReconciler(shard *operatorv1alpha1.Shard, names naming.Scheme) reconciling.NamedServiceReconcilerFactory { return func() (string, reconciling.ServiceReconciler) { - return resources.GetShardServiceName(shard), func(svc *corev1.Service) (*corev1.Service, error) { - labels := resources.GetShardResourceLabels(shard) - svc.SetLabels(labels) - svc.Spec.Type = corev1.ServiceTypeClusterIP - svc.Spec.Ports = []corev1.ServicePort{ - { - Name: "https", - Protocol: corev1.ProtocolTCP, - Port: 6443, - TargetPort: intstr.FromInt32(6443), - AppProtocol: ptr.To("https"), - }, - { + return names.ShardServiceName(shard), func(svc *corev1.Service) (*corev1.Service, error) { + ports := []corev1.ServicePort{{ + Name: "https", + Protocol: corev1.ProtocolTCP, + Port: 6443, + TargetPort: intstr.FromInt32(6443), + AppProtocol: ptr.To("https"), + }} + + if shard.Spec.KCPVirtualWorkspace == nil { + ports = append(ports, corev1.ServicePort{ Name: "https-virtual-workspaces", Protocol: corev1.ProtocolTCP, Port: 6444, TargetPort: intstr.FromInt32(6444), AppProtocol: ptr.To("https"), - }, + }) } + + labels := names.ShardResourceLabels(shard) + svc.SetLabels(labels) + svc.Spec.Type = corev1.ServiceTypeClusterIP + svc.Spec.Ports = ports svc.Spec.Selector = labels return utils.ApplyServiceTemplate(svc, shard.Spec.ServiceTemplate), nil diff --git a/internal/resources/utils/authentication.go b/internal/resources/utils/authentication.go index b25987a8..988d7a63 100644 --- a/internal/resources/utils/authentication.go +++ b/internal/resources/utils/authentication.go @@ -23,7 +23,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -81,7 +81,7 @@ func applyOIDCConfiguration(deployment *appsv1.Deployment, config operatorv1alph return deployment } -func applyServiceAccountAuthentication(deployment *appsv1.Deployment, rootShard *operatorv1alpha1.RootShard) *appsv1.Deployment { +func applyServiceAccountAuthentication(deployment *appsv1.Deployment, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) *appsv1.Deployment { // Secrets and volumes volumes := []corev1.Volume{} @@ -89,31 +89,31 @@ func applyServiceAccountAuthentication(deployment *appsv1.Deployment, rootShard // Root shard is not on the list, so we add it manually volumes = append(volumes, corev1.Volume{ - Name: resources.GetRootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), + Name: names.RootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: resources.GetRootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), + SecretName: names.RootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), }, }, }) volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: resources.GetRootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), + Name: names.RootShardCertificateName(rootShard, operatorv1alpha1.ServiceAccountCertificate), ReadOnly: true, MountPath: fmt.Sprintf("/etc/kcp/tls/%s/%s", rootShard.Name, string(operatorv1alpha1.ServiceAccountCertificate)), }) for _, shard := range rootShard.Status.Shards { volumes = append(volumes, corev1.Volume{ - Name: resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: shard.Name}}, operatorv1alpha1.ServiceAccountCertificate), + Name: names.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: shard.Name}}, operatorv1alpha1.ServiceAccountCertificate), VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: shard.Name}}, operatorv1alpha1.ServiceAccountCertificate), + SecretName: names.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: shard.Name}}, operatorv1alpha1.ServiceAccountCertificate), }, }, }) volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: shard.Name}}, operatorv1alpha1.ServiceAccountCertificate), + Name: names.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: shard.Name}}, operatorv1alpha1.ServiceAccountCertificate), ReadOnly: true, MountPath: fmt.Sprintf("/etc/kcp/tls/%s/%s", shard.Name, string(operatorv1alpha1.ServiceAccountCertificate)), }) diff --git a/internal/resources/utils/authentication_test.go b/internal/resources/utils/authentication_test.go index ea37532f..5d51a5d7 100644 --- a/internal/resources/utils/authentication_test.go +++ b/internal/resources/utils/authentication_test.go @@ -26,11 +26,12 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) func TestApplyServiceAccountAuthentication(t *testing.T) { + version1 := naming.NewVersion1() tests := []struct { name string rootShard *operatorv1alpha1.RootShard @@ -71,7 +72,7 @@ func TestApplyServiceAccountAuthentication(t *testing.T) { // Check volumes assert.Len(t, dep.Spec.Template.Spec.Volumes, 1) rootShardVolume := dep.Spec.Template.Spec.Volumes[0] - expectedVolumeName := resources.GetRootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) + expectedVolumeName := version1.RootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) assert.Equal(t, expectedVolumeName, rootShardVolume.Name) assert.NotNil(t, rootShardVolume.Secret) assert.Equal(t, expectedVolumeName, rootShardVolume.Secret.SecretName) @@ -135,9 +136,9 @@ func TestApplyServiceAccountAuthentication(t *testing.T) { assert.Equal(t, volume.Name, volume.Secret.SecretName) } - expectedRootVolume := resources.GetRootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) - expectedShard1Volume := resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "shard-1"}}, operatorv1alpha1.ServiceAccountCertificate) - expectedShard2Volume := resources.GetShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "shard-2"}}, operatorv1alpha1.ServiceAccountCertificate) + expectedRootVolume := version1.RootShardCertificateName(&operatorv1alpha1.RootShard{ObjectMeta: metav1.ObjectMeta{Name: "test-root-shard"}}, operatorv1alpha1.ServiceAccountCertificate) + expectedShard1Volume := version1.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "shard-1"}}, operatorv1alpha1.ServiceAccountCertificate) + expectedShard2Volume := version1.ShardCertificateName(&operatorv1alpha1.Shard{ObjectMeta: metav1.ObjectMeta{Name: "shard-2"}}, operatorv1alpha1.ServiceAccountCertificate) assert.True(t, volumeNames[expectedRootVolume], "Root shard volume not found") assert.True(t, volumeNames[expectedShard1Volume], "Shard-1 volume not found") @@ -289,7 +290,7 @@ func TestApplyServiceAccountAuthentication(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := applyServiceAccountAuthentication(tt.initialDeploy, tt.rootShard) + result := applyServiceAccountAuthentication(tt.initialDeploy, tt.rootShard, version1) require.NotNil(t, result) assert.Equal(t, tt.initialDeploy, result, "Function should return the same deployment instance") diff --git a/internal/resources/utils/certificates.go b/internal/resources/utils/certificates.go index abfcff88..5824a8c9 100644 --- a/internal/resources/utils/certificates.go +++ b/internal/resources/utils/certificates.go @@ -21,6 +21,7 @@ import ( "encoding/pem" "fmt" "maps" + "net/url" "os" certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" @@ -100,7 +101,7 @@ func applyCertificateSpecTemplate(cert *certmanagerv1.Certificate, tpl *operator cert.Spec.SecretTemplate.Labels = addNewKeys(cert.Spec.SecretTemplate.Labels, secretTpl.Labels) } if tpl.IssuerRef != nil { - cert.Spec.IssuerRef = cmmeta.ObjectReference{ + cert.Spec.IssuerRef = cmmeta.IssuerReference{ Name: tpl.IssuerRef.Name, Kind: tpl.IssuerRef.Kind, Group: tpl.IssuerRef.Group, @@ -244,3 +245,18 @@ func MergeCABundlesFiles(caFile1, caFile2 string) ([]byte, error) { return merged, nil } + +// ExtractHostnameFromURL extracts the hostname from a URL string. +// Returns empty string if the URL is invalid or empty. +func ExtractHostnameFromURL(rawURL string) string { + if rawURL == "" { + return "" + } + + parsed, err := url.Parse(rawURL) + if err != nil { + return "" + } + + return parsed.Hostname() +} diff --git a/internal/resources/utils/certificates_test.go b/internal/resources/utils/certificates_test.go index a9699269..852b24ba 100644 --- a/internal/resources/utils/certificates_test.go +++ b/internal/resources/utils/certificates_test.go @@ -122,3 +122,64 @@ YWJjZGVmZ2hpams= }) } } + +func TestExtractHostnameFromURL(t *testing.T) { + tests := []struct { + name string + url string + expected string + }{ + { + name: "empty URL", + url: "", + expected: "", + }, + { + name: "valid URL with https", + url: "https://api.example.com", + expected: "api.example.com", + }, + { + name: "valid URL with port", + url: "https://api.example.com:6443", + expected: "api.example.com", + }, + { + name: "valid URL with http", + url: "http://localhost:8080", + expected: "localhost", + }, + { + name: "URL with path", + url: "https://api.example.com:6443/path/to/resource", + expected: "api.example.com", + }, + { + name: "subdomain URL", + url: "https://root.shard.kcp.example.com:6443", + expected: "root.shard.kcp.example.com", + }, + { + name: "invalid URL", + url: "not-a-valid-url", + expected: "", + }, + { + name: "URL with IPv4 address", + url: "https://192.168.1.1:6443", + expected: "192.168.1.1", + }, + { + name: "URL with IPv6 address", + url: "https://[2001:db8::1]:6443", + expected: "2001:db8::1", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ExtractHostnameFromURL(tt.url) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/internal/resources/utils/deployments.go b/internal/resources/utils/deployments.go index 7ec0b096..1ca0cd3c 100644 --- a/internal/resources/utils/deployments.go +++ b/internal/resources/utils/deployments.go @@ -22,6 +22,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -104,14 +105,14 @@ func ApplyAuthConfiguration(deployment *appsv1.Deployment, config *operatorv1alp return deployment } -func ApplyFrontProxyAuthConfiguration(deployment *appsv1.Deployment, config *operatorv1alpha1.AuthSpec, rootShard *operatorv1alpha1.RootShard) *appsv1.Deployment { +func ApplyFrontProxyAuthConfiguration(deployment *appsv1.Deployment, config *operatorv1alpha1.AuthSpec, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) *appsv1.Deployment { if config == nil { return deployment } deployment = ApplyAuthConfiguration(deployment, config) if config.ServiceAccount != nil && config.ServiceAccount.Enabled { - deployment = applyServiceAccountAuthentication(deployment, rootShard) + deployment = applyServiceAccountAuthentication(deployment, rootShard, names) } return deployment diff --git a/internal/resources/utils/frontproxy.go b/internal/resources/utils/frontproxy.go new file mode 100644 index 00000000..6bbe5370 --- /dev/null +++ b/internal/resources/utils/frontproxy.go @@ -0,0 +1,55 @@ +/* +Copyright 2026 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 utils + +import ( + "net" + "strconv" + + operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" +) + +func GetFrontProxyExternalPort(fp *operatorv1alpha1.FrontProxy, r *operatorv1alpha1.RootShard) int { + // easy, the user explicitly configured an external port on the FrontProxy itself + if port := fp.Spec.External.Port; port > 0 { + return int(port) + } + + // fallback to deprecated ExternalHostname + if extName := fp.Spec.ExternalHostname; extName != "" { + _, port, err := net.SplitHostPort(extName) + if err == nil { + parsed, err := strconv.Atoi(port) + if err == nil { + return parsed + } + } + + // no port was given in the URL; assume the default + return 6443 + } + + // if nothing valid is configured on the FrontProxy, check the RootShard + if r != nil { + if port := r.Spec.External.Port; port > 0 { + return int(port) + } + } + + // last resort, fallback to the default kcp port + return 6443 +} diff --git a/internal/resources/virtualworkspace/certificate.go b/internal/resources/virtualworkspace/certificate.go index 067cfd88..3678bf8d 100644 --- a/internal/resources/virtualworkspace/certificate.go +++ b/internal/resources/virtualworkspace/certificate.go @@ -24,6 +24,7 @@ import ( "github.com/kcp-dev/kcp-operator/internal/reconciling" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -32,11 +33,11 @@ func commonName(vw *operatorv1alpha1.VirtualWorkspace) string { return fmt.Sprintf("%s-virtual-workspace", vw.Name) } -func ClientCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, issuerName string) reconciling.NamedCertificateReconcilerFactory { +func ClientCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, issuerName string, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ClientCertificate template := vw.Spec.CertificateTemplates.CertificateTemplate(certKind) - name := resources.GetVirtualWorkspaceCertificateName(vw, certKind) + name := names.VirtualWorkspaceCertificateName(vw, certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { @@ -68,7 +69,7 @@ func ClientCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, issuerNa Organizations: []string{"system:masters"}, }, - IssuerRef: certmanagermetav1.ObjectReference{ + IssuerRef: certmanagermetav1.IssuerReference{ Name: issuerName, Kind: "Issuer", Group: "cert-manager.io", @@ -80,15 +81,15 @@ func ClientCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, issuerNa } } -func ServerCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *operatorv1alpha1.RootShard) reconciling.NamedCertificateReconcilerFactory { +func ServerCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *operatorv1alpha1.RootShard, names naming.Scheme) reconciling.NamedCertificateReconcilerFactory { const certKind = operatorv1alpha1.ServerCertificate - name := resources.GetVirtualWorkspaceCertificateName(vw, certKind) + name := names.VirtualWorkspaceCertificateName(vw, certKind) template := vw.Spec.CertificateTemplates.CertificateTemplate(certKind) return func() (string, reconciling.CertificateReconciler) { return name, func(cert *certmanagerv1.Certificate) (*certmanagerv1.Certificate, error) { - cert.SetLabels(resources.GetVirtualWorkspaceResourceLabels(vw)) + cert.SetLabels(names.VirtualWorkspaceResourceLabels(vw)) cert.Spec = certmanagerv1.CertificateSpec{ SecretName: name, SecretTemplate: &certmanagerv1.CertificateSecretTemplate{ @@ -113,11 +114,11 @@ func ServerCertificateReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShar DNSNames: []string{ "localhost", - resources.GetVirtualWorkspaceBaseHost(vw), + names.VirtualWorkspaceBaseHost(vw), }, - IssuerRef: certmanagermetav1.ObjectReference{ - Name: resources.GetRootShardCAName(rootShard, operatorv1alpha1.ServerCA), + IssuerRef: certmanagermetav1.IssuerReference{ + Name: names.RootShardCAName(rootShard, operatorv1alpha1.ServerCA), Kind: "Issuer", Group: "cert-manager.io", }, diff --git a/internal/resources/virtualworkspace/deployment.go b/internal/resources/virtualworkspace/deployment.go index 9deaf5d1..485535fd 100644 --- a/internal/resources/virtualworkspace/deployment.go +++ b/internal/resources/virtualworkspace/deployment.go @@ -28,6 +28,7 @@ import ( "k8s.io/utils/ptr" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -88,18 +89,18 @@ func getEffectiveCacheRef(rootShard *operatorv1alpha1.RootShard, shard *operator return "" } -func kubeconfigSecret(rootShard *operatorv1alpha1.RootShard, shard *operatorv1alpha1.Shard, certName operatorv1alpha1.Certificate) string { +func kubeconfigSecret(rootShard *operatorv1alpha1.RootShard, shard *operatorv1alpha1.Shard, certName operatorv1alpha1.Certificate, names naming.Scheme) string { if shard != nil { - return resources.GetShardKubeconfigSecret(shard, certName) + return names.ShardKubeconfigSecret(shard, certName) } else { - return resources.GetRootShardKubeconfigSecret(rootShard, certName) + return names.RootShardKubeconfigSecret(rootShard, certName) } } -func DeploymentReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *operatorv1alpha1.RootShard, shard *operatorv1alpha1.Shard) reconciling.NamedDeploymentReconcilerFactory { +func DeploymentReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *operatorv1alpha1.RootShard, shard *operatorv1alpha1.Shard, names naming.Scheme) reconciling.NamedDeploymentReconcilerFactory { return func() (string, reconciling.DeploymentReconciler) { - return resources.GetVirtualWorkspaceDeploymentName(vw), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { - labels := resources.GetVirtualWorkspaceResourceLabels(vw) + return names.VirtualWorkspaceDeploymentName(vw), func(dep *appsv1.Deployment) (*appsv1.Deployment, error) { + labels := names.VirtualWorkspaceResourceLabels(vw) dep.SetLabels(labels) dep.Spec.Selector = &metav1.LabelSelector{ MatchLabels: labels, @@ -109,7 +110,7 @@ func DeploymentReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *oper secretMounts := []utils.SecretMount{{ VolumeName: "kcp-ca", - SecretName: resources.GetRootShardCAName(rootShard, operatorv1alpha1.RootCA), + SecretName: names.RootShardCAName(rootShard, operatorv1alpha1.RootCA), MountPath: getCAMountPath(operatorv1alpha1.RootCA), }} @@ -124,14 +125,14 @@ func DeploymentReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *oper } { secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-ca", ca), - SecretName: resources.GetRootShardCAName(rootShard, ca), + SecretName: names.RootShardCAName(rootShard, ca), MountPath: getCAMountPath(ca), }) } secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-cert", operatorv1alpha1.ServerCertificate), - SecretName: resources.GetVirtualWorkspaceCertificateName(vw, operatorv1alpha1.ServerCertificate), + SecretName: names.VirtualWorkspaceCertificateName(vw, operatorv1alpha1.ServerCertificate), MountPath: getCertificateMountPath(operatorv1alpha1.ServerCertificate), }) @@ -141,13 +142,13 @@ func DeploymentReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *oper secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-kubeconfig", operatorv1alpha1.LogicalClusterAdminCertificate), - SecretName: kubeconfigSecret(rootShard, shard, operatorv1alpha1.LogicalClusterAdminCertificate), + SecretName: kubeconfigSecret(rootShard, shard, operatorv1alpha1.LogicalClusterAdminCertificate, names), MountPath: getKubeconfigMountPath(operatorv1alpha1.LogicalClusterAdminCertificate), }) secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: fmt.Sprintf("%s-cert", operatorv1alpha1.ClientCertificate), - SecretName: resources.GetVirtualWorkspaceCertificateName(vw, operatorv1alpha1.ClientCertificate), + SecretName: names.VirtualWorkspaceCertificateName(vw, operatorv1alpha1.ClientCertificate), MountPath: getCertificateMountPath(operatorv1alpha1.LogicalClusterAdminCertificate), }) @@ -157,17 +158,17 @@ func DeploymentReconciler(vw *operatorv1alpha1.VirtualWorkspace, rootShard *oper secretMounts = append(secretMounts, utils.SecretMount{ VolumeName: "cache-server-kubeconfig", - SecretName: resources.GetCacheServerKubeconfigName(cacheRef), + SecretName: names.CacheServerKubeconfigName(cacheRef), MountPath: getCacheServerKubeconfigMountPath(), }, utils.SecretMount{ VolumeName: "cache-server-ca", - SecretName: resources.GetCacheServerCAName(cacheRef, operatorv1alpha1.RootCA), + SecretName: names.CacheServerCAName(cacheRef, operatorv1alpha1.RootCA), MountPath: getCacheServerCAMountPath(operatorv1alpha1.RootCA), }, utils.SecretMount{ VolumeName: "cache-server-client-cert", - SecretName: fmt.Sprintf("%s-client-certificate", cacheRef), + SecretName: names.CacheServerClientCertificateName(cacheRef), MountPath: getCacheServerClientCertMountPath(), }, ) @@ -248,6 +249,7 @@ func getArgs(vw *operatorv1alpha1.VirtualWorkspace, rootShard *operatorv1alpha1. // requestheader CA fmt.Sprintf("--requestheader-client-ca-file=%s/tls.crt", getCAMountPath(operatorv1alpha1.RequestHeaderClientCA)), + fmt.Sprintf("--requestheader-allowed-names=%s,%s", resources.FrontProxyCommonName, resources.RootShardProxyCommonName), "--requestheader-username-headers=X-Remote-User", "--requestheader-group-headers=X-Remote-Group", "--requestheader-extra-headers-prefix=X-Remote-Extra-", diff --git a/internal/resources/virtualworkspace/service.go b/internal/resources/virtualworkspace/service.go index a4515fe8..9e632586 100644 --- a/internal/resources/virtualworkspace/service.go +++ b/internal/resources/virtualworkspace/service.go @@ -23,19 +23,15 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" "github.com/kcp-dev/kcp-operator/internal/resources/utils" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) -func GetVirtualWorkspaceServiceName(vw *operatorv1alpha1.VirtualWorkspace) string { - return resources.GetVirtualWorkspaceDeploymentName(vw) -} - -func ServiceReconciler(vw *operatorv1alpha1.VirtualWorkspace) reconciling.NamedServiceReconcilerFactory { +func ServiceReconciler(vw *operatorv1alpha1.VirtualWorkspace, names naming.Scheme) reconciling.NamedServiceReconcilerFactory { return func() (string, reconciling.ServiceReconciler) { - return GetVirtualWorkspaceServiceName(vw), func(svc *corev1.Service) (*corev1.Service, error) { - labels := resources.GetVirtualWorkspaceResourceLabels(vw) + return names.VirtualWorkspaceServiceName(vw), func(svc *corev1.Service) (*corev1.Service, error) { + labels := names.VirtualWorkspaceResourceLabels(vw) svc.SetLabels(labels) svc.Spec.Type = corev1.ServiceTypeClusterIP svc.Spec.Ports = []corev1.ServicePort{ diff --git a/sdk/apis/operator/v1alpha1/common.go b/sdk/apis/operator/v1alpha1/common.go index fa8f459c..66814585 100644 --- a/sdk/apis/operator/v1alpha1/common.go +++ b/sdk/apis/operator/v1alpha1/common.go @@ -418,8 +418,11 @@ type OIDCConfiguration struct { // ClientID is the OIDC client ID configured on the issuer side for this kcp instance. ClientID string `json:"clientID"` - // Optionally provide the client secret for the OIDC client. This is not used by kcp itself, but is used to generate - // a OIDC kubeconfig that can be shared with users to log in via the OIDC provider. + // ClientSecret is the OIDC client secret configured on the issuer side for this kcp instance. + // This is not used by kcp itself, but is used to generate a OIDC kubeconfig that can be + // shared with users to log in via the OIDC provider. + // +optional + // Deprecated: kube OIDC is secretless. ClientSecret string `json:"clientSecret,omitempty"` // Experimental: Optionally provides a custom claim for fetching groups. The claim must be a string or an array of strings. diff --git a/test/crds/README.md b/test/crds/README.md index b855f44f..e7a658a1 100644 --- a/test/crds/README.md +++ b/test/crds/README.md @@ -1,4 +1,4 @@ # Third-party CRDs Sourced from: -- `https://github.com/cert-manager/cert-manager/releases/download/v1.19.3/cert-manager.crds.yaml` +- `https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.crds.yaml` diff --git a/test/crds/cert-manager.crds.yaml b/test/crds/cert-manager.crds.yaml index f4cf1864..16c2a983 100644 --- a/test/crds/cert-manager.crds.yaml +++ b/test/crds/cert-manager.crds.yaml @@ -24,7 +24,7 @@ metadata: app.kubernetes.io/name: "cert-manager" app.kubernetes.io/instance: "cert-manager" app.kubernetes.io/component: "crds" - app.kubernetes.io/version: "v1.19.3" + app.kubernetes.io/version: "v1.20.2" spec: group: acme.cert-manager.io names: @@ -299,6 +299,22 @@ spec: The TenantID of the Azure Service Principal used to authenticate with Azure DNS. If set, ClientID and ClientSecret must also be set. type: string + zoneType: + description: |- + ZoneType determines which type of Azure DNS zone to use. + + Valid values are: + - AzurePublicZone (default): Use a public Azure DNS zone. + - AzurePrivateZone: Use an Azure Private DNS zone. + + If not specified, AzurePublicZone is used. + + Support for Azure Private DNS zones is currently + experimental and may change in future releases. + enum: + - AzurePublicZone + - AzurePrivateZone + type: string required: - resourceGroupName - subscriptionID @@ -422,7 +438,7 @@ spec: description: |- The IP address or hostname of an authoritative DNS server supporting RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. + enclosed in square brackets (e.g [2001:db8::1]); port is optional. This field is required. type: string protocol: @@ -472,8 +488,8 @@ spec: description: |- The AccessKeyID is used for authentication. Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials type: string accessKeyIDSecretRef: @@ -481,8 +497,8 @@ spec: The SecretAccessKey is used for authentication. If set, pull the AWS access key ID from a key within a Kubernetes Secret. Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: key: @@ -571,8 +587,8 @@ spec: secretAccessKeySecretRef: description: |- The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: key: @@ -1929,9 +1945,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -3140,9 +3157,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -3290,6 +3308,10 @@ spec: - metadata - spec type: object + selectableFields: + - jsonPath: .spec.issuerRef.group + - jsonPath: .spec.issuerRef.kind + - jsonPath: .spec.issuerRef.name served: true storage: true subresources: @@ -3307,7 +3329,7 @@ metadata: app.kubernetes.io/name: "cert-manager" app.kubernetes.io/instance: "cert-manager" app.kubernetes.io/component: "crds" - app.kubernetes.io/version: "v1.19.3" + app.kubernetes.io/version: "v1.20.2" spec: group: acme.cert-manager.io names: @@ -3566,6 +3588,10 @@ spec: - metadata - spec type: object + selectableFields: + - jsonPath: .spec.issuerRef.group + - jsonPath: .spec.issuerRef.kind + - jsonPath: .spec.issuerRef.name served: true storage: true subresources: @@ -3583,7 +3609,7 @@ metadata: app.kubernetes.io/name: "cert-manager" app.kubernetes.io/instance: "cert-manager" app.kubernetes.io/component: "crds" - app.kubernetes.io/version: "v1.19.3" + app.kubernetes.io/version: "v1.20.2" spec: group: cert-manager.io names: @@ -3887,6 +3913,10 @@ spec: type: string type: object type: object + selectableFields: + - jsonPath: .spec.issuerRef.group + - jsonPath: .spec.issuerRef.kind + - jsonPath: .spec.issuerRef.name served: true storage: true subresources: @@ -3904,7 +3934,7 @@ metadata: app.kubernetes.io/name: "cert-manager" app.kubernetes.io/instance: "cert-manager" app.kubernetes.io/component: "crds" - app.kubernetes.io/version: "v1.19.3" + app.kubernetes.io/version: "v1.20.2" spec: group: cert-manager.io names: @@ -4347,9 +4377,6 @@ spec: will be generated whenever a re-issuance occurs. Default is `Always`. The default was changed from `Never` to `Always` in cert-manager >=v1.18.0. - The new default can be disabled by setting the - `--feature-gates=DefaultPrivateKeyRotationPolicyAlways=false` option on - the controller component. enum: - Never - Always @@ -4705,6 +4732,10 @@ spec: type: integer type: object type: object + selectableFields: + - jsonPath: .spec.issuerRef.group + - jsonPath: .spec.issuerRef.kind + - jsonPath: .spec.issuerRef.name served: true storage: true subresources: @@ -4722,7 +4753,7 @@ metadata: app.kubernetes.io/name: "cert-manager" app.kubernetes.io/instance: "cert-manager" app.kubernetes.io/component: "crds" - app.kubernetes.io/version: "v1.19.3" + app.kubernetes.io/version: "v1.20.2" spec: group: cert-manager.io names: @@ -5110,6 +5141,22 @@ spec: The TenantID of the Azure Service Principal used to authenticate with Azure DNS. If set, ClientID and ClientSecret must also be set. type: string + zoneType: + description: |- + ZoneType determines which type of Azure DNS zone to use. + + Valid values are: + - AzurePublicZone (default): Use a public Azure DNS zone. + - AzurePrivateZone: Use an Azure Private DNS zone. + + If not specified, AzurePublicZone is used. + + Support for Azure Private DNS zones is currently + experimental and may change in future releases. + enum: + - AzurePublicZone + - AzurePrivateZone + type: string required: - resourceGroupName - subscriptionID @@ -5233,7 +5280,7 @@ spec: description: |- The IP address or hostname of an authoritative DNS server supporting RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. + enclosed in square brackets (e.g [2001:db8::1]); port is optional. This field is required. type: string protocol: @@ -5283,8 +5330,8 @@ spec: description: |- The AccessKeyID is used for authentication. Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials type: string accessKeyIDSecretRef: @@ -5292,8 +5339,8 @@ spec: The SecretAccessKey is used for authentication. If set, pull the AWS access key ID from a key within a Kubernetes Secret. Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: key: @@ -5382,8 +5429,8 @@ spec: secretAccessKeySecretRef: description: |- The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: key: @@ -6740,9 +6787,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -7951,9 +7999,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -8210,8 +8259,8 @@ spec: properties: audiences: description: |- - TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token - consisting of the issuer's namespace and name is always included. + TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. + The default audiences are always included in the token. items: type: string type: array @@ -8339,16 +8388,16 @@ spec: type: object venafi: description: |- - Venafi configures this issuer to sign certificates using a Venafi TPP - or Venafi Cloud policy zone. + Venafi configures this issuer to sign certificates using a CyberArk Certificate Manager Self-Hosted + or SaaS policy zone. properties: cloud: description: |- - Cloud specifies the Venafi cloud configuration settings. - Only one of TPP or Cloud may be specified. + Cloud specifies the CyberArk Certificate Manager SaaS configuration settings. + Only one of CyberArk Certificate Manager may be specified. properties: apiTokenSecretRef: - description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. + description: APITokenSecretRef is a secret key selector for the CyberArk Certificate Manager SaaS API token. properties: key: description: |- @@ -8366,7 +8415,7 @@ spec: type: object url: description: |- - URL is the base URL for Venafi Cloud. + URL is the base URL for CyberArk Certificate Manager SaaS. Defaults to "https://api.venafi.cloud/". type: string required: @@ -8374,13 +8423,13 @@ spec: type: object tpp: description: |- - TPP specifies Trust Protection Platform configuration settings. - Only one of TPP or Cloud may be specified. + TPP specifies CyberArk Certificate Manager Self-Hosted configuration settings. + Only one of CyberArk Certificate Manager may be specified. properties: caBundle: description: |- Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. + chain presented by the CyberArk Certificate Manager Self-Hosted server. Only used if using HTTPS; ignored for HTTP. If undefined, the certificate bundle in the cert-manager controller container is used to validate the chain. format: byte @@ -8388,7 +8437,7 @@ spec: caBundleSecretRef: description: |- Reference to a Secret containing a base64-encoded bundle of PEM CAs - which will be used to validate the certificate chain presented by the TPP server. + which will be used to validate the certificate chain presented by the CyberArk Certificate Manager Self-Hosted server. Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in the cert-manager controller container is used to validate the TLS connection. @@ -8409,7 +8458,7 @@ spec: type: object credentialsRef: description: |- - CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. + CredentialsRef is a reference to a Secret containing the CyberArk Certificate Manager Self-Hosted API credentials. The secret must contain the key 'access-token' for the Access Token Authentication, or two keys, 'username' and 'password' for the API Keys Authentication. properties: @@ -8423,7 +8472,7 @@ spec: type: object url: description: |- - URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, + URL is the base URL for the vedsdk endpoint of the CyberArk Certificate Manager Self-Hosted instance, for example: "https://tpp.example.com/vedsdk". type: string required: @@ -8432,8 +8481,8 @@ spec: type: object zone: description: |- - Zone is the Venafi Policy Zone to use for this issuer. - All requests made to the Venafi platform will be restricted by the named + Zone is the Certificate Manager Policy Zone to use for this issuer. + All requests made to the Certificate Manager platform will be restricted by the named zone policy. This field is required. type: string @@ -8539,7 +8588,7 @@ metadata: app.kubernetes.io/name: "cert-manager" app.kubernetes.io/instance: "cert-manager" app.kubernetes.io/component: "crds" - app.kubernetes.io/version: "v1.19.3" + app.kubernetes.io/version: "v1.20.2" spec: group: cert-manager.io names: @@ -8926,6 +8975,22 @@ spec: The TenantID of the Azure Service Principal used to authenticate with Azure DNS. If set, ClientID and ClientSecret must also be set. type: string + zoneType: + description: |- + ZoneType determines which type of Azure DNS zone to use. + + Valid values are: + - AzurePublicZone (default): Use a public Azure DNS zone. + - AzurePrivateZone: Use an Azure Private DNS zone. + + If not specified, AzurePublicZone is used. + + Support for Azure Private DNS zones is currently + experimental and may change in future releases. + enum: + - AzurePublicZone + - AzurePrivateZone + type: string required: - resourceGroupName - subscriptionID @@ -9049,7 +9114,7 @@ spec: description: |- The IP address or hostname of an authoritative DNS server supporting RFC2136 in the form host:port. If the host is an IPv6 address it must be - enclosed in square brackets (e.g [2001:db8::1]) ; port is optional. + enclosed in square brackets (e.g [2001:db8::1]); port is optional. This field is required. type: string protocol: @@ -9099,8 +9164,8 @@ spec: description: |- The AccessKeyID is used for authentication. Cannot be set when SecretAccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials type: string accessKeyIDSecretRef: @@ -9108,8 +9173,8 @@ spec: The SecretAccessKey is used for authentication. If set, pull the AWS access key ID from a key within a Kubernetes Secret. Cannot be set when AccessKeyID is set. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: key: @@ -9198,8 +9263,8 @@ spec: secretAccessKeySecretRef: description: |- The SecretAccessKey is used for authentication. - If neither the Access Key nor Key ID are set, we fall-back to using env - vars, shared credentials file or AWS Instance metadata, + If neither the Access Key nor Key ID are set, we fall back to using env + vars, shared credentials file, or AWS Instance metadata, see: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials properties: key: @@ -10556,9 +10621,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -11767,9 +11833,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- @@ -12026,8 +12093,8 @@ spec: properties: audiences: description: |- - TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. The default token - consisting of the issuer's namespace and name is always included. + TokenAudiences is an optional list of extra audiences to include in the token passed to Vault. + The default audiences are always included in the token. items: type: string type: array @@ -12155,16 +12222,16 @@ spec: type: object venafi: description: |- - Venafi configures this issuer to sign certificates using a Venafi TPP - or Venafi Cloud policy zone. + Venafi configures this issuer to sign certificates using a CyberArk Certificate Manager Self-Hosted + or SaaS policy zone. properties: cloud: description: |- - Cloud specifies the Venafi cloud configuration settings. - Only one of TPP or Cloud may be specified. + Cloud specifies the CyberArk Certificate Manager SaaS configuration settings. + Only one of CyberArk Certificate Manager may be specified. properties: apiTokenSecretRef: - description: APITokenSecretRef is a secret key selector for the Venafi Cloud API token. + description: APITokenSecretRef is a secret key selector for the CyberArk Certificate Manager SaaS API token. properties: key: description: |- @@ -12182,7 +12249,7 @@ spec: type: object url: description: |- - URL is the base URL for Venafi Cloud. + URL is the base URL for CyberArk Certificate Manager SaaS. Defaults to "https://api.venafi.cloud/". type: string required: @@ -12190,13 +12257,13 @@ spec: type: object tpp: description: |- - TPP specifies Trust Protection Platform configuration settings. - Only one of TPP or Cloud may be specified. + TPP specifies CyberArk Certificate Manager Self-Hosted configuration settings. + Only one of CyberArk Certificate Manager may be specified. properties: caBundle: description: |- Base64-encoded bundle of PEM CAs which will be used to validate the certificate - chain presented by the TPP server. Only used if using HTTPS; ignored for HTTP. + chain presented by the CyberArk Certificate Manager Self-Hosted server. Only used if using HTTPS; ignored for HTTP. If undefined, the certificate bundle in the cert-manager controller container is used to validate the chain. format: byte @@ -12204,7 +12271,7 @@ spec: caBundleSecretRef: description: |- Reference to a Secret containing a base64-encoded bundle of PEM CAs - which will be used to validate the certificate chain presented by the TPP server. + which will be used to validate the certificate chain presented by the CyberArk Certificate Manager Self-Hosted server. Only used if using HTTPS; ignored for HTTP. Mutually exclusive with CABundle. If neither CABundle nor CABundleSecretRef is defined, the certificate bundle in the cert-manager controller container is used to validate the TLS connection. @@ -12225,7 +12292,7 @@ spec: type: object credentialsRef: description: |- - CredentialsRef is a reference to a Secret containing the Venafi TPP API credentials. + CredentialsRef is a reference to a Secret containing the CyberArk Certificate Manager Self-Hosted API credentials. The secret must contain the key 'access-token' for the Access Token Authentication, or two keys, 'username' and 'password' for the API Keys Authentication. properties: @@ -12239,7 +12306,7 @@ spec: type: object url: description: |- - URL is the base URL for the vedsdk endpoint of the Venafi TPP instance, + URL is the base URL for the vedsdk endpoint of the CyberArk Certificate Manager Self-Hosted instance, for example: "https://tpp.example.com/vedsdk". type: string required: @@ -12248,8 +12315,8 @@ spec: type: object zone: description: |- - Zone is the Venafi Policy Zone to use for this issuer. - All requests made to the Venafi platform will be restricted by the named + Zone is the Certificate Manager Policy Zone to use for this issuer. + All requests made to the Certificate Manager platform will be restricted by the named zone policy. This field is required. type: string diff --git a/test/e2e/cacheserver/cacheserver_test.go b/test/e2e/cacheserver/cacheserver_test.go index c1e439e6..ddfc7fce 100644 --- a/test/e2e/cacheserver/cacheserver_test.go +++ b/test/e2e/cacheserver/cacheserver_test.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/types" ctrlruntime "sigs.k8s.io/controller-runtime" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/utils" ) @@ -42,6 +43,7 @@ func TestCacheWithRootShard(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() // create namespace namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "cache-rootshard") @@ -50,7 +52,7 @@ func TestCacheWithRootShard(t *testing.T) { cacheServer := utils.DeployCacheServer(ctx, t, client, namespace.Name) // deploy a root shard that uses our cache - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { rs.Spec.Cache.Reference = &corev1.LocalObjectReference{ Name: cacheServer.Name, } @@ -104,6 +106,7 @@ func TestCacheWithExternalEtcdAndRootShard(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() // create namespace namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "cache-with-etcd-rootshard") @@ -112,7 +115,7 @@ func TestCacheWithExternalEtcdAndRootShard(t *testing.T) { cacheServer := utils.DeployCacheServerWithExternalEtcd(ctx, t, client, namespace.Name, 2) // deploy a root shard that uses our cache - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { rs.Spec.Cache.Reference = &corev1.LocalObjectReference{ Name: cacheServer.Name, } @@ -161,6 +164,7 @@ func TestCacheWithMultipleExplicitShards(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() // create namespace namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "cache-sharded-explicit") @@ -169,7 +173,7 @@ func TestCacheWithMultipleExplicitShards(t *testing.T) { cacheServer := utils.DeployCacheServer(ctx, t, client, namespace.Name) // deploy a root shard that uses our cache - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { rs.Spec.Cache.Reference = &corev1.LocalObjectReference{ Name: cacheServer.Name, } @@ -228,6 +232,7 @@ func TestCacheWithMultipleShardsInheritingConfig(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() // create namespace namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "cache-sharded-inherit") @@ -236,7 +241,7 @@ func TestCacheWithMultipleShardsInheritingConfig(t *testing.T) { cacheServer := utils.DeployCacheServer(ctx, t, client, namespace.Name) // deploy a root shard that uses our cache - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "", func(rs *operatorv1alpha1.RootShard) { rs.Spec.Cache.Reference = &corev1.LocalObjectReference{ Name: cacheServer.Name, } diff --git a/test/e2e/frontproxies/frontproxies_test.go b/test/e2e/frontproxies/frontproxies_test.go index 36af7e4b..3bdfd87b 100644 --- a/test/e2e/frontproxies/frontproxies_test.go +++ b/test/e2e/frontproxies/frontproxies_test.go @@ -20,7 +20,6 @@ package frontproxies import ( "context" - "fmt" "testing" "time" @@ -32,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/types" ctrlruntime "sigs.k8s.io/controller-runtime" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/utils" ) @@ -41,14 +41,14 @@ func TestCreateFrontProxy(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "create-frontproxy") - // externalHostname must match whatever DeployFrontProxy chooses as the name for the FrontProxy - externalHostname := fmt.Sprintf("front-proxy-front-proxy.%s.svc.cluster.local", namespace.Name) + externalHostname := utils.FrontProxyExternalHostname(namespace.Name, namingScheme) // deploy rootshard - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, externalHostname) + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, externalHostname) // deploy front-proxy frontProxy := utils.DeployFrontProxy(ctx, t, client, namespace.Name, rootShard.Name, externalHostname) diff --git a/test/e2e/kubeconfig-rbac/frontproxies_test.go b/test/e2e/kubeconfig-rbac/frontproxies_test.go index 32f50053..20161b1a 100644 --- a/test/e2e/kubeconfig-rbac/frontproxies_test.go +++ b/test/e2e/kubeconfig-rbac/frontproxies_test.go @@ -20,7 +20,6 @@ package kubeconfigrbac import ( "context" - "fmt" "testing" "time" @@ -35,6 +34,7 @@ import ( ctrlruntime "sigs.k8s.io/controller-runtime" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/utils" ) @@ -44,15 +44,16 @@ func TestProvisionFrontProxyRBAC(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() rootCluster := logicalcluster.NewPath("root") namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "provision-frontproxy-rbac") // externalHostname must match whatever DeployFrontProxy chooses as the name for the FrontProxy - externalHostname := fmt.Sprintf("front-proxy-front-proxy.%s.svc.cluster.local", namespace.Name) + externalHostname := utils.FrontProxyExternalHostname(namespace.Name, namingScheme) // deploy rootshard - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, externalHostname) + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, externalHostname) // deploy front-proxy frontProxy := utils.DeployFrontProxy(ctx, t, client, namespace.Name, rootShard.Name, externalHostname) diff --git a/test/e2e/rootshards/proxy_test.go b/test/e2e/rootshards/proxy_test.go index 30fd44ff..6292fc92 100644 --- a/test/e2e/rootshards/proxy_test.go +++ b/test/e2e/rootshards/proxy_test.go @@ -20,7 +20,6 @@ package rootshards import ( "context" - "fmt" "testing" "time" @@ -36,6 +35,7 @@ import ( ctrlruntime "sigs.k8s.io/controller-runtime" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/utils" ) @@ -45,14 +45,15 @@ func TestRootShardProxy(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "rootshard-proxy") // externalHostname must match whatever DeployFrontProxy chooses as the name for the FrontProxy - externalHostname := fmt.Sprintf("front-proxy-front-proxy.%s.svc.cluster.local", namespace.Name) + externalHostname := utils.FrontProxyExternalHostname(namespace.Name, namingScheme) // deploy a root shard incl. etcd - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, externalHostname) + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, externalHostname) // deploy a 2nd shard incl. etcd shardName := "aadvark" diff --git a/test/e2e/rootshards/rootshards_test.go b/test/e2e/rootshards/rootshards_test.go index 3b761a17..111a0e60 100644 --- a/test/e2e/rootshards/rootshards_test.go +++ b/test/e2e/rootshards/rootshards_test.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/types" ctrlruntime "sigs.k8s.io/controller-runtime" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/utils" ) @@ -40,9 +41,10 @@ func TestCreateRootShard(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "create-rootshard") - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "") + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "") configSecretName := "kubeconfig" diff --git a/test/e2e/shards/shards_test.go b/test/e2e/shards/shards_test.go index 4977be6b..6eb1729f 100644 --- a/test/e2e/shards/shards_test.go +++ b/test/e2e/shards/shards_test.go @@ -34,6 +34,7 @@ import ( ctrlruntime "sigs.k8s.io/controller-runtime" resources "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/utils" ) @@ -43,12 +44,13 @@ func TestCreateShard(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() // create namespace namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "create-shard") // deploy a root shard incl. etcd - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "") + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "") // deploy a 2nd shard incl. etcd shardName := "aadvark" @@ -149,12 +151,13 @@ func TestShardBundleAnnotation(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() // create namespace namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "shard-bundle-annotation") // deploy a root shard incl. etcd - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, "") + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, "") // deploy a shard without bundle annotation first shardName := "annotated-shard" @@ -162,7 +165,7 @@ func TestShardBundleAnnotation(t *testing.T) { shard := utils.DeployShard(ctx, t, client, namespace.Name, shardName, rootShard.Name) // verify no bundle exists yet - bundleName := fmt.Sprintf("%s-bundle", shard.Name) + bundleName := namingScheme.BundleName(shard.Name) bundle := &operatorv1alpha1.Bundle{} err := client.Get(ctx, types.NamespacedName{ Namespace: namespace.Name, @@ -277,29 +280,29 @@ bundleReady: // verify specific expected objects exist expectedObjectsList := []string{ // CA certificates from RootShard (6 objects) - fmt.Sprintf("secrets.core.v1:%s/%s-front-proxy-client-ca", namespace.Name, rootShard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-requestheader-client-ca", namespace.Name, rootShard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-server-ca", namespace.Name, rootShard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-ca", namespace.Name, rootShard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-client-ca", namespace.Name, rootShard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-service-account-ca", namespace.Name, rootShard.Name), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.RootShardCAName(&rootShard, operatorv1alpha1.RootCA)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.RootShardCAName(&rootShard, operatorv1alpha1.ServerCA)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.RootShardCAName(&rootShard, operatorv1alpha1.RequestHeaderClientCA)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.RootShardCAName(&rootShard, operatorv1alpha1.ClientCA)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.RootShardCAName(&rootShard, operatorv1alpha1.ServiceAccountCA)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.RootShardCAName(&rootShard, operatorv1alpha1.FrontProxyClientCA)), // Shard-specific certificates and secrets (9 objects) - fmt.Sprintf("secrets.core.v1:%s/%s-logical-cluster-admin", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-logical-cluster-admin-kubeconfig", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-client-kubeconfig", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-external-logical-cluster-admin", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-external-logical-cluster-admin-kubeconfig", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-virtual-workspaces", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-client", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-server", namespace.Name, shard.Name), - fmt.Sprintf("secrets.core.v1:%s/%s-service-account", namespace.Name, shard.Name), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardCertificateName(&shard, operatorv1alpha1.LogicalClusterAdminCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardKubeconfigSecret(&shard, operatorv1alpha1.LogicalClusterAdminCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardCertificateName(&shard, operatorv1alpha1.ClientCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardKubeconfigSecret(&shard, operatorv1alpha1.ClientCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardCertificateName(&shard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardKubeconfigSecret(&shard, operatorv1alpha1.ExternalLogicalClusterAdminCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardCertificateName(&shard, operatorv1alpha1.ServerCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardCertificateName(&shard, operatorv1alpha1.VirtualWorkspacesCertificate)), + fmt.Sprintf("secrets.core.v1:%s/%s", namespace.Name, namingScheme.ShardCertificateName(&shard, operatorv1alpha1.ServiceAccountCertificate)), // Deployment (1 object) - fmt.Sprintf("deployments.apps.v1:%s/%s-shard-kcp", namespace.Name, shard.Name), + fmt.Sprintf("deployments.apps.v1:%s/%s", namespace.Name, namingScheme.ShardDeploymentName(&shard)), // Service (1 object) - fmt.Sprintf("services.core.v1:%s/%s-shard-kcp", namespace.Name, shard.Name), + fmt.Sprintf("services.core.v1:%s/%s", namespace.Name, namingScheme.ShardServiceName(&shard)), } t.Log("Verifying all expected objects are present in bundle...") diff --git a/test/e2e/virtualworkspaces/virtualworkspaces_test.go b/test/e2e/virtualworkspaces/virtualworkspaces_test.go index 9ed9aa37..695eba94 100644 --- a/test/e2e/virtualworkspaces/virtualworkspaces_test.go +++ b/test/e2e/virtualworkspaces/virtualworkspaces_test.go @@ -42,6 +42,7 @@ import ( ctrlruntime "sigs.k8s.io/controller-runtime" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" "github.com/kcp-dev/kcp-operator/test/conformance" "github.com/kcp-dev/kcp-operator/test/utils" @@ -63,12 +64,13 @@ func TestExternalVirtualWorkspace(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "external-vw") // Deploy the standalone, external kcp virtual-workspace (do not wait for readiness since both // the root shard and the VW will need to have a little dance before both are ready). - vw := utils.DeployVirtualWorkspace(ctx, t, client, namespace.Name, "kcp-vw", false, func(vw *operatorv1alpha1.VirtualWorkspace) { + vw := utils.DeployVirtualWorkspace(ctx, t, client, namingScheme, namespace.Name, "kcp-vw", false, func(vw *operatorv1alpha1.VirtualWorkspace) { vw.Spec.Target.RootShardRef = &corev1.LocalObjectReference{ Name: "r00t", // Must match the rootshard name } @@ -76,10 +78,10 @@ func TestExternalVirtualWorkspace(t *testing.T) { }) // externalHostname must match whatever DeployFrontProxy chooses as the name for the FrontProxy - externalHostname := fmt.Sprintf("front-proxy-front-proxy.%s.svc.cluster.local", namespace.Name) + externalHostname := utils.FrontProxyExternalHostname(namespace.Name, namingScheme) // Deploy a root shard that uses the external VW (in-process VW is disabled). - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, externalHostname, func(rs *operatorv1alpha1.RootShard) { + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, externalHostname, func(rs *operatorv1alpha1.RootShard) { // Point the root shard to use our external VW instead of the in-process one. rs.Spec.KCPVirtualWorkspace = &corev1.LocalObjectReference{ Name: vw.Name, @@ -95,16 +97,8 @@ func TestExternalVirtualWorkspace(t *testing.T) { fp.Spec.Resources = lowResourceRequirements() }) - // Wait for the VirtualWorkspace pods to be ready. t.Log("Waiting for VirtualWorkspace pods to be ready...") - vwPodOpts := []ctrlruntimeclient.ListOption{ - ctrlruntimeclient.InNamespace(vw.Namespace), - ctrlruntimeclient.MatchingLabels{ - "app.kubernetes.io/component": "virtual-workspace", - "app.kubernetes.io/instance": vw.Name, - }, - } - utils.WaitForPods(t, ctx, client, vwPodOpts...) + waitForVirtualWorkspacePods(t, ctx, client, namingScheme, &vw) // Create a kubeconfig to access the root shard. configSecretName := "kubeconfig" @@ -193,7 +187,7 @@ func TestExternalVirtualWorkspace(t *testing.T) { } // Set up port forwarding to the VirtualWorkspace service - vwServiceName := fmt.Sprintf("%s-virtual-workspace", vw.Name) + vwServiceName := namingScheme.VirtualWorkspaceServiceName(&vw) vwLocalPortStr, err := server.GetFreePort(t) if err != nil { t.Fatalf("Failed to get free port: %v", err) @@ -251,6 +245,7 @@ func TestMultipleShardsWithExternalVirtualWorkspacesAndExtCache(t *testing.T) { client := utils.GetKubeClient(t) ctx := context.Background() + namingScheme := naming.NewVersion1() namespace := utils.CreateSelfDestructingNamespace(t, ctx, client, "multi-shard-extcache-vw") @@ -258,7 +253,7 @@ func TestMultipleShardsWithExternalVirtualWorkspacesAndExtCache(t *testing.T) { cacheServer := utils.DeployCacheServer(ctx, t, client, namespace.Name) // Deploy external VirtualWorkspace for root shard - rootVW := utils.DeployVirtualWorkspace(ctx, t, client, namespace.Name, "root-vw", false, func(vw *operatorv1alpha1.VirtualWorkspace) { + rootVW := utils.DeployVirtualWorkspace(ctx, t, client, namingScheme, namespace.Name, "root-vw", false, func(vw *operatorv1alpha1.VirtualWorkspace) { vw.Spec.Target.RootShardRef = &corev1.LocalObjectReference{ Name: "r00t", } @@ -266,10 +261,10 @@ func TestMultipleShardsWithExternalVirtualWorkspacesAndExtCache(t *testing.T) { }) // externalHostname must match whatever DeployFrontProxy chooses as the name for the FrontProxy - externalHostname := fmt.Sprintf("front-proxy-front-proxy.%s.svc.cluster.local", namespace.Name) + externalHostname := utils.FrontProxyExternalHostname(namespace.Name, namingScheme) // Deploy root shard with external VW - rootShard := utils.DeployRootShard(ctx, t, client, namespace.Name, externalHostname, func(rs *operatorv1alpha1.RootShard) { + rootShard := utils.DeployRootShard(ctx, t, client, namingScheme, namespace.Name, externalHostname, func(rs *operatorv1alpha1.RootShard) { rs.Spec.KCPVirtualWorkspace = &corev1.LocalObjectReference{ Name: rootVW.Name, } @@ -283,7 +278,7 @@ func TestMultipleShardsWithExternalVirtualWorkspacesAndExtCache(t *testing.T) { }) // Deploy external VirtualWorkspace for regular shard1 - shard1VW := utils.DeployVirtualWorkspace(ctx, t, client, namespace.Name, "shard1-vw", false, func(vw *operatorv1alpha1.VirtualWorkspace) { + shard1VW := utils.DeployVirtualWorkspace(ctx, t, client, namingScheme, namespace.Name, "shard1-vw", false, func(vw *operatorv1alpha1.VirtualWorkspace) { vw.Spec.Target.ShardRef = &corev1.LocalObjectReference{ Name: "shard1", } @@ -310,10 +305,10 @@ func TestMultipleShardsWithExternalVirtualWorkspacesAndExtCache(t *testing.T) { // Wait for both VirtualWorkspace pods to be ready t.Log("Waiting for root VirtualWorkspace pods to be ready...") - waitForVirtualWorkspacePods(t, ctx, client, rootVW.Namespace, rootVW.Name) + waitForVirtualWorkspacePods(t, ctx, client, namingScheme, &rootVW) t.Log("Waiting for shard VirtualWorkspace pods to be ready...") - waitForVirtualWorkspacePods(t, ctx, client, shard1VW.Namespace, shard1VW.Name) + waitForVirtualWorkspacePods(t, ctx, client, namingScheme, &shard1VW) // verify the setup test := conformance.NewWorkspaceSchedulingTest(frontProxy.Name, client, namespace.Name) @@ -327,15 +322,12 @@ func TestMultipleShardsWithExternalVirtualWorkspacesAndExtCache(t *testing.T) { } // waitForVirtualWorkspacePods waits for VirtualWorkspace pods to become ready. -func waitForVirtualWorkspacePods(t *testing.T, ctx context.Context, client ctrlruntimeclient.Client, namespace, name string) { +func waitForVirtualWorkspacePods(t *testing.T, ctx context.Context, client ctrlruntimeclient.Client, names naming.Scheme, vw *operatorv1alpha1.VirtualWorkspace) { t.Helper() opts := []ctrlruntimeclient.ListOption{ - ctrlruntimeclient.InNamespace(namespace), - ctrlruntimeclient.MatchingLabels{ - "app.kubernetes.io/component": "virtual-workspace", - "app.kubernetes.io/instance": name, - }, + ctrlruntimeclient.InNamespace(vw.Namespace), + ctrlruntimeclient.MatchingLabels(names.VirtualWorkspaceResourceLabels(vw)), } utils.WaitForPods(t, ctx, client, opts...) } diff --git a/test/utils/deploy.go b/test/utils/deploy.go index ba980b1b..0531d8e9 100644 --- a/test/utils/deploy.go +++ b/test/utils/deploy.go @@ -26,7 +26,7 @@ import ( corev1 "k8s.io/api/core/v1" ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -126,7 +126,7 @@ func DeployShard(ctx context.Context, t *testing.T, client ctrlruntimeclient.Cli return shard } -func DeployRootShard(ctx context.Context, t *testing.T, client ctrlruntimeclient.Client, namespace string, externalHostname string, patches ...func(*operatorv1alpha1.RootShard)) operatorv1alpha1.RootShard { +func DeployRootShard(ctx context.Context, t *testing.T, client ctrlruntimeclient.Client, names naming.Scheme, namespace string, externalHostname string, patches ...func(*operatorv1alpha1.RootShard)) operatorv1alpha1.RootShard { t.Helper() etcd := DeployEtcd(t, "etcd-r00t", namespace) @@ -136,7 +136,7 @@ func DeployRootShard(ctx context.Context, t *testing.T, client ctrlruntimeclient rootShard.Namespace = namespace if externalHostname == "" { - externalHostname = resources.GetRootShardBaseHost(&rootShard) + externalHostname = names.RootShardBaseHost(&rootShard) } rootShard.Spec = operatorv1alpha1.RootShardSpec{ @@ -303,7 +303,7 @@ func DeployCacheServerWithExternalEtcd(ctx context.Context, t *testing.T, client })...) } -func DeployVirtualWorkspace(ctx context.Context, t *testing.T, client ctrlruntimeclient.Client, namespace, name string, waitForReady bool, patches ...func(*operatorv1alpha1.VirtualWorkspace)) operatorv1alpha1.VirtualWorkspace { +func DeployVirtualWorkspace(ctx context.Context, t *testing.T, client ctrlruntimeclient.Client, names naming.Scheme, namespace, name string, waitForReady bool, patches ...func(*operatorv1alpha1.VirtualWorkspace)) operatorv1alpha1.VirtualWorkspace { t.Helper() vw := operatorv1alpha1.VirtualWorkspace{} @@ -311,7 +311,7 @@ func DeployVirtualWorkspace(ctx context.Context, t *testing.T, client ctrlruntim vw.Namespace = namespace vw.Spec = operatorv1alpha1.VirtualWorkspaceSpec{ External: operatorv1alpha1.ExternalConfig{ - Hostname: resources.GetVirtualWorkspaceBaseHost(&vw), + Hostname: names.VirtualWorkspaceBaseHost(&vw), Port: 6443, }, } diff --git a/test/utils/utils.go b/test/utils/utils.go index 791b4eb4..7129bc50 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -44,6 +44,7 @@ import ( ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kcp-dev/kcp-operator/internal/resources" + "github.com/kcp-dev/kcp-operator/internal/resources/naming" operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1" ) @@ -297,10 +298,12 @@ func ConnectWithRootShardProxy( ) ctrlruntimeclient.Client { t.Helper() + namingScheme := naming.NewVersion1() + // get the secret for the kcp-operator client cert key := types.NamespacedName{ Namespace: rootShard.Namespace, - Name: resources.GetRootShardCertificateName(rootShard, operatorv1alpha1.OperatorCertificate), + Name: namingScheme.RootShardCertificateName(rootShard, operatorv1alpha1.OperatorCertificate), } certSecret := &corev1.Secret{} @@ -311,7 +314,7 @@ func ConnectWithRootShardProxy( // start a port forwarding localPort := getPort(t) servicePort := 6443 - serviceName := resources.GetRootShardProxyServiceName(rootShard) + serviceName := namingScheme.RootShardProxyServiceName(rootShard) SelfDestuctingPortForward(t, ctx, rootShard.Namespace, "svc/"+serviceName, servicePort, localPort) @@ -369,3 +372,15 @@ func GetKcpRelease() *semver.Version { return parsed } + +func FrontProxyExternalHostname(namespace string, names naming.Scheme) string { + fp := &operatorv1alpha1.FrontProxy{} + fp.Name = "front-proxy" // has to match what is used in DeployFrontProxy() + fp.Namespace = namespace + + rs := &operatorv1alpha1.RootShard{} + rs.Name = "r00t" // has to match what is used in DeployRootShard() + rs.Namespace = namespace + + return names.FrontProxyBaseHost(fp, rs) +}