From a9108f322afb0e6d748f9390aff8e3718fa35462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20B=C3=A4hler?= Date: Fri, 10 Jan 2025 02:52:08 +0100 Subject: [PATCH] feat: add globalproxysettings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Oliver Bähler --- .../workflows/{e2e.yaml => e2e-legacy.yaml} | 0 .github/workflows/e2e.yml | 56 +++++ .golangci.yml | 16 +- Makefile | 52 ++-- PROJECT | 11 + api/v1beta1/clusterresoure.go | 1 + api/v1beta1/globalproxysettings_types.go | 60 +++++ api/v1beta1/zz_generated.deepcopy.go | 122 +++++++++ charts/capsule-proxy/README.md | 8 +- ...apsule.clastix.io_globalproxysettings.yaml | 175 +++++++++++++ .../capsule.clastix.io_proxysettings.yaml | 6 +- charts/capsule-proxy/templates/_pod.tpl | 3 + charts/capsule-proxy/values.yaml | 7 +- config/deploy/sidecar-setup.yaml | 238 ------------------ config/deploy/standalone-setup.yaml | 124 --------- .../samples/capsule_v1beta1_proxysetting.yaml | 12 - .../curl-http-tests/00_root.bats | 0 .../curl-http-tests/namespaces/get.bats | 0 .../curl-http-tests/namespaces/list.bats | 0 .../curl-https-tests/00_root.bats | 0 .../curl-https-tests/namespaces/get.bats | 0 .../curl-https-tests/namespaces/list.bats | 0 e2e-legacy/kind.yaml | 15 ++ .../kubectl-http-tests/00_root.bats | 0 .../kubectl-http-tests/namespaces/list.bats | 0 .../kubectl-https-tests/00_root.bats | 0 .../ingressclasses/delete.bats | 0 .../ingressclasses/get.bats | 0 .../ingressclasses/list.bats | 0 .../ingressclasses/update.bats | 0 .../kubectl-https-tests/metrics/get.bats | 0 .../kubectl-https-tests/metrics/list.bats | 0 .../namespaces/create.bats | 0 .../kubectl-https-tests/namespaces/list.bats | 0 .../kubectl-https-tests/nodes/delete.bats | 0 .../kubectl-https-tests/nodes/get.bats | 0 .../kubectl-https-tests/nodes/list.bats | 0 .../kubectl-https-tests/nodes/update.bats | 0 .../priorityclasses/delete.bats | 0 .../priorityclasses/get.bats | 0 .../priorityclasses/list.bats | 0 .../priorityclasses/update.bats | 0 .../storageclasses/delete.bats | 0 .../storageclasses/get.bats | 0 .../storageclasses/list.bats | 0 .../storageclasses/update.bats | 0 .../libs/ingressclass_utils.bash | 0 .../libs/namespaces_utils.bash | 0 {e2e => e2e-legacy}/libs/poll.bash | 0 .../libs/priorityclass_utils.bash | 0 .../libs/proxysetting_utils.bash | 0 .../libs/rolebinding_utils.bash | 0 .../libs/serviceaccount_utils.bash | 0 .../libs/storageclass_utils.bash | 0 {e2e => e2e-legacy}/libs/tenants_utils.bash | 0 {e2e => e2e-legacy}/run.bash | 0 e2e/e2e_suite_test.go | 14 ++ e2e/global_settings_test.go | 164 ++++++++++++ e2e/kind.yaml | 9 +- e2e/suite_client_test.go | 58 +++++ e2e/suite_test.go | 50 ++++ e2e/utils_test.go | 67 +++++ go.mod | 64 +++-- go.sum | 67 +++++ internal/indexer/global_proxy_setting.go | 46 ++++ internal/modules/clusterscoped/get.go | 6 +- internal/modules/clusterscoped/list.go | 6 +- internal/modules/ingressclass/get.go | 25 +- internal/modules/ingressclass/list.go | 21 +- internal/modules/lease/get.go | 15 +- internal/modules/metric/get.go | 19 +- internal/modules/metric/list.go | 17 +- internal/modules/module.go | 1 + internal/modules/namespace/get.go | 23 +- internal/modules/namespace/list.go | 19 +- internal/modules/namespace/post.go | 4 + internal/modules/namespaced/catchall.go | 4 + internal/modules/node/get.go | 19 +- internal/modules/node/list.go | 17 +- internal/modules/persistentvolume/get.go | 17 +- internal/modules/persistentvolume/list.go | 17 +- internal/modules/pod/get.go | 17 +- internal/modules/priorityclass/get.go | 21 +- internal/modules/priorityclass/list.go | 20 +- internal/modules/runtimeclass/get.go | 19 +- internal/modules/runtimeclass/list.go | 18 +- internal/modules/storageclass/get.go | 21 +- internal/modules/storageclass/list.go | 19 +- internal/modules/tenants/get.go | 19 +- internal/modules/tenants/list.go | 17 +- .../utils.go => utils/clusterscope.go} | 5 +- internal/tenant/proxytenant.go | 26 ++ .../webserver/middleware/user_in_group.go | 5 +- internal/webserver/webserver.go | 27 +- main.go | 5 + 95 files changed, 1333 insertions(+), 581 deletions(-) rename .github/workflows/{e2e.yaml => e2e-legacy.yaml} (100%) create mode 100644 .github/workflows/e2e.yml create mode 100644 api/v1beta1/globalproxysettings_types.go create mode 100644 charts/capsule-proxy/crds/capsule.clastix.io_globalproxysettings.yaml delete mode 100644 config/deploy/sidecar-setup.yaml delete mode 100644 config/deploy/standalone-setup.yaml delete mode 100644 config/samples/capsule_v1beta1_proxysetting.yaml rename {e2e => e2e-legacy}/curl-http-tests/00_root.bats (100%) rename {e2e => e2e-legacy}/curl-http-tests/namespaces/get.bats (100%) rename {e2e => e2e-legacy}/curl-http-tests/namespaces/list.bats (100%) rename {e2e => e2e-legacy}/curl-https-tests/00_root.bats (100%) rename {e2e => e2e-legacy}/curl-https-tests/namespaces/get.bats (100%) rename {e2e => e2e-legacy}/curl-https-tests/namespaces/list.bats (100%) create mode 100644 e2e-legacy/kind.yaml rename {e2e => e2e-legacy}/kubectl-http-tests/00_root.bats (100%) rename {e2e => e2e-legacy}/kubectl-http-tests/namespaces/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/00_root.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/ingressclasses/delete.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/ingressclasses/get.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/ingressclasses/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/ingressclasses/update.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/metrics/get.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/metrics/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/namespaces/create.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/namespaces/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/nodes/delete.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/nodes/get.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/nodes/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/nodes/update.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/priorityclasses/delete.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/priorityclasses/get.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/priorityclasses/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/priorityclasses/update.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/storageclasses/delete.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/storageclasses/get.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/storageclasses/list.bats (100%) rename {e2e => e2e-legacy}/kubectl-https-tests/storageclasses/update.bats (100%) rename {e2e => e2e-legacy}/libs/ingressclass_utils.bash (100%) rename {e2e => e2e-legacy}/libs/namespaces_utils.bash (100%) rename {e2e => e2e-legacy}/libs/poll.bash (100%) rename {e2e => e2e-legacy}/libs/priorityclass_utils.bash (100%) rename {e2e => e2e-legacy}/libs/proxysetting_utils.bash (100%) rename {e2e => e2e-legacy}/libs/rolebinding_utils.bash (100%) rename {e2e => e2e-legacy}/libs/serviceaccount_utils.bash (100%) rename {e2e => e2e-legacy}/libs/storageclass_utils.bash (100%) rename {e2e => e2e-legacy}/libs/tenants_utils.bash (100%) rename {e2e => e2e-legacy}/run.bash (100%) create mode 100644 e2e/e2e_suite_test.go create mode 100644 e2e/global_settings_test.go create mode 100644 e2e/suite_client_test.go create mode 100644 e2e/suite_test.go create mode 100644 e2e/utils_test.go create mode 100644 internal/indexer/global_proxy_setting.go rename internal/modules/{clusterscoped/utils.go => utils/clusterscope.go} (85%) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e-legacy.yaml similarity index 100% rename from .github/workflows/e2e.yaml rename to .github/workflows/e2e-legacy.yaml diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..1e9bc91 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,56 @@ +name: e2e +permissions: {} + +on: + push: + branches: [ "*" ] + paths: + - '.github/workflows/e2e.yml' + - 'api/**' + - 'controllers/**' + - 'internal/**' + - 'e2e/*' + - 'Dockerfile' + - 'go.*' + - 'main.go' + - 'Makefile' + pull_request: + branches: [ "*" ] + paths: + - '.github/workflows/e2e.yml' + - 'api/**' + - 'controllers/**' + - 'internal/**' + - 'e2e/*' + - 'Dockerfile' + - 'go.*' + - 'main.go' + - 'Makefile' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + kind: + name: Kubernetes + strategy: + fail-fast: false + matrix: + k8s-version: [ 'v1.24.7', 'v1.25.3', 'v1.26.3', 'v1.27.2', 'v1.28.0', 'v1.29.0', 'v1.30.0', 'v1.31.0' ] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + with: + fetch-depth: 0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version-file: 'go.mod' + - uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v3 + with: + version: v3.14.2 + - uses: engineerd/setup-kind@aa272fe2a7309878ffc2a81c56cfe3ef108ae7d0 # v0.5.0 + with: + skipClusterCreation: true + - name: e2e testing + run: KIND_K8S_VERSION=${{ matrix.k8s-version }} make e2e diff --git a/.golangci.yml b/.golangci.yml index 6891e0d..bcbab8f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,9 +11,17 @@ linters-settings: min-occurrences: 2 cyclop: max-complexity: 17 +issues: + exclude-rules: + - path: (.+)_test.go + linters: + - revive + text: "^(dot-imports)" linters: enable-all: true disable: + - err113 + - mnd - errchkjson - errname - forcetypeassert @@ -27,14 +35,10 @@ linters: - nilerr - exhaustruct - depguard - # should be enabled - revive - - err113 - - mnd - nilnil - wsl - perfsprint - - # deprecated - exportloopref - - execinquery \ No newline at end of file + - execinquery + - forcetypeassert diff --git a/Makefile b/Makefile index 218cd17..282e543 100644 --- a/Makefile +++ b/Makefile @@ -106,7 +106,7 @@ helm-docs: docker helm-lint: docker @docker run -v "$(SRC_ROOT):/workdir" --entrypoint /bin/sh quay.io/helmpack/chart-testing:v3.3.1 -c "cd /workdir; ct lint --config .github/configs/ct.yaml --lint-conf .github/configs/lintconf.yaml --all --debug" -helm-test: helm-controller-version kind ct ko-build-all helm-create helm-install helm-destroy +helm-test: helm-controller-version ct ko-build-all helm-create helm-install helm-destroy helm-install: @kubectl apply --server-side=true -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml @@ -114,13 +114,13 @@ helm-install: @kubectl apply --server-side=true -f https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.58.0/bundle.yaml @$(CT) install --config $(SRC_ROOT)/.github/configs/ct.yaml --namespace=capsule-system --all --debug -helm-create: - @kind create cluster --wait=60s --name capsule-charts - @kind load docker-image --name capsule-charts $(CAPSULE_PROXY_IMG):$(VERSION) +helm-create: kind + @$(KIND) create cluster --wait=60s --name capsule-charts + @$(KIND) load docker-image --name capsule-charts $(CAPSULE_PROXY_IMG):$(VERSION) @kubectl create ns capsule-system -helm-destroy: - @kind delete cluster --name capsule-charts +helm-destroy: kind + @$(KIND) delete cluster --name capsule-charts #################### # -- Testing @@ -129,14 +129,18 @@ helm-destroy: .PHONY: e2e e2e: e2e-build e2e-install e2e-exec -.PHONY: e2e-exec -e2e-exec: +.PHONY: e2e-legacy-exec +e2e-legacy-exec: @./e2e/run.bash $${CLIENT_TEST:-kubectl}-$${CAPSULE_PROXY_MODE:-https} +.PHONY: e2e-exec +e2e-exec: ginkgo + $(GINKGO) -v -tags e2e ./e2e + .PHONY: e2e-build -e2e-build: +e2e-build: kind @echo "Building kubernetes env using Kind $${KIND_K8S_VERSION:-v1.27.0}..." - @kind create cluster --name capsule --image kindest/node:$${KIND_K8S_VERSION:-v1.27.0} --config ./e2e/kind.yaml --wait=120s \ + @$(KIND) create cluster --name capsule --image kindest/node:$${KIND_K8S_VERSION:-v1.27.0} --config ./e2e/kind.yaml --wait=120s \ && kubectl taint nodes capsule-worker2 key1=value1:NoSchedule @helm repo add bitnami https://charts.bitnami.com/bitnami @helm repo update @@ -149,13 +153,13 @@ e2e-build: e2e-install: install-capsule install-capsule-proxy rbac-fix .PHONY: e2e-load-image -e2e-load-image: ko-build-all +e2e-load-image: kind ko-build-all @echo "Loading Docker image..." - @kind load docker-image --name capsule --nodes capsule-worker $(CAPSULE_PROXY_IMG):$(VERSION) + @$(KIND) load docker-image --name capsule $(CAPSULE_PROXY_IMG):$(VERSION) .PHONY: e2e-destroy -e2e-destroy: - kind delete cluster --name capsule +e2e-destroy: kind + $(KIND) delete cluster --name capsule install-capsule: @echo "Installing capsule..." @@ -185,7 +189,8 @@ ifeq ($(CAPSULE_PROXY_MODE),http) --set "kind=DaemonSet" \ --set "daemonset.hostNetwork=true" \ --set "serviceMonitor.enabled=false" \ - --set "options.generateCertificates=false" + --set "options.generateCertificates=false" \ + --set "options.extraArgs={--feature-gates=ProxyClusterScoped=true,--feature-gates=ProxyAllNamespaced=true}" else @echo "Running in HTTPS mode" @echo "capsule proxy certificates..." @@ -194,19 +199,19 @@ else && kubectl --namespace capsule-system create secret generic capsule-proxy --from-file=tls.key=./127.0.0.1-key.pem --from-file=tls.crt=./127.0.0.1.pem --from-literal=ca=$$(cat $(ROOTCA) | base64 |tr -d '\n') @echo "kubeconfig configurations..." @cd hack \ - && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- alice oil capsule.clastix.io \ + && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- alice oil projectcapsule.dev,capsule.clastix.io \ && mv alice-oil.kubeconfig alice.kubeconfig \ && KUBECONFIG=alice.kubeconfig kubectl config set clusters.kind-capsule.certificate-authority-data $$(cat $(ROOTCA) | base64 |tr -d '\n') \ && KUBECONFIG=alice.kubeconfig kubectl config set clusters.kind-capsule.server https://127.0.0.1:9001 \ - && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- bob gas capsule.clastix.io \ + && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- bob gas projectcapsule.dev,capsule.clastix.io \ && mv bob-gas.kubeconfig bob.kubeconfig \ && KUBECONFIG=bob.kubeconfig kubectl config set clusters.kind-capsule.certificate-authority-data $$(cat $(ROOTCA) | base64 |tr -d '\n') \ && KUBECONFIG=bob.kubeconfig kubectl config set clusters.kind-capsule.server https://127.0.0.1:9001 \ - && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- joe gas capsule.clastix.io,foo.clastix.io \ + && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- joe gas projectcapsule.dev,capsule.clastix.io,foo.clastix.io \ && mv joe-gas.kubeconfig foo.clastix.io.kubeconfig \ && KUBECONFIG=foo.clastix.io.kubeconfig kubectl config set clusters.kind-capsule.certificate-authority-data $$(cat $(ROOTCA) | base64 |tr -d '\n') \ && KUBECONFIG=foo.clastix.io.kubeconfig kubectl config set clusters.kind-capsule.server https://127.0.0.1:9001 \ - && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- dave soil capsule.clastix.io,bar.clastix.io \ + && curl -s https://raw.githubusercontent.com/projectcapsule/capsule/main/hack/create-user.sh | bash -s -- dave soil projectcapsule.dev,capsule.clastix.io,bar.clastix.io \ && mv dave-soil.kubeconfig dave.kubeconfig \ && kubectl --kubeconfig=dave.kubeconfig config set clusters.kind-capsule.certificate-authority-data $$(cat $(ROOTCA) | base64 |tr -d '\n') \ && kubectl --kubeconfig=dave.kubeconfig config set clusters.kind-capsule.server https://127.0.0.1:9001 @@ -219,8 +224,10 @@ else --set "service.nodePort=" \ --set "kind=DaemonSet" \ --set "daemonset.hostNetwork=true" \ - --set "serviceMonitor.enabled=false" + --set "serviceMonitor.enabled=false" \ + --set "options.extraArgs={--feature-gates=ProxyClusterScoped=true,--feature-gates=ProxyAllNamespaced=true}" endif + @kubectl rollout restart ds capsule-proxy -n capsule-system || true rbac-fix: @echo "RBAC customization..." @@ -258,6 +265,11 @@ CONTROLLER_GEN_VERSION = v0.8.0 controller-gen: ## Download controller-gen locally if necessary. $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION)) +GINKGO := $(shell pwd)/bin/ginkgo +GINKGO_VERSION = v2.19.0 +ginkgo: ## Download ginkgo locally if necessary. + $(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo@$(GINKGO_VERSION)) + MKCERT = $(shell pwd)/bin/mkcert MKCERT_VERSION = v1.4.4 mkcert: ## Download mkcert locally if necessary. diff --git a/PROJECT b/PROJECT index 6596e15..dd1de41 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: clastix.io layout: - go.kubebuilder.io/v3 @@ -16,4 +20,11 @@ resources: kind: ProxySettings path: github.com/projectcapsule/capsule-proxy/api/v1beta1 version: v1beta1 +- api: + crdVersion: v1 + domain: clastix.io + group: capsule + kind: GlobalProxySettings + path: github.com/projectcapsule/capsule-proxy/api/v1beta1 + version: v1beta1 version: "3" diff --git a/api/v1beta1/clusterresoure.go b/api/v1beta1/clusterresoure.go index c1a8a95..9fe9a44 100644 --- a/api/v1beta1/clusterresoure.go +++ b/api/v1beta1/clusterresoure.go @@ -26,5 +26,6 @@ type ClusterResource struct { Operations []ClusterResourceOperation `json:"operations"` // Select all cluster scoped resources with the given label selector. + // Defining a selector which does not match any resources is considered not selectable (eg. using operation NotExists). Selector *metav1.LabelSelector `json:"selector"` } diff --git a/api/v1beta1/globalproxysettings_types.go b/api/v1beta1/globalproxysettings_types.go new file mode 100644 index 0000000..88c5172 --- /dev/null +++ b/api/v1beta1/globalproxysettings_types.go @@ -0,0 +1,60 @@ +// Copyright 2020-2023 Project Capsule Authors. +// SPDX-License-Identifier: Apache-2.0 + +package v1beta1 + +import ( + "github.com/projectcapsule/capsule/api/v1beta2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GlobalProxySettingsSpec defines the desired state of GlobalProxySettings. +type GlobalProxySettingsSpec struct { + // Subjects that should receive additional permissions. + // The subjects are selected based on the oncoming requests. They don't have to relate to an existing tenant. + // However they must be part of the capsule-user groups. + // +kubebuilder:validation:MinItems=1 + Rules []GlobalSubjectSpec `json:"rules"` +} + +type GlobalSubjectSpec struct { + // Subjects that should receive additional permissions. + // The subjects are selected based on the oncoming requests. They don't have to relate to an existing tenant. + // However they must be part of the capsule-user groups. + Subjects []GlobalSubject `json:"subjects"` + // Cluster Resources for tenant Owner. + ClusterResources []ClusterResource `json:"clusterResources,omitempty"` +} + +type GlobalSubject struct { + // Kind of tenant owner. Possible values are "User", "Group", and "ServiceAccount". + Kind v1beta2.OwnerKind `json:"kind"` + // Name of tenant owner. + Name string `json:"name"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Cluster + +// GlobalProxySettings is the Schema for the globalproxysettings API. +type GlobalProxySettings struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec GlobalProxySettingsSpec `json:"spec,omitempty"` +} + +//+kubebuilder:object:root=true + +// GlobalProxySettingsList contains a list of GlobalProxySettings. +type GlobalProxySettingsList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []GlobalProxySettings `json:"items"` +} + +//nolint:gochecknoinits +func init() { + SchemeBuilder.Register(&GlobalProxySettings{}, &GlobalProxySettingsList{}) +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 09bd9d3..c209456 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -49,6 +49,128 @@ func (in *ClusterResource) DeepCopy() *ClusterResource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalProxySettings) DeepCopyInto(out *GlobalProxySettings) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalProxySettings. +func (in *GlobalProxySettings) DeepCopy() *GlobalProxySettings { + if in == nil { + return nil + } + out := new(GlobalProxySettings) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalProxySettings) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalProxySettingsList) DeepCopyInto(out *GlobalProxySettingsList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GlobalProxySettings, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalProxySettingsList. +func (in *GlobalProxySettingsList) DeepCopy() *GlobalProxySettingsList { + if in == nil { + return nil + } + out := new(GlobalProxySettingsList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalProxySettingsList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalProxySettingsSpec) DeepCopyInto(out *GlobalProxySettingsSpec) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]GlobalSubjectSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalProxySettingsSpec. +func (in *GlobalProxySettingsSpec) DeepCopy() *GlobalProxySettingsSpec { + if in == nil { + return nil + } + out := new(GlobalProxySettingsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalSubject) DeepCopyInto(out *GlobalSubject) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalSubject. +func (in *GlobalSubject) DeepCopy() *GlobalSubject { + if in == nil { + return nil + } + out := new(GlobalSubject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalSubjectSpec) DeepCopyInto(out *GlobalSubjectSpec) { + *out = *in + if in.Subjects != nil { + in, out := &in.Subjects, &out.Subjects + *out = make([]GlobalSubject, len(*in)) + copy(*out, *in) + } + if in.ClusterResources != nil { + in, out := &in.ClusterResources, &out.ClusterResources + *out = make([]ClusterResource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalSubjectSpec. +func (in *GlobalSubjectSpec) DeepCopy() *GlobalSubjectSpec { + if in == nil { + return nil + } + out := new(GlobalSubjectSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OwnerSpec) DeepCopyInto(out *OwnerSpec) { *out = *in diff --git a/charts/capsule-proxy/README.md b/charts/capsule-proxy/README.md index 3295fb5..4d9c893 100644 --- a/charts/capsule-proxy/README.md +++ b/charts/capsule-proxy/README.md @@ -79,8 +79,10 @@ If you only need to make minor customizations, you can specify them on the comma | Key | Type | Default | Description | |-----|------|---------|-------------| -| crds.install | bool | `false` | Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) | +| crds.annnotations | object | `{}` | Extra Annotations for CRDs | +| crds.install | bool | `true` | Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) | | crds.keep | bool | `true` | Keep the CustomResourceDefinitions (when the chart is deleted) | +| crds.labels | object | `{}` | Extra Labels for CRDs | ### Global Parameters @@ -122,8 +124,10 @@ If you only need to make minor customizations, you can specify them on the comma | Key | Type | Default | Description | |-----|------|---------|-------------| | affinity | object | `{}` | Set affinity rules for the capsule-proxy pod. | -| crds.install | bool | `false` | Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) | +| crds.annnotations | object | `{}` | Extra Annotations for CRDs | +| crds.install | bool | `true` | Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) | | crds.keep | bool | `true` | Keep the CustomResourceDefinitions (when the chart is deleted) | +| crds.labels | object | `{}` | Extra Labels for CRDs | | daemonset.hostNetwork | bool | `false` | Use the host network namespace for capsule-proxy pod. | | daemonset.hostPort | bool | `false` | Binding the capsule-proxy listening port to the host port. | | env | list | `[]` | Additional environment variables | diff --git a/charts/capsule-proxy/crds/capsule.clastix.io_globalproxysettings.yaml b/charts/capsule-proxy/crds/capsule.clastix.io_globalproxysettings.yaml new file mode 100644 index 0000000..f21c92f --- /dev/null +++ b/charts/capsule-proxy/crds/capsule.clastix.io_globalproxysettings.yaml @@ -0,0 +1,175 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: globalproxysettings.capsule.clastix.io +spec: + group: capsule.clastix.io + names: + kind: GlobalProxySettings + listKind: GlobalProxySettingsList + plural: globalproxysettings + singular: globalproxysettings + scope: Cluster + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: GlobalProxySettings is the Schema for the globalproxysettings + API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: GlobalProxySettingsSpec defines the desired state of GlobalProxySettings. + properties: + rules: + description: Subjects that should receive additional permissions. + The subjects are selected based on the oncoming requests. They don't + have to relate to an existing tenant. However they must be part + of the capsule-user groups. + items: + properties: + clusterResources: + description: Cluster Resources for tenant Owner. + items: + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that + contains the resources. If multiple API groups are specified, + any action requested against any resource listed will + be allowed. '*' represents all resources. Empty string + represents v1 api resources. + items: + type: string + type: array + operations: + default: + - List + description: Operations which can be executed on the selected + resources. + items: + enum: + - List + - Update + - Delete + type: string + type: array + resources: + description: Resources is a list of resources this rule + applies to. '*' represents all resources. + items: + type: string + type: array + selector: + description: Select all cluster scoped resources with + the given label selector. Defining a selector which + does not match any resources is considered not selectable + (eg. using operation NotExists). + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + required: + - apiGroups + - operations + - resources + - selector + type: object + type: array + subjects: + description: Subjects that should receive additional permissions. + The subjects are selected based on the oncoming requests. + They don't have to relate to an existing tenant. However they + must be part of the capsule-user groups. + items: + properties: + kind: + description: Kind of tenant owner. Possible values are + "User", "Group", and "ServiceAccount". + enum: + - User + - Group + - ServiceAccount + type: string + name: + description: Name of tenant owner. + type: string + required: + - kind + - name + type: object + type: array + required: + - subjects + type: object + minItems: 1 + type: array + required: + - rules + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/capsule-proxy/crds/capsule.clastix.io_proxysettings.yaml b/charts/capsule-proxy/crds/capsule.clastix.io_proxysettings.yaml index 4db612b..37ef889 100644 --- a/charts/capsule-proxy/crds/capsule.clastix.io_proxysettings.yaml +++ b/charts/capsule-proxy/crds/capsule.clastix.io_proxysettings.yaml @@ -74,7 +74,9 @@ spec: type: array selector: description: Select all cluster scoped resources with - the given label selector. + the given label selector. Defining a selector which + does not match any resources is considered not selectable + (eg. using operation NotExists). properties: matchExpressions: description: matchExpressions is a list of label selector @@ -103,11 +105,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string diff --git a/charts/capsule-proxy/templates/_pod.tpl b/charts/capsule-proxy/templates/_pod.tpl index 3ea6f57..cc0c244 100644 --- a/charts/capsule-proxy/templates/_pod.tpl +++ b/charts/capsule-proxy/templates/_pod.tpl @@ -83,6 +83,9 @@ spec: - name: probe containerPort: 8081 protocol: TCP + - name: pprof + containerPort: 8082 + protocol: TCP {{- if .Values.livenessProbe.enabled }} livenessProbe: {{- toYaml (omit .Values.livenessProbe "enabled") | nindent 6 }} diff --git a/charts/capsule-proxy/values.yaml b/charts/capsule-proxy/values.yaml index 42e12db..254618b 100644 --- a/charts/capsule-proxy/values.yaml +++ b/charts/capsule-proxy/values.yaml @@ -102,9 +102,14 @@ jobs: # Manage CRD Lifecycle crds: # -- Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) - install: false + install: true # -- Keep the CustomResourceDefinitions (when the chart is deleted) keep: true + # -- Extra Labels for CRDs + labels: {} + # -- Extra Annotations for CRDs + annnotations: {} + # Manage RBAC Lifecycle rbac: diff --git a/config/deploy/sidecar-setup.yaml b/config/deploy/sidecar-setup.yaml deleted file mode 100644 index e87b5a5..0000000 --- a/config/deploy/sidecar-setup.yaml +++ /dev/null @@ -1,238 +0,0 @@ - -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: kubernetes-dashboard ---- - -apiVersion: v1 -kind: Secret -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard-csrf - namespace: kubernetes-dashboard -type: Opaque -data: - csrf: "" ---- - -apiVersion: v1 -kind: Secret -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard-key-holder - namespace: kubernetes-dashboard -type: Opaque ---- - -kind: ConfigMap -apiVersion: v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard-settings - namespace: kubernetes-dashboard ---- - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: kubernetes-dashboard -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: kubernetes-dashboard - namespace: kubernetes-dashboard ---- - -kind: Deployment -apiVersion: apps/v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: kubernetes-dashboard -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: kubernetes-dashboard - template: - metadata: - labels: - k8s-app: kubernetes-dashboard - spec: - containers: - - name: proxy - image: clastix/capsule-proxy - imagePullPolicy: Always - args: - - --capsule-user-group=capsule.clastix.io - - --zap-devel - - --zap-log-level=10 - - --enable-ssl=true - - --ssl-cert-path=/opt/certs/tls.crt - - --ssl-key-path=/opt/certs/tls.key - volumeMounts: - - name: proxy-certs - mountPath: /opt/certs - ports: - - containerPort: 9001 - name: http - protocol: TCP - resources: - - name: dashboard - image: kubernetesui/dashboard:v2.0.4 - imagePullPolicy: Always - ports: - - containerPort: 8443 - protocol: TCP - args: - - --auto-generate-certificates - - --namespace=cmp-system - - --tls-cert-file=tls.crt - - --tls-key-file=tls.key - - --apiserver-host=https://localhost:9001 - - --kubeconfig=/opt/.kube/config - volumeMounts: - - name: kubernetes-dashboard-certs - mountPath: /certs - - mountPath: /tmp - name: tmp-volume - - mountPath: /opt/.kube - name: kubeconfig - livenessProbe: - httpGet: - scheme: HTTPS - path: / - port: 8443 - initialDelaySeconds: 30 - timeoutSeconds: 30 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1001 - runAsGroup: 2001 - volumes: - - name: kubernetes-dashboard-certs - secret: - secretName: kubernetes-dashboard-certs - - name: proxy-certs - secret: - secretName: proxy-certs - - name: tmp-volume - emptyDir: {} - - name: kubeconfig - configMap: - defaultMode: 420 - name: kubernetes-dashboard-kubeconfig - serviceAccountName: kubernetes-dashboard ---- - -apiVersion: v1 -kind: ConfigMap -metadata: - name: kubernetes-dashboard-kubeconfig - namespace: kubernetes-dashboard -data: - config: | - kind: Config - apiVersion: v1 - clusters: - - cluster: - insecure-skip-tls-verify: true - server: https://localhost:9001 - name: localhost - contexts: - - context: - cluster: localhost - user: kubernetes-admin - name: admin@localhost - current-context: admin@localhost - preferences: {} - users: - - name: kubernetes-admin - user: - client-certificate-data: REDACTED - client-key-data: REDACTED ---- - -kind: Service -apiVersion: v1 -metadata: - labels: - k8s-app: dashboard-metrics-scraper - name: dashboard-metrics-scraper - namespace: kubernetes-dashboard -spec: - ports: - - port: 8000 - targetPort: 8000 - selector: - k8s-app: dashboard-metrics-scraper ---- - -kind: Service -apiVersion: v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: kubernetes-dashboard -spec: - ports: - - port: 443 - targetPort: 8443 - selector: - k8s-app: kubernetes-dashboard ---- - -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: kubernetes-dashboard - namespace: kubernetes-dashboard - annotations: - ingress.kubernetes.io/ssl-passthrough: "true" - ingress.kubernetes.io: ssl-redirect -spec: - rules: - - host: dashboard.clastix.io - http: - paths: - - backend: - serviceName: kubernetes-dashboard - servicePort: 443 - path: / ---- - -apiVersion: v1 -data: - tls.crt: REDACTED - tls.key: REDACTED -kind: Secret -metadata: - name: kubernetes-dashboard-certs - namespace: kubernetes-dashboard -type: Opaque - ---- - -apiVersion: v1 -data: - tls.crt: REDACTED - tls.key: REDACTED -kind: Secret -metadata: - name: proxy-certs - namespace: kubernetes-dashboard -type: Opaque diff --git a/config/deploy/standalone-setup.yaml b/config/deploy/standalone-setup.yaml deleted file mode 100644 index 881fb6c..0000000 --- a/config/deploy/standalone-setup.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: proxy - namespace: capsule-system ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: proxy - labels: - run: proxy -subjects: -- kind: ServiceAccount - name: proxy - namespace: capsule-system -roleRef: - kind: ClusterRole - name: cluster-admin - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - run: proxy - name: proxy - namespace: capsule-system -spec: - replicas: 1 - selector: - matchLabels: - run: proxy - template: - metadata: - labels: - run: proxy - spec: - restartPolicy: Always - serviceAccountName: proxy - hostNetwork: false - volumes: - - configMap: - defaultMode: 420 - name: certs - name: certs - containers: - - name: proxy - image: clastix/capsule-proxy - imagePullPolicy: IfNotPresent - args: - - --capsule-user-group=capsule.clastix.io - - --zap-log-level=5 - - --enable-ssl=true - - --ssl-cert-path=/opt/certs/tls.crt - - --ssl-key-path=/opt/certs/tls.key - ports: - - containerPort: 9001 - name: proxy - protocol: TCP - - containerPort: 8080 - name: metrics - protocol: TCP - - containerPort: 8081 - name: probe - protocol: TCP - readinessProbe: - httpGet: - path: /readyz/ - port: probe - scheme: HTTP - livenessProbe: - httpGet: - path: /healthz/ - port: probe - scheme: HTTP - resources: - volumeMounts: - - mountPath: /opt/certs - name: certs ---- -apiVersion: v1 -kind: Service -metadata: - name: proxy - namespace: capsule-system - labels: - run: proxy -spec: - ports: - - protocol: TCP - port: 9001 - targetPort: 9001 - selector: - run: proxy ---- -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - namespace: capsule-system - name: proxy - labels: - run: proxy - annotations: - ingress.kubernetes.io/ssl-passthrough: "true" -spec: - rules: - - host: kube.clastix.io - http: - paths: - - path: / - backend: - serviceName: proxy - servicePort: 9001 ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: certs - namespace: capsule-system -data: - tls.crt: REDACTED - tls.key: REDACTED diff --git a/config/samples/capsule_v1beta1_proxysetting.yaml b/config/samples/capsule_v1beta1_proxysetting.yaml deleted file mode 100644 index c0f4931..0000000 --- a/config/samples/capsule_v1beta1_proxysetting.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: capsule.clastix.io/v1beta1 -kind: ProxySetting -metadata: - name: nodes-tenant-reader -spec: - subjects: - - kind: User - name: bob - proxySettings: - - kind: Nodes - operations: - - List diff --git a/e2e/curl-http-tests/00_root.bats b/e2e-legacy/curl-http-tests/00_root.bats similarity index 100% rename from e2e/curl-http-tests/00_root.bats rename to e2e-legacy/curl-http-tests/00_root.bats diff --git a/e2e/curl-http-tests/namespaces/get.bats b/e2e-legacy/curl-http-tests/namespaces/get.bats similarity index 100% rename from e2e/curl-http-tests/namespaces/get.bats rename to e2e-legacy/curl-http-tests/namespaces/get.bats diff --git a/e2e/curl-http-tests/namespaces/list.bats b/e2e-legacy/curl-http-tests/namespaces/list.bats similarity index 100% rename from e2e/curl-http-tests/namespaces/list.bats rename to e2e-legacy/curl-http-tests/namespaces/list.bats diff --git a/e2e/curl-https-tests/00_root.bats b/e2e-legacy/curl-https-tests/00_root.bats similarity index 100% rename from e2e/curl-https-tests/00_root.bats rename to e2e-legacy/curl-https-tests/00_root.bats diff --git a/e2e/curl-https-tests/namespaces/get.bats b/e2e-legacy/curl-https-tests/namespaces/get.bats similarity index 100% rename from e2e/curl-https-tests/namespaces/get.bats rename to e2e-legacy/curl-https-tests/namespaces/get.bats diff --git a/e2e/curl-https-tests/namespaces/list.bats b/e2e-legacy/curl-https-tests/namespaces/list.bats similarity index 100% rename from e2e/curl-https-tests/namespaces/list.bats rename to e2e-legacy/curl-https-tests/namespaces/list.bats diff --git a/e2e-legacy/kind.yaml b/e2e-legacy/kind.yaml new file mode 100644 index 0000000..8d9ceba --- /dev/null +++ b/e2e-legacy/kind.yaml @@ -0,0 +1,15 @@ +apiVersion: kind.x-k8s.io/v1alpha4 +networking: + apiServerAddress: "127.0.0.1" + apiServerPort: 6443 +kind: Cluster +nodes: + - role: control-plane + - role: worker + extraPortMappings: + - hostPort: 9001 + containerPort: 9001 + - role: worker + extraPortMappings: + - hostPort: 9002 + containerPort: 9002 diff --git a/e2e/kubectl-http-tests/00_root.bats b/e2e-legacy/kubectl-http-tests/00_root.bats similarity index 100% rename from e2e/kubectl-http-tests/00_root.bats rename to e2e-legacy/kubectl-http-tests/00_root.bats diff --git a/e2e/kubectl-http-tests/namespaces/list.bats b/e2e-legacy/kubectl-http-tests/namespaces/list.bats similarity index 100% rename from e2e/kubectl-http-tests/namespaces/list.bats rename to e2e-legacy/kubectl-http-tests/namespaces/list.bats diff --git a/e2e/kubectl-https-tests/00_root.bats b/e2e-legacy/kubectl-https-tests/00_root.bats similarity index 100% rename from e2e/kubectl-https-tests/00_root.bats rename to e2e-legacy/kubectl-https-tests/00_root.bats diff --git a/e2e/kubectl-https-tests/ingressclasses/delete.bats b/e2e-legacy/kubectl-https-tests/ingressclasses/delete.bats similarity index 100% rename from e2e/kubectl-https-tests/ingressclasses/delete.bats rename to e2e-legacy/kubectl-https-tests/ingressclasses/delete.bats diff --git a/e2e/kubectl-https-tests/ingressclasses/get.bats b/e2e-legacy/kubectl-https-tests/ingressclasses/get.bats similarity index 100% rename from e2e/kubectl-https-tests/ingressclasses/get.bats rename to e2e-legacy/kubectl-https-tests/ingressclasses/get.bats diff --git a/e2e/kubectl-https-tests/ingressclasses/list.bats b/e2e-legacy/kubectl-https-tests/ingressclasses/list.bats similarity index 100% rename from e2e/kubectl-https-tests/ingressclasses/list.bats rename to e2e-legacy/kubectl-https-tests/ingressclasses/list.bats diff --git a/e2e/kubectl-https-tests/ingressclasses/update.bats b/e2e-legacy/kubectl-https-tests/ingressclasses/update.bats similarity index 100% rename from e2e/kubectl-https-tests/ingressclasses/update.bats rename to e2e-legacy/kubectl-https-tests/ingressclasses/update.bats diff --git a/e2e/kubectl-https-tests/metrics/get.bats b/e2e-legacy/kubectl-https-tests/metrics/get.bats similarity index 100% rename from e2e/kubectl-https-tests/metrics/get.bats rename to e2e-legacy/kubectl-https-tests/metrics/get.bats diff --git a/e2e/kubectl-https-tests/metrics/list.bats b/e2e-legacy/kubectl-https-tests/metrics/list.bats similarity index 100% rename from e2e/kubectl-https-tests/metrics/list.bats rename to e2e-legacy/kubectl-https-tests/metrics/list.bats diff --git a/e2e/kubectl-https-tests/namespaces/create.bats b/e2e-legacy/kubectl-https-tests/namespaces/create.bats similarity index 100% rename from e2e/kubectl-https-tests/namespaces/create.bats rename to e2e-legacy/kubectl-https-tests/namespaces/create.bats diff --git a/e2e/kubectl-https-tests/namespaces/list.bats b/e2e-legacy/kubectl-https-tests/namespaces/list.bats similarity index 100% rename from e2e/kubectl-https-tests/namespaces/list.bats rename to e2e-legacy/kubectl-https-tests/namespaces/list.bats diff --git a/e2e/kubectl-https-tests/nodes/delete.bats b/e2e-legacy/kubectl-https-tests/nodes/delete.bats similarity index 100% rename from e2e/kubectl-https-tests/nodes/delete.bats rename to e2e-legacy/kubectl-https-tests/nodes/delete.bats diff --git a/e2e/kubectl-https-tests/nodes/get.bats b/e2e-legacy/kubectl-https-tests/nodes/get.bats similarity index 100% rename from e2e/kubectl-https-tests/nodes/get.bats rename to e2e-legacy/kubectl-https-tests/nodes/get.bats diff --git a/e2e/kubectl-https-tests/nodes/list.bats b/e2e-legacy/kubectl-https-tests/nodes/list.bats similarity index 100% rename from e2e/kubectl-https-tests/nodes/list.bats rename to e2e-legacy/kubectl-https-tests/nodes/list.bats diff --git a/e2e/kubectl-https-tests/nodes/update.bats b/e2e-legacy/kubectl-https-tests/nodes/update.bats similarity index 100% rename from e2e/kubectl-https-tests/nodes/update.bats rename to e2e-legacy/kubectl-https-tests/nodes/update.bats diff --git a/e2e/kubectl-https-tests/priorityclasses/delete.bats b/e2e-legacy/kubectl-https-tests/priorityclasses/delete.bats similarity index 100% rename from e2e/kubectl-https-tests/priorityclasses/delete.bats rename to e2e-legacy/kubectl-https-tests/priorityclasses/delete.bats diff --git a/e2e/kubectl-https-tests/priorityclasses/get.bats b/e2e-legacy/kubectl-https-tests/priorityclasses/get.bats similarity index 100% rename from e2e/kubectl-https-tests/priorityclasses/get.bats rename to e2e-legacy/kubectl-https-tests/priorityclasses/get.bats diff --git a/e2e/kubectl-https-tests/priorityclasses/list.bats b/e2e-legacy/kubectl-https-tests/priorityclasses/list.bats similarity index 100% rename from e2e/kubectl-https-tests/priorityclasses/list.bats rename to e2e-legacy/kubectl-https-tests/priorityclasses/list.bats diff --git a/e2e/kubectl-https-tests/priorityclasses/update.bats b/e2e-legacy/kubectl-https-tests/priorityclasses/update.bats similarity index 100% rename from e2e/kubectl-https-tests/priorityclasses/update.bats rename to e2e-legacy/kubectl-https-tests/priorityclasses/update.bats diff --git a/e2e/kubectl-https-tests/storageclasses/delete.bats b/e2e-legacy/kubectl-https-tests/storageclasses/delete.bats similarity index 100% rename from e2e/kubectl-https-tests/storageclasses/delete.bats rename to e2e-legacy/kubectl-https-tests/storageclasses/delete.bats diff --git a/e2e/kubectl-https-tests/storageclasses/get.bats b/e2e-legacy/kubectl-https-tests/storageclasses/get.bats similarity index 100% rename from e2e/kubectl-https-tests/storageclasses/get.bats rename to e2e-legacy/kubectl-https-tests/storageclasses/get.bats diff --git a/e2e/kubectl-https-tests/storageclasses/list.bats b/e2e-legacy/kubectl-https-tests/storageclasses/list.bats similarity index 100% rename from e2e/kubectl-https-tests/storageclasses/list.bats rename to e2e-legacy/kubectl-https-tests/storageclasses/list.bats diff --git a/e2e/kubectl-https-tests/storageclasses/update.bats b/e2e-legacy/kubectl-https-tests/storageclasses/update.bats similarity index 100% rename from e2e/kubectl-https-tests/storageclasses/update.bats rename to e2e-legacy/kubectl-https-tests/storageclasses/update.bats diff --git a/e2e/libs/ingressclass_utils.bash b/e2e-legacy/libs/ingressclass_utils.bash similarity index 100% rename from e2e/libs/ingressclass_utils.bash rename to e2e-legacy/libs/ingressclass_utils.bash diff --git a/e2e/libs/namespaces_utils.bash b/e2e-legacy/libs/namespaces_utils.bash similarity index 100% rename from e2e/libs/namespaces_utils.bash rename to e2e-legacy/libs/namespaces_utils.bash diff --git a/e2e/libs/poll.bash b/e2e-legacy/libs/poll.bash similarity index 100% rename from e2e/libs/poll.bash rename to e2e-legacy/libs/poll.bash diff --git a/e2e/libs/priorityclass_utils.bash b/e2e-legacy/libs/priorityclass_utils.bash similarity index 100% rename from e2e/libs/priorityclass_utils.bash rename to e2e-legacy/libs/priorityclass_utils.bash diff --git a/e2e/libs/proxysetting_utils.bash b/e2e-legacy/libs/proxysetting_utils.bash similarity index 100% rename from e2e/libs/proxysetting_utils.bash rename to e2e-legacy/libs/proxysetting_utils.bash diff --git a/e2e/libs/rolebinding_utils.bash b/e2e-legacy/libs/rolebinding_utils.bash similarity index 100% rename from e2e/libs/rolebinding_utils.bash rename to e2e-legacy/libs/rolebinding_utils.bash diff --git a/e2e/libs/serviceaccount_utils.bash b/e2e-legacy/libs/serviceaccount_utils.bash similarity index 100% rename from e2e/libs/serviceaccount_utils.bash rename to e2e-legacy/libs/serviceaccount_utils.bash diff --git a/e2e/libs/storageclass_utils.bash b/e2e-legacy/libs/storageclass_utils.bash similarity index 100% rename from e2e/libs/storageclass_utils.bash rename to e2e-legacy/libs/storageclass_utils.bash diff --git a/e2e/libs/tenants_utils.bash b/e2e-legacy/libs/tenants_utils.bash similarity index 100% rename from e2e/libs/tenants_utils.bash rename to e2e-legacy/libs/tenants_utils.bash diff --git a/e2e/run.bash b/e2e-legacy/run.bash similarity index 100% rename from e2e/run.bash rename to e2e-legacy/run.bash diff --git a/e2e/e2e_suite_test.go b/e2e/e2e_suite_test.go new file mode 100644 index 0000000..d5b8e6c --- /dev/null +++ b/e2e/e2e_suite_test.go @@ -0,0 +1,14 @@ +package e2e_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestE2e(t *testing.T) { + t.Parallel() + RegisterFailHandler(Fail) + RunSpecs(t, "E2e Suite") +} diff --git a/e2e/global_settings_test.go b/e2e/global_settings_test.go new file mode 100644 index 0000000..18081cb --- /dev/null +++ b/e2e/global_settings_test.go @@ -0,0 +1,164 @@ +package e2e_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + + v1beta1 "github.com/projectcapsule/capsule-proxy/api/v1beta1" +) + +var _ = Describe("GlobalProxySettings", func() { + var aliceClient, bobClient *kubernetes.Clientset + + BeforeEach(func() { + var err error + + aliceClient, err = loadKubeConfig("alice") + Expect(err).ToNot(HaveOccurred()) + bobClient, err = loadKubeConfig("bob") + Expect(err).ToNot(HaveOccurred()) + + // Create Global Proxy Settings + settings := []*v1beta1.GlobalProxySettings{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "global-proxy-settings", + Labels: e2eLabels(), + }, + Spec: v1beta1.GlobalProxySettingsSpec{ + Rules: []v1beta1.GlobalSubjectSpec{ + { + ClusterResources: []v1beta1.ClusterResource{ + { + APIGroups: []string{"rbac.authorization.k8s.io/*"}, + Resources: []string{"*"}, + Operations: []v1beta1.ClusterResourceOperation{v1beta1.ClusterResourceOperationList}, + Selector: &metav1.LabelSelector{ + MatchLabels: e2eLabels(), + }, + }, + { + APIGroups: []string{"capsule.clastix.io/*"}, + Resources: []string{"*"}, + Operations: []v1beta1.ClusterResourceOperation{v1beta1.ClusterResourceOperationList}, + Selector: &metav1.LabelSelector{ + MatchLabels: e2eLabels(), + }, + }, + }, + Subjects: []v1beta1.GlobalSubject{ + { + Kind: "User", + Name: "alice", + }, + { + Kind: "User", + Name: "bob", + }, + }, + }, + }, + }, + }, + } + + for _, tran := range settings { + Eventually(func() error { + tran.ResourceVersion = "" + + return k8sClient.Create(context.TODO(), tran) + }).Should(Succeed()) + } + + // Load Alice's kubeconfig + aliceClient, err = loadKubeConfig("alice") + Expect(err).NotTo(HaveOccurred()) + + // Load Bob's kubeconfig + bobClient, err = loadKubeConfig("bob") + Expect(err).NotTo(HaveOccurred()) + }) + + JustAfterEach(func() { + // Define Resources which are lifecycled after each test + resourcesToClean := []client.Object{ + &v1beta1.GlobalProxySettings{}, + &rbacv1.ClusterRole{}, + } + + Eventually(func() error { + return cleanResources(resourcesToClean, e2eSelector()) + }, defaultTimeoutInterval, defaultPollInterval).Should(Succeed()) + }) + + It("Allow listing specific clusterroles (without tenants)", func() { + roles := []*rbacv1.ClusterRole{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "tenant-viewer", + Labels: e2eLabels(), + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"list"}, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "tenant-editor", + Labels: e2eLabels(), + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"*"}, + }, + }, + }, + } + + for _, role := range roles { + Eventually(func() error { + role.ResourceVersion = "" + + return k8sClient.Create(context.Background(), role) + }).Should(Succeed()) + } + + listClusterRoles := func(clientset *kubernetes.Clientset) ([]string, error) { + clusterRoles, err := clientset.RbacV1().ClusterRoles().List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + var roleNames []string + for _, role := range clusterRoles.Items { + roleNames = append(roleNames, role.Name) + } + + return roleNames, nil + } + + // Should only list the clusterroles that are allowed by the GlobalProxySettings + expectedRoles := []string{"tenant-editor", "tenant-viewer"} + + // Check Alice's access to ClusterRoles + Eventually(func() ([]string, error) { + return listClusterRoles(aliceClient) + }).Should(Equal(expectedRoles), "Alice should only have access to the specified cluster roles") + + // Check Bob's access to ClusterRoles (must contain only the expected roles) + Eventually(func() ([]string, error) { + return listClusterRoles(bobClient) + }).Should(Equal(expectedRoles), "Bob should only have access to the specified cluster roles") + }) +}) diff --git a/e2e/kind.yaml b/e2e/kind.yaml index 8d9ceba..075bea1 100644 --- a/e2e/kind.yaml +++ b/e2e/kind.yaml @@ -1,8 +1,8 @@ apiVersion: kind.x-k8s.io/v1alpha4 -networking: - apiServerAddress: "127.0.0.1" - apiServerPort: 6443 kind: Cluster +#networking: +# apiServerAddress: "127.0.0.1" +# apiServerPort: 6443 nodes: - role: control-plane - role: worker @@ -10,6 +10,3 @@ nodes: - hostPort: 9001 containerPort: 9001 - role: worker - extraPortMappings: - - hostPort: 9002 - containerPort: 9002 diff --git a/e2e/suite_client_test.go b/e2e/suite_client_test.go new file mode 100644 index 0000000..c948d78 --- /dev/null +++ b/e2e/suite_client_test.go @@ -0,0 +1,58 @@ +package e2e_test + +import ( + "context" + "time" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type e2eClient struct { + client.Client +} + +func (e *e2eClient) sleep() { + time.Sleep(250 * time.Millisecond) +} + +func (e *e2eClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + defer e.sleep() + + return e.Client.Get(ctx, key, obj, opts...) +} + +func (e *e2eClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + defer e.sleep() + + return e.Client.List(ctx, list, opts...) +} + +func (e *e2eClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + defer e.sleep() + + return e.Client.Create(ctx, obj, opts...) +} + +func (e *e2eClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + defer e.sleep() + + return e.Client.Delete(ctx, obj, opts...) +} + +func (e *e2eClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + defer e.sleep() + + return e.Client.Update(ctx, obj, opts...) +} + +func (e *e2eClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + defer e.sleep() + + return e.Client.Patch(ctx, obj, patch, opts...) +} + +func (e *e2eClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + defer e.sleep() + + return e.Client.DeleteAllOf(ctx, obj, opts...) +} diff --git a/e2e/suite_test.go b/e2e/suite_test.go new file mode 100644 index 0000000..38b33c8 --- /dev/null +++ b/e2e/suite_test.go @@ -0,0 +1,50 @@ +//nolint:all +package e2e_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/rest" + "k8s.io/kubectl/pkg/scheme" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + v1beta1 "github.com/projectcapsule/capsule-proxy/api/v1beta1" +) + +//nolint:gochecknoglobals +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment +) + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + UseExistingCluster: ptr.To(true), + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + Expect(v1beta1.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) + + ctrlClient, err := client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(ctrlClient).ToNot(BeNil()) + + k8sClient = &e2eClient{Client: ctrlClient} +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + Expect(testEnv.Stop()).ToNot(HaveOccurred()) +}) diff --git a/e2e/utils_test.go b/e2e/utils_test.go new file mode 100644 index 0000000..3482eea --- /dev/null +++ b/e2e/utils_test.go @@ -0,0 +1,67 @@ +//nolint:all +package e2e_test + +import ( + "context" + "path/filepath" + "time" + + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + defaultTimeoutInterval = 20 * time.Second + defaultPollInterval = time.Second +) + +// Returns labels to identify e2e resources. +func e2eLabels() map[string]string { + return map[string]string{ + "app.kubernetes.io/env": "e2e", + } +} + +// Returns a label selector to filter e2e resources. +func e2eSelector() labels.Selector { + return labels.SelectorFromSet(e2eLabels()) +} + +// Pass objects which require cleanup and a label selector to filter them. +func cleanResources(res []client.Object, selector labels.Selector) (err error) { + for _, resource := range res { + err = k8sClient.DeleteAllOf(context.TODO(), resource, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: selector, + }, + }) + + if err != nil { + return err + } + } + + return nil +} + +// LoadKubeConfig loads the kubeconfig for a specific user and returns the Kubernetes client. +func loadKubeConfig(user string) (*kubernetes.Clientset, error) { + // Adjust the path to your kubeconfigs + kubeConfigPath := filepath.Join("../hack", user+".kubeconfig") + + // Build the configuration + config, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath) + if err != nil { + return nil, err + } + + // Create the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + return clientset, nil +} diff --git a/go.mod b/go.mod index f149bee..7408d89 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,16 @@ module github.com/projectcapsule/capsule-proxy -go 1.23 +go 1.23.0 + +toolchain go1.23.3 require ( github.com/go-logr/logr v1.4.2 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/gorilla/handlers v1.5.2 github.com/gorilla/mux v1.8.1 + github.com/onsi/ginkgo/v2 v2.21.0 + github.com/onsi/gomega v1.35.1 github.com/pkg/errors v0.9.1 github.com/projectcapsule/capsule v0.7.2 github.com/prometheus/client_golang v1.20.5 @@ -14,14 +18,16 @@ require ( github.com/spf13/pflag v1.0.5 github.com/thediveo/enumflag v0.10.1 go.uber.org/zap v1.27.0 - golang.org/x/net v0.31.0 - k8s.io/api v0.31.3 - k8s.io/apiextensions-apiserver v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/apiserver v0.31.3 - k8s.io/client-go v0.31.3 - k8s.io/component-base v0.31.3 - sigs.k8s.io/controller-runtime v0.19.3 + golang.org/x/net v0.34.0 + k8s.io/api v0.32.0 + k8s.io/apiextensions-apiserver v0.32.0 + k8s.io/apimachinery v0.32.0 + k8s.io/apiserver v0.32.0 + k8s.io/client-go v0.32.0 + k8s.io/component-base v0.32.0 + k8s.io/kubectl v0.31.3 + k8s.io/utils v0.0.0-20241210054802-24370beab758 + sigs.k8s.io/controller-runtime v0.19.4 ) require ( @@ -33,45 +39,53 @@ require ( github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/zapr v1.3.0 // 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/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/uuid v1.6.0 // indirect github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/klauspost/compress v1.17.11 // 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.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.61.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/protobuf v1.36.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240822171749-76de80e0abd9 // indirect - k8s.io/utils v0.0.0-20240821151609-f90d01438635 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 5b8ad2e..bd7ef40 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -20,6 +21,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -35,6 +37,8 @@ github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq 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.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -64,6 +68,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -72,6 +78,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -81,6 +89,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= @@ -110,6 +120,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -124,6 +136,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -141,10 +155,14 @@ github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -166,6 +184,8 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= +github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= @@ -175,6 +195,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -208,6 +229,10 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -223,6 +248,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -239,9 +266,13 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -259,15 +290,23 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -277,6 +316,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= 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= @@ -289,6 +330,8 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -315,27 +358,51 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= +k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= +k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw= k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= +k8s.io/apiserver v0.32.0 h1:VJ89ZvQZ8p1sLeiWdRJpRD6oLozNZD2+qVSLi+ft5Qs= +k8s.io/apiserver v0.32.0/go.mod h1:HFh+dM1/BE/Hm4bS4nTXHVfN6Z6tFIZPi649n83b4Ag= k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU= +k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM= 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-20240822171749-76de80e0abd9 h1:y+4z/s0h3R97P/o/098DSjlpyNpHzGirNPlTL+GHdqY= k8s.io/kube-openapi v0.0.0-20240822171749-76de80e0abd9/go.mod h1:s4yb9FXajAVNRnxSB5Ckpr/oq2LP4mKSMWeZDVppd30= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= +k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= +k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= k8s.io/utils v0.0.0-20240821151609-f90d01438635 h1:2wThSvJoW/Ncn9TmQEYXRnevZXi2duqHWf5OX9S3zjI= k8s.io/utils v0.0.0-20240821151609-f90d01438635/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw= sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM= +sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= +sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/indexer/global_proxy_setting.go b/internal/indexer/global_proxy_setting.go new file mode 100644 index 0000000..41c8d30 --- /dev/null +++ b/internal/indexer/global_proxy_setting.go @@ -0,0 +1,46 @@ +// Copyright 2020-2023 Project Capsule Authors. +// SPDX-License-Identifier: Apache-2.0 + +package indexer + +import ( + "fmt" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/projectcapsule/capsule-proxy/api/v1beta1" +) + +const ( + GlobalKindField = "spec.subjects.ownerkind" +) + +// ProxySetting is the indexer that allows retrieving the Capsule Proxy Settings +// for a specific actor according to its kind. +type GlobalProxySetting struct{} + +func (p GlobalProxySetting) Object() client.Object { + return &v1beta1.GlobalProxySettings{} +} + +func (p GlobalProxySetting) Field() string { + return GlobalKindField +} + +func (p GlobalProxySetting) Func() client.IndexerFunc { + return func(object client.Object) (owners []string) { + proxySetting := object.(*v1beta1.GlobalProxySettings) + + for _, owner := range proxySetting.Spec.Rules { + for _, subject := range owner.Subjects { + if subject.Kind == "" || subject.Name == "" { + continue + } + + owners = append(owners, fmt.Sprintf("%s:%s", subject.Kind.String(), subject.Name)) + } + } + + return + } +} diff --git a/internal/modules/clusterscoped/get.go b/internal/modules/clusterscoped/get.go index 7272bee..8ab2dac 100644 --- a/internal/modules/clusterscoped/get.go +++ b/internal/modules/clusterscoped/get.go @@ -41,6 +41,10 @@ func Get(discovery *discovery.DiscoveryClient, client client.Reader, writer clie } } +func (g get) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{} +} + func (g get) GroupKind() schema.GroupKind { return schema.GroupKind{} } @@ -58,7 +62,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req gvk := utils.GetGVKFromURL(proxyRequest.GetHTTPRequest().URL.Path) - operations, requirements := getRequirements(gvk, proxyTenants) + operations, requirements := utils.GetClusterScopeRequirements(gvk, proxyTenants) if len(requirements) > 0 { // Verify if the list operation is allowed if slices.Contains(operations, v1beta1.ClusterResourceOperationList) { diff --git a/internal/modules/clusterscoped/list.go b/internal/modules/clusterscoped/list.go index c95874e..a0992a8 100644 --- a/internal/modules/clusterscoped/list.go +++ b/internal/modules/clusterscoped/list.go @@ -34,6 +34,10 @@ func List(client client.Reader, writer client.Writer, path string) modules.Modul } } +func (l list) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{} +} + func (l list) GroupKind() schema.GroupKind { return schema.GroupKind{} } @@ -49,7 +53,7 @@ func (l list) Methods() []string { func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Request) (selector labels.Selector, err error) { gvk := utils.GetGVKFromURL(proxyRequest.GetHTTPRequest().URL.Path) - operations, requirements := getRequirements(gvk, proxyTenants) + operations, requirements := utils.GetClusterScopeRequirements(gvk, proxyTenants) if len(requirements) > 0 { // Verify if the list operation is allowed if slices.Contains(operations, v1beta1.ClusterResourceOperationList) { diff --git a/internal/modules/ingressclass/get.go b/internal/modules/ingressclass/get.go index 1f1a5e2..cf7e0cf 100644 --- a/internal/modules/ingressclass/get.go +++ b/internal/modules/ingressclass/get.go @@ -25,24 +25,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("ingressclass_get"), - gk: schema.GroupKind{ - Group: networkingv1.GroupName, - Kind: "ingressclasses", + gk: schema.GroupVersionKind{ + Group: networkingv1.GroupName, + Version: "*", + Kind: "ingressclasses", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/apis/networking.k8s.io/{version}/{endpoint:ingressclasses}/{name}" } @@ -60,19 +65,19 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req if len(requirements) > 0 { ic, errIc := getIngressClassFromRequest(httpRequest) if errIc != nil { - return nil, errors.NewBadRequest(errIc, g.gk) + return nil, errors.NewBadRequest(errIc, g.GroupKind()) } - return utils.HandleGetSelector(httpRequest.Context(), ic, g.client, requirements, name, g.gk) + return utils.HandleGetSelector(httpRequest.Context(), ic, g.client, requirements, name, g.GroupKind()) } icl, err := getIngressClassListFromRequest(httpRequest) if err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } if err = g.client.List(httpRequest.Context(), icl, client.MatchingLabels{corev1.LabelMetadataName: name}); err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } var r *labels.Requirement @@ -83,7 +88,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req switch httpRequest.Method { case http.MethodGet: - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) default: return nil, nil } diff --git a/internal/modules/ingressclass/list.go b/internal/modules/ingressclass/list.go index 328f087..9fc1952 100644 --- a/internal/modules/ingressclass/list.go +++ b/internal/modules/ingressclass/list.go @@ -22,24 +22,29 @@ import ( type list struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { return &list{ client: client, log: ctrl.Log.WithName("ingressclass_list"), - gk: schema.GroupKind{ - Group: networkingv1.GroupName, - Kind: "ingressclasses", + gk: schema.GroupVersionKind{ + Group: networkingv1.GroupName, + Version: "*", + Kind: "ingressclasses", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/apis/networking.k8s.io/{version}/{endpoint:ingressclasses/?}" } @@ -58,18 +63,18 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re icl, err := getIngressClassListFromRequest(httpRequest) if err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } if err = l.client.List(httpRequest.Context(), icl); err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } var r *labels.Requirement if r, err = getIngressClassSelector(icl, exactMatch, regexMatch); err != nil { if !allowed { - return nil, errors.NewNotAllowed(l.gk) + return nil, errors.NewNotAllowed(l.GroupKind()) } r, _ = labels.NewRequirement("dontexistsignoreme", selection.Exists, []string{}) diff --git a/internal/modules/lease/get.go b/internal/modules/lease/get.go index 5ed8a60..bad0684 100644 --- a/internal/modules/lease/get.go +++ b/internal/modules/lease/get.go @@ -22,24 +22,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("node_get"), - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "nodes", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "nodes", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/{name}" } diff --git a/internal/modules/metric/get.go b/internal/modules/metric/get.go index 711dd13..9990f73 100644 --- a/internal/modules/metric/get.go +++ b/internal/modules/metric/get.go @@ -24,24 +24,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("metric_get"), - gk: schema.GroupKind{ - Group: "metrics.k8s.io", - Kind: "nodes", + gk: schema.GroupVersionKind{ + Group: "metrics.k8s.io", + Version: "*", + Kind: "nodes", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/apis/metrics.k8s.io/{version}/nodes/{name}" } @@ -59,7 +64,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req nl := &corev1.NodeList{} if err = g.client.List(httpRequest.Context(), nl, client.MatchingLabels{"kubernetes.io/hostname": name}); err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } var r *labels.Requirement @@ -69,7 +74,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req } if httpRequest.Method == http.MethodGet { - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) } return nil, nil diff --git a/internal/modules/metric/list.go b/internal/modules/metric/list.go index 61abfdf..4b8c975 100644 --- a/internal/modules/metric/list.go +++ b/internal/modules/metric/list.go @@ -22,24 +22,29 @@ import ( type list struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { return &list{ client: client, log: ctrl.Log.WithName("metric_list"), - gk: schema.GroupKind{ - Group: "metrics.k8s.io", - Kind: "nodes", + gk: schema.GroupVersionKind{ + Group: "metrics.k8s.io", + Version: "*", + Kind: "nodes", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/apis/metrics.k8s.io/{version}/{endpoint:nodes/?}" } @@ -55,7 +60,7 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re nl := &corev1.NodeList{} if err = l.client.List(httpRequest.Context(), nl); err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } var r *labels.Requirement diff --git a/internal/modules/module.go b/internal/modules/module.go index 9acebdd..6983f62 100644 --- a/internal/modules/module.go +++ b/internal/modules/module.go @@ -12,6 +12,7 @@ import ( ) type Module interface { + GroupVersionKind() schema.GroupVersionKind GroupKind() schema.GroupKind Path() string Methods() []string diff --git a/internal/modules/namespace/get.go b/internal/modules/namespace/get.go index d5c7421..7fe7435 100644 --- a/internal/modules/namespace/get.go +++ b/internal/modules/namespace/get.go @@ -30,7 +30,7 @@ type get struct { client client.Reader log logr.Logger rbReflector *controllers.RoleBindingReflector - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(roleBindingsReflector *controllers.RoleBindingReflector, client client.Reader) modules.Module { @@ -41,17 +41,22 @@ func Get(roleBindingsReflector *controllers.RoleBindingReflector, client client. client: client, log: ctrl.Log.WithName("namespace_get"), rbReflector: roleBindingsReflector, - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "namespaces", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "namespaces", }, } } -func (l get) GroupKind() schema.GroupKind { +func (l get) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l get) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l get) Path() string { return "/api/v1/namespaces/{name}" } @@ -70,7 +75,7 @@ func (l get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req } // Returning a not found if the Namespace is not owned by a Tenant resource. if len(ns.GetOwnerReferences()) == 0 || ns.GetOwnerReferences()[0].Kind != "Tenant" { - return nil, errors.NewNotFoundError(name, l.gk) + return nil, errors.NewNotFoundError(name, l.GroupKind()) } // Extracting the Tenant name from the owner reference: // in some scenarios Capsule could lag in reconciling the Tenant resources as performing the Namespace metadata @@ -88,14 +93,14 @@ func (l get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req // in case of rolebinding reflector, using the local cache. if l.rbReflector != nil { if userNamespaces, err = l.rbReflector.GetUserNamespacesFromRequest(proxyRequest); err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } if !sets.NewString(userNamespaces...).Has(name) { - return nil, errors.NewNotFoundError(name, l.gk) + return nil, errors.NewNotFoundError(name, l.GroupKind()) } } else if !tenants.Has(tntName) { - return nil, errors.NewNotFoundError(name, l.gk) + return nil, errors.NewNotFoundError(name, l.GroupKind()) } return labels.NewSelector(), nil diff --git a/internal/modules/namespace/list.go b/internal/modules/namespace/list.go index 0786a31..1b5bf6d 100644 --- a/internal/modules/namespace/list.go +++ b/internal/modules/namespace/list.go @@ -23,24 +23,29 @@ import ( type list struct { roleBindingsReflector *controllers.RoleBindingReflector log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(roleBindingsReflector *controllers.RoleBindingReflector) modules.Module { return &list{ roleBindingsReflector: roleBindingsReflector, log: ctrl.Log.WithName("namespace_list"), - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "namespaces", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "namespaces", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return basePath } @@ -55,7 +60,7 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re if l.roleBindingsReflector != nil { userNamespaces, err = l.roleBindingsReflector.GetUserNamespacesFromRequest(proxyRequest) if err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } } else { for _, tnt := range proxyTenants { @@ -73,7 +78,7 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re } if err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } return labels.NewSelector().Add(*r), nil diff --git a/internal/modules/namespace/post.go b/internal/modules/namespace/post.go index a46ee1c..cbb6221 100644 --- a/internal/modules/namespace/post.go +++ b/internal/modules/namespace/post.go @@ -20,6 +20,10 @@ func Post() modules.Module { return &post{} } +func (l post) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{} +} + func (l post) GroupKind() schema.GroupKind { return schema.GroupKind{} } diff --git a/internal/modules/namespaced/catchall.go b/internal/modules/namespaced/catchall.go index ce94eb8..612fd70 100644 --- a/internal/modules/namespaced/catchall.go +++ b/internal/modules/namespaced/catchall.go @@ -30,6 +30,10 @@ func CatchAll(client client.Reader, writer client.Writer, path string) modules.M } } +func (l catchall) GroupVersionKind() schema.GroupVersionKind { + return schema.GroupVersionKind{} +} + func (l catchall) GroupKind() schema.GroupKind { return schema.GroupKind{} } diff --git a/internal/modules/node/get.go b/internal/modules/node/get.go index 1813c1e..a7e7904 100644 --- a/internal/modules/node/get.go +++ b/internal/modules/node/get.go @@ -24,24 +24,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("node_get"), - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "nodes", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "nodes", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/api/v1/nodes/{name}" } @@ -58,7 +63,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req nl := &corev1.NodeList{} if err = g.client.List(httpRequest.Context(), nl, client.MatchingLabels{"kubernetes.io/hostname": name}); err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } var r *labels.Requirement @@ -68,7 +73,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req } if httpRequest.Method == http.MethodGet { - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) } return nil, nil diff --git a/internal/modules/node/list.go b/internal/modules/node/list.go index ade70f3..eb32dc3 100644 --- a/internal/modules/node/list.go +++ b/internal/modules/node/list.go @@ -22,24 +22,29 @@ import ( type list struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { return &list{ client: client, log: ctrl.Log.WithName("node_list"), - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "nodes", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "nodes", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/api/v1/{endpoint:nodes/?}" } @@ -54,7 +59,7 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re nl := &corev1.NodeList{} if err = l.client.List(httpRequest.Context(), nl); err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } var r *labels.Requirement diff --git a/internal/modules/persistentvolume/get.go b/internal/modules/persistentvolume/get.go index 6a35f37..406c6e3 100644 --- a/internal/modules/persistentvolume/get.go +++ b/internal/modules/persistentvolume/get.go @@ -23,7 +23,7 @@ type get struct { client client.Reader log logr.Logger labelKey string - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { @@ -33,17 +33,22 @@ func Get(client client.Reader) modules.Module { client: client, log: ctrl.Log.WithName("persistentvolume_get"), labelKey: label, - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "persistentvolumes", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "persistentvolumes", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/api/v1/{endpoint:persistentvolumes}/{name}" } @@ -61,5 +66,5 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req rc := &corev1.PersistentVolume{} - return utils.HandleGetSelector(httpRequest.Context(), rc, g.client, []labels.Requirement{requirement}, name, g.gk) + return utils.HandleGetSelector(httpRequest.Context(), rc, g.client, []labels.Requirement{requirement}, name, g.GroupKind()) } diff --git a/internal/modules/persistentvolume/list.go b/internal/modules/persistentvolume/list.go index 9ff9a47..a03210e 100644 --- a/internal/modules/persistentvolume/list.go +++ b/internal/modules/persistentvolume/list.go @@ -23,7 +23,7 @@ type list struct { client client.Reader log logr.Logger labelKey string - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { @@ -33,17 +33,22 @@ func List(client client.Reader) modules.Module { client: client, log: ctrl.Log.WithName("persistentvolume_list"), labelKey: label, - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "persistentvolumes", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "persistentvolumes", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/api/v1/{endpoint:persistentvolumes/?}" } @@ -57,7 +62,7 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re allowed, requirement := getPersistentVolume(httpRequest, proxyTenants, l.labelKey) if !allowed { - return nil, errors.NewNotAllowed(l.gk) + return nil, errors.NewNotAllowed(l.GroupKind()) } return utils.HandleListSelector([]labels.Requirement{requirement}) diff --git a/internal/modules/pod/get.go b/internal/modules/pod/get.go index 33125dc..2ffba10 100644 --- a/internal/modules/pod/get.go +++ b/internal/modules/pod/get.go @@ -24,24 +24,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("node_get"), - gk: schema.GroupKind{ - Group: corev1.GroupName, - Kind: "nodes", + gk: schema.GroupVersionKind{ + Group: corev1.GroupName, + Version: "*", + Kind: "nodes", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/api/v1/pods" } @@ -93,7 +98,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req node := &corev1.Node{} if err = g.client.Get(httpRequest.Context(), types.NamespacedName{Name: name}, node); err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } for _, sel := range selectors { diff --git a/internal/modules/priorityclass/get.go b/internal/modules/priorityclass/get.go index 1c8f205..461b5ee 100644 --- a/internal/modules/priorityclass/get.go +++ b/internal/modules/priorityclass/get.go @@ -25,24 +25,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("priorityclass_get"), - gk: schema.GroupKind{ - Group: schedulingv1.GroupName, - Kind: "priorityclasses", + gk: schema.GroupVersionKind{ + Group: schedulingv1.GroupName, + Version: "*", + Kind: "priorityclasses", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/apis/scheduling.k8s.io/v1/{endpoint:priorityclasses}/{name}" } @@ -60,12 +65,12 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req if len(requirements) > 0 { pc := &schedulingv1.PriorityClass{} - return utils.HandleGetSelector(httpRequest.Context(), pc, g.client, requirements, name, g.gk) + return utils.HandleGetSelector(httpRequest.Context(), pc, g.client, requirements, name, g.GroupKind()) } sc := &schedulingv1.PriorityClassList{} if err = g.client.List(httpRequest.Context(), sc, client.MatchingLabels{corev1.LabelMetadataName: name}); err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } var r *labels.Requirement @@ -75,7 +80,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req case err == nil: return labels.NewSelector().Add(*r), nil case httpRequest.Method == http.MethodGet: - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) default: return nil, nil } diff --git a/internal/modules/priorityclass/list.go b/internal/modules/priorityclass/list.go index 16df0d2..ba12252 100644 --- a/internal/modules/priorityclass/list.go +++ b/internal/modules/priorityclass/list.go @@ -22,24 +22,29 @@ import ( type list struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { return &list{ client: client, log: ctrl.Log.WithName("priorityclass_list"), - gk: schema.GroupKind{ - Group: schedulingv1.GroupName, - Kind: "priorityclasses", + gk: schema.GroupVersionKind{ + Group: schedulingv1.GroupName, + Version: "*", + Kind: "priorityclasses", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/apis/scheduling.k8s.io/v1/{endpoint:priorityclasses/?}" } @@ -56,16 +61,17 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re return utils.HandleListSelector(selectorsMatch) } + // Regex Deprecated, Therefor handeled last sc := &schedulingv1.PriorityClassList{} if err = l.client.List(httpRequest.Context(), sc); err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } var r *labels.Requirement if r, err = getPriorityClassSelector(sc, exactMatch, regexMatch); err != nil { if !allowed { - return nil, errors.NewNotAllowed(l.gk) + return nil, errors.NewNotAllowed(l.GroupKind()) } r, _ = labels.NewRequirement("dontexistsignoreme", selection.Exists, []string{}) diff --git a/internal/modules/runtimeclass/get.go b/internal/modules/runtimeclass/get.go index b2ad1af..b324eb3 100644 --- a/internal/modules/runtimeclass/get.go +++ b/internal/modules/runtimeclass/get.go @@ -22,24 +22,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("runtimeclass_get"), - gk: schema.GroupKind{ - Group: nodev1.GroupName, - Kind: "runtimeclasses", + gk: schema.GroupVersionKind{ + Group: nodev1.GroupName, + Version: "*", + Kind: "runtimeclasses", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/apis/node.k8s.io/v1/{endpoint:runtimeclasses}/{name}" } @@ -55,10 +60,10 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req _, requirements := getRuntimeClass(httpRequest, proxyTenants) if len(requirements) == 0 { - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) } rc := &nodev1.RuntimeClass{} - return utils.HandleGetSelector(httpRequest.Context(), rc, g.client, requirements, name, g.gk) + return utils.HandleGetSelector(httpRequest.Context(), rc, g.client, requirements, name, g.GroupKind()) } diff --git a/internal/modules/runtimeclass/list.go b/internal/modules/runtimeclass/list.go index 2e02adc..61ae2b1 100644 --- a/internal/modules/runtimeclass/list.go +++ b/internal/modules/runtimeclass/list.go @@ -22,24 +22,29 @@ import ( type list struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { return &list{ client: client, log: ctrl.Log.WithName("runtimeclass_list"), - gk: schema.GroupKind{ - Group: nodev1.GroupName, - Kind: "runtimeclasses", + gk: schema.GroupVersionKind{ + Group: nodev1.GroupName, + Version: "*", + Kind: "runtimeclasses", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/apis/node.k8s.io/v1/{endpoint:runtimeclasses/?}" } @@ -52,8 +57,9 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re httpRequest := proxyRequest.GetHTTPRequest() allowed, selectorsMatch := getRuntimeClass(httpRequest, proxyTenants) + if !allowed { - return nil, errors.NewNotAllowed(l.gk) + return nil, errors.NewNotAllowed(l.GroupKind()) } if len(selectorsMatch) == 0 { diff --git a/internal/modules/storageclass/get.go b/internal/modules/storageclass/get.go index a0a08b9..5dc1381 100644 --- a/internal/modules/storageclass/get.go +++ b/internal/modules/storageclass/get.go @@ -25,24 +25,29 @@ import ( type get struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { return &get{ client: client, log: ctrl.Log.WithName("storageclass_get"), - gk: schema.GroupKind{ - Group: storagev1.GroupName, - Kind: "storageclasses", + gk: schema.GroupVersionKind{ + Group: storagev1.GroupName, + Version: "*", + Kind: "storageclasses", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { return "/apis/storage.k8s.io/v1/{endpoint:storageclasses}/{name}" } @@ -60,12 +65,12 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req if len(requirements) > 0 { sc := &storagev1.StorageClass{} - return utils.HandleGetSelector(httpRequest.Context(), sc, g.client, requirements, name, g.gk) + return utils.HandleGetSelector(httpRequest.Context(), sc, g.client, requirements, name, g.GroupKind()) } sc := &storagev1.StorageClassList{} if err = g.client.List(httpRequest.Context(), sc, client.MatchingLabels{corev1.LabelMetadataName: name}); err != nil { - return nil, errors.NewBadRequest(err, g.gk) + return nil, errors.NewBadRequest(err, g.GroupKind()) } var r *labels.Requirement @@ -75,7 +80,7 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req case err == nil: return labels.NewSelector().Add(*r), nil case httpRequest.Method == http.MethodGet: - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) default: return nil, nil } diff --git a/internal/modules/storageclass/list.go b/internal/modules/storageclass/list.go index fd66847..c308f1e 100644 --- a/internal/modules/storageclass/list.go +++ b/internal/modules/storageclass/list.go @@ -22,24 +22,29 @@ import ( type list struct { client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List(client client.Reader) modules.Module { return &list{ client: client, log: ctrl.Log.WithName("storageclass_list"), - gk: schema.GroupKind{ - Group: storagev1.GroupName, - Kind: "storageclasses", + gk: schema.GroupVersionKind{ + Group: storagev1.GroupName, + Version: "*", + Kind: "storageclasses", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return "/apis/storage.k8s.io/v1/{endpoint:storageclasses/?}" } @@ -58,14 +63,14 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Re sc := &storagev1.StorageClassList{} if err = l.client.List(httpRequest.Context(), sc); err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } var r *labels.Requirement if r, err = getStorageClassSelector(sc, exactMatch, regexMatch); err != nil { if !allowed { - return nil, errors.NewNotAllowed(l.gk) + return nil, errors.NewNotAllowed(l.GroupKind()) } r, _ = labels.NewRequirement("dontexistsignoreme", selection.Exists, []string{}) diff --git a/internal/modules/tenants/get.go b/internal/modules/tenants/get.go index aa8acf5..9cc8ae6 100644 --- a/internal/modules/tenants/get.go +++ b/internal/modules/tenants/get.go @@ -25,7 +25,7 @@ type get struct { capsuleLabel string client client.Reader log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func Get(client client.Reader) modules.Module { @@ -35,19 +35,24 @@ func Get(client client.Reader) modules.Module { capsuleLabel: label, client: client, log: ctrl.Log.WithName("tenant_get"), - gk: schema.GroupKind{ - Group: "capsule.clastix.io", - Kind: "tenants", + gk: schema.GroupVersionKind{ + Group: "capsule.clastix.io", + Version: "*", + Kind: "tenants", }, } } -func (g get) GroupKind() schema.GroupKind { +func (g get) GroupVersionKind() schema.GroupVersionKind { return g.gk } +func (g get) GroupKind() schema.GroupKind { + return g.gk.GroupKind() +} + func (g get) Path() string { - return "/apis/capsule.clastix.io/v1beta2/tenants/{name}" + return "/apis/{}/v1beta2/tenants/{name}" } func (g get) Methods() []string { @@ -67,5 +72,5 @@ func (g get) Handle(proxyTenants []*tenant.ProxyTenant, proxyRequest request.Req return labels.NewSelector(), nil } - return nil, errors.NewNotFoundError(name, g.gk) + return nil, errors.NewNotFoundError(name, g.GroupKind()) } diff --git a/internal/modules/tenants/list.go b/internal/modules/tenants/list.go index 55a1e73..5d28a6f 100644 --- a/internal/modules/tenants/list.go +++ b/internal/modules/tenants/list.go @@ -20,23 +20,28 @@ import ( type list struct { log logr.Logger - gk schema.GroupKind + gk schema.GroupVersionKind } func List() modules.Module { return &list{ log: ctrl.Log.WithName("tenant_list"), - gk: schema.GroupKind{ - Group: "capsule.clastix.io", - Kind: "tenants", + gk: schema.GroupVersionKind{ + Group: "capsule.clastix.io", + Version: "*", + Kind: "tenants", }, } } -func (l list) GroupKind() schema.GroupKind { +func (l list) GroupVersionKind() schema.GroupVersionKind { return l.gk } +func (l list) GroupKind() schema.GroupKind { + return l.gk.GroupKind() +} + func (l list) Path() string { return basePath } @@ -62,7 +67,7 @@ func (l list) Handle(proxyTenants []*tenant.ProxyTenant, _ request.Request) (sel } if err != nil { - return nil, errors.NewBadRequest(err, l.gk) + return nil, errors.NewBadRequest(err, l.GroupKind()) } return labels.NewSelector().Add(*r), nil diff --git a/internal/modules/clusterscoped/utils.go b/internal/modules/utils/clusterscope.go similarity index 85% rename from internal/modules/clusterscoped/utils.go rename to internal/modules/utils/clusterscope.go index 549c322..0362ead 100644 --- a/internal/modules/clusterscoped/utils.go +++ b/internal/modules/utils/clusterscope.go @@ -1,4 +1,4 @@ -package clusterscoped +package utils import ( "regexp" @@ -12,7 +12,8 @@ import ( "github.com/projectcapsule/capsule-proxy/internal/tenant" ) -func getRequirements(gvk *schema.GroupVersionKind, proxyTenants []*tenant.ProxyTenant) (operations []v1beta1.ClusterResourceOperation, requirements []labels.Requirement) { +// Calculate Requirements for a given GroupVersionKind based on the ProxyTenants clusterResource configurations. +func GetClusterScopeRequirements(gvk *schema.GroupVersionKind, proxyTenants []*tenant.ProxyTenant) (operations []v1beta1.ClusterResourceOperation, requirements []labels.Requirement) { operations = []v1beta1.ClusterResourceOperation{} requirements = []labels.Requirement{} diff --git a/internal/tenant/proxytenant.go b/internal/tenant/proxytenant.go index 34e75ae..6472168 100644 --- a/internal/tenant/proxytenant.go +++ b/internal/tenant/proxytenant.go @@ -7,6 +7,7 @@ import ( "net/http" capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/projectcapsule/capsule-proxy/api/v1beta1" ) @@ -56,6 +57,31 @@ func NewProxyTenant(ownerName string, ownerKind capsulev1beta2.OwnerKind, tenant } } +// This Function returns a ProxyTenant struct for GlobalProxySettings. These Settings are currently not bound to a tenant and therefor +// an empty tenant and empty ProxySettings are returned. +func NewClusterProxy(ownerName string, ownerKind capsulev1beta2.OwnerKind, owners []v1beta1.GlobalSubjectSpec) *ProxyTenant { + var tenantClusterResources []v1beta1.ClusterResource + + for _, global := range owners { + for _, subject := range global.Subjects { + if subject.Name == ownerName && subject.Kind == ownerKind { + tenantClusterResources = global.ClusterResources + } + } + } + + return &ProxyTenant{ + Tenant: capsulev1beta2.Tenant{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global", + }, + Spec: capsulev1beta2.TenantSpec{}, + }, + ProxySetting: defaultProxySettings(), + ClusterResources: tenantClusterResources, + } +} + func (p *ProxyTenant) RequestAllowed(request *http.Request, serviceKind capsulev1beta2.ProxyServiceKind) (ok bool) { return p.ProxySetting[serviceKind].IsAllowed(request) } diff --git a/internal/webserver/middleware/user_in_group.go b/internal/webserver/middleware/user_in_group.go index c940fff..5d9d6c3 100644 --- a/internal/webserver/middleware/user_in_group.go +++ b/internal/webserver/middleware/user_in_group.go @@ -47,6 +47,8 @@ func CheckUserInCapsuleGroupMiddleware(client client.Writer, log logr.Logger, cl if err != nil { log.Error(err, "Cannot retrieve username and group from request") } + log.V(10).Info("request groups", "groups", groups) + for _, group := range groups { if controllers.CapsuleUserGroups.Has(group) { next.ServeHTTP(writer, request) @@ -54,7 +56,8 @@ func CheckUserInCapsuleGroupMiddleware(client client.Writer, log logr.Logger, cl return } } - log.V(5).Info("current user is not a Capsule one") + + log.V(5).Info("current user is not a Capsule one", "capsule-groups", controllers.CapsuleUserGroups) impersonate(writer, request) }) } diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go index a6668b3..e4d20a7 100644 --- a/internal/webserver/webserver.go +++ b/internal/webserver/webserver.go @@ -439,14 +439,17 @@ func (n *kubeFilter) ownerFromCapsuleToProxySetting(owners capsulev1beta2.OwnerL return out } +//nolint:funlen func (n *kubeFilter) getProxyTenantsForOwnerKind(ctx context.Context, ownerKind capsulev1beta2.OwnerKind, ownerName string) (proxyTenants []*tenant.ProxyTenant, err error) { //nolint:prealloc var tenants []string + ownerIndexValue := fmt.Sprintf("%s:%s", ownerKind.String(), ownerName) + tl := &capsulev1beta2.TenantList{} f := client.MatchingFields{ - ".spec.owner.ownerkind": fmt.Sprintf("%s:%s", ownerKind.String(), ownerName), + ".spec.owner.ownerkind": ownerIndexValue, } if err = n.managerReader.List(ctx, tl, f); err != nil { return nil, fmt.Errorf("cannot retrieve Tenants list: %w", err) @@ -455,10 +458,12 @@ func (n *kubeFilter) getProxyTenantsForOwnerKind(ctx context.Context, ownerKind n.log.V(8).Info("Tenant", "owner", ownerKind, "name", ownerName, "tenantList items", tl.Items, "number of tenants", len(tl.Items)) proxySettings := &v1beta1.ProxySettingList{} - if err = n.managerReader.List(ctx, proxySettings, client.MatchingFields{indexer.SubjectKindField: fmt.Sprintf("%s:%s", ownerKind.String(), ownerName)}); err != nil { + if err = n.managerReader.List(ctx, proxySettings, client.MatchingFields{indexer.SubjectKindField: ownerIndexValue}); err != nil { n.log.Error(err, "cannot retrieve ProxySetting", "owner", ownerKind, "name", ownerName) } + n.log.V(10).Info("Collected ProxySettings", "owner", ownerKind, "name", ownerName, "settings", proxySettings) + for _, proxySetting := range proxySettings.Items { tntList := &capsulev1beta2.TenantList{} if err = n.managerReader.List(ctx, tntList, client.MatchingFields{".status.namespaces": proxySetting.GetNamespace()}); err != nil { @@ -474,6 +479,24 @@ func (n *kubeFilter) getProxyTenantsForOwnerKind(ctx context.Context, ownerKind proxyTenants = append(proxyTenants, tenant.NewProxyTenant(ownerName, ownerKind, tntList.Items[0], proxySetting.Spec.Subjects)) } + // Consider Global ProxySettings + // Only consider GlobalProxySettings if the feature gate is enabled + if n.gates.Enabled(features.ProxyClusterScoped) { + globalProxySettings := &v1beta1.GlobalProxySettingsList{} + if err = n.managerReader.List(ctx, globalProxySettings, client.MatchingFields{indexer.GlobalKindField: ownerIndexValue}); err != nil { + n.log.Error(err, "cannot retrieve GlobalProxySettings", "owner", ownerKind, "name", ownerName) + } + // Convert GlobalProxySettings to TenantProxies + for _, globalProxySetting := range globalProxySettings.Items { + n.log.V(10).Info("Converting GlobalProxySettings", "Setting", globalProxySetting.Name) + + tProxy := tenant.NewClusterProxy(ownerName, ownerKind, globalProxySetting.Spec.Rules) + proxyTenants = append(proxyTenants, tProxy) + } + + n.log.V(10).Info("Collected GlobalProxySettings", "owner", ownerKind, "name", ownerName, "settings", len(globalProxySettings.Items)) + } + for _, t := range tl.Items { proxyTenants = append(proxyTenants, tenant.NewProxyTenant(ownerName, ownerKind, t, n.ownerFromCapsuleToProxySetting(t.Spec.Owners))) tenants = append(tenants, t.GetName()) diff --git a/main.go b/main.go index d6a0bd1..d91f43f 100644 --- a/main.go +++ b/main.go @@ -174,6 +174,7 @@ First match is used and can be specified multiple times as comma separated value mgr, err = ctrl.NewManager(config, ctrl.Options{ Scheme: scheme, HealthProbeBindAddress: ":8081", + PprofBindAddress: ":8082", }) if err != nil { log.Error(err, "cannot create new Manager") @@ -209,6 +210,10 @@ First match is used and can be specified multiple times as comma separated value &tenant.OwnerReference{}, &indexer.ProxySetting{}, } + // Optional Indexers + if gates.Enabled(features.ProxyClusterScoped) { + indexers = append(indexers, &indexer.GlobalProxySetting{}) + } for _, fieldIndex := range indexers { if err = mgr.GetFieldIndexer().IndexField(ctx, fieldIndex.Object(), fieldIndex.Field(), fieldIndex.Func()); err != nil {