Kaynağa Gözat

e2e: migrate provider suite to v2 kubernetes mode

Moritz Johner 3 ay önce
ebeveyn
işleme
8906a441c1

+ 332 - 0
E2E_V2_PLAN.md

@@ -0,0 +1,332 @@
+# E2E V2 Migration Plan
+
+## Goal
+
+Reuse the existing provider e2e suite for the new V2 architecture, starting with the Kubernetes provider, and delete `e2e/suites/v2` after its reusable logic has been migrated.
+
+The target outcome is:
+
+- `provider.test` remains the single suite binary for provider e2e tests.
+- legacy and V2 runs are selected by configuration, not by separate suites.
+- existing table-driven provider assertions are reused.
+- V2-only Kubernetes tests for push, capabilities, and metrics live under `e2e/suites/provider`.
+- only the Kubernetes V2 provider is enabled initially.
+
+## Agreed Decisions
+
+- Use `provider.test` for both legacy and V2 runs.
+- Replace the current `test.e2e.v2` flow so it runs `provider.test` in V2 mode instead of `v2.test`.
+- Reuse the existing table-driven tests in `e2e/suites/provider/cases/kubernetes`.
+- Also migrate the V2 push, capabilities, and metrics tests into `e2e/suites/provider`.
+- For referent-auth equivalence in V2, use `ClusterProvider` with `AuthenticationScopeManifestNamespace`.
+- In the first migration step, enable only the Kubernetes provider deployment in V2 mode.
+
+## Proposed Design
+
+### 1. Introduce a provider suite mode switch
+
+Add an environment-driven mode switch for the provider suite.
+
+Suggested env var:
+
+- `E2E_PROVIDER_MODE=legacy|v2`
+
+Behavior:
+
+- default: `legacy`
+- `v2`: install ESO with V2 enabled, create Provider/ClusterProvider CRDs, and enable the Kubernetes provider deployment
+
+This keeps the suite layout stable and avoids carrying `e2e/suites/v2` as a parallel test hierarchy.
+
+### 2. Move reusable V2 bootstrap into framework/addon
+
+The V2 install logic currently proven in `e2e/suites/v2/suite_test.go` should be promoted into reusable addon mutators instead of staying in suite code.
+
+Recommended shape:
+
+- add V2 mutators in `e2e/framework/addon`
+- use `addon.NewESO(...)` for both legacy and V2
+- do **not** rely on the bespoke installer in `e2e/framework/addon/eso_v2.go`; either remove it later or repurpose it so there is only one supported installation path
+
+Suggested mutators:
+
+- `addon.WithV2Mode()`
+- `addon.WithProviderNamespace("external-secrets-system")`
+- `addon.WithV2Providers(...providers)` or a narrower `addon.WithKubernetesV2Provider()`
+
+Minimum V2 Helm settings:
+
+- `v2.enabled=true`
+- `crds.createProvider=true`
+- `crds.createClusterProvider=true`
+- `providers.enabled=true`
+- one provider entry for Kubernetes
+- release namespace `external-secrets-system`
+- release name `external-secrets`
+
+### 3. Make framework defaults mode-aware
+
+The main framework coupling today is that the default testcase builder assumes a namespaced `SecretStore` reference and no ref kind.
+
+Add mode-aware defaults to `framework.Framework` so existing test tables can continue to build their manifests the same way.
+
+Suggested new fields on `framework.Framework`:
+
+- `DefaultSecretStoreRefKind string`
+- `DefaultPushSecretStoreRefKind string`
+- optionally `DefaultPushSecretStoreRefAPIVersion string`
+- optionally `ProviderMode string`
+
+Set them in `framework.New(...)` based on `E2E_PROVIDER_MODE`:
+
+- legacy:
+  - `DefaultSecretStoreRefKind = ""`
+  - `DefaultPushSecretStoreRefKind = ""`
+- v2:
+  - `DefaultSecretStoreRefKind = esv1.ProviderKindStr`
+  - `DefaultPushSecretStoreRefKind = esv1.ProviderKindStr`
+
+Update default testcase creation in `e2e/framework/testcase.go` so:
+
+- `makeDefaultExternalSecretTestCase()` sets `Spec.SecretStoreRef.Kind` from framework defaults
+- `makeDefaultPushSecretTestCase()` sets `Spec.SecretStoreRefs[0].Kind` from framework defaults
+
+This allows existing table-driven tests to reuse the same setup with no broad manifest rewrite.
+
+### 4. Move V2 resource helpers out of `e2e/suites/v2`
+
+Anything that is reusable test infrastructure should move out of the `v2` suite package before that package is deleted.
+
+Recommended destination:
+
+- new package under `e2e/framework/v2` or similar
+
+Helpers to move first:
+
+- cluster CA bundle lookup
+- Kubernetes provider CR creation
+- Provider creation
+- ClusterProvider creation
+- provider readiness waits
+- RBAC helper for Kubernetes provider access
+- metrics scraping helpers
+
+Likely source files:
+
+- `e2e/suites/v2/helpers.go`
+- `e2e/suites/v2/metrics_helpers.go`
+
+After migration, provider cases should import only framework packages, never `e2e/suites/v2`.
+
+## Kubernetes Provider Migration
+
+### 5. Add a V2 setup path to the Kubernetes provider case
+
+Extend `e2e/suites/provider/cases/kubernetes/provider.go` so the provider setup can operate in both modes.
+
+Recommended approach:
+
+- keep the current `Provider` as the abstraction used by the table tests
+- branch internally on provider mode
+- legacy mode keeps creating `SecretStore` and `ClusterSecretStore`
+- V2 mode creates:
+  - namespaced `Kind=Kubernetes`
+  - namespaced `Kind=Provider`
+  - `ClusterProvider` for the referent-auth equivalent path
+
+Recommended structure:
+
+- `BeforeEach()` decides `setupLegacy()` vs `setupV2()`
+- `CreateStore()` keeps legacy behavior
+- add `CreateStoreV2()`
+- `CreateReferentStore()` keeps legacy behavior
+- add `CreateReferentStoreV2()`
+
+### 6. Preserve existing testcase naming semantics
+
+To minimize rewiring, keep the resource names aligned with what the table tests already expect.
+
+For the default case in V2 mode:
+
+- `Provider.Name = f.Namespace.Name`
+- `Provider.Namespace = f.Namespace.Name`
+- `ExternalSecret.Spec.SecretStoreRef.Name` remains untouched by tests and still resolves correctly
+
+For referent-auth cases in V2 mode:
+
+- create a `ClusterProvider` using the existing referent naming pattern
+- wire it with `AuthenticationScopeManifestNamespace`
+
+### 7. Map referent-auth behavior explicitly
+
+Today the Kubernetes suite expresses referent auth by switching to `ClusterSecretStore`.
+
+In V2, the semantic equivalent should be:
+
+- `SecretStoreRef.Kind = ClusterProvider`
+- `SecretStoreRef.Name = <referent-name>`
+- `ClusterProvider.Spec.AuthenticationScope = ManifestNamespace`
+
+Update `withReferentStore(...)` so it branches by mode:
+
+- legacy: `ClusterSecretStore`
+- V2: `ClusterProvider`
+
+Use API constants where available:
+
+- `esv1.ProviderKindStr`
+- `esv1.ClusterProviderKindStr`
+- `esv1.AuthenticationScopeManifestNamespace`
+
+### 8. RBAC model for Kubernetes V2 tests
+
+The V2 Kubernetes provider needs explicit RBAC to read or write secrets in the target namespace.
+
+For namespaced provider tests:
+
+- create role + rolebinding in the remote namespace
+- bind to the manifest namespace service account used by the provider auth configuration
+
+For referent/cluster-provider cases:
+
+- ensure RBAC is granted in the namespace the provider should access
+- when `AuthenticationScopeManifestNamespace` is used, bind the service account identity from the ExternalSecret/PushSecret namespace
+
+The plan should prefer one RBAC helper with explicit parameters over many ad-hoc copies.
+
+## Test Migration Scope
+
+### 9. Reuse the existing table-driven Kubernetes tests
+
+Keep the existing table-driven assertions in:
+
+- `e2e/suites/provider/cases/kubernetes/kubernetes.go`
+
+These should run in both modes, but V2 execution should be label-gated so we can migrate incrementally.
+
+Recommended labeling:
+
+- retain current `kubernetes` label
+- add V2-specific coverage behind `v2`
+- optionally add `legacy` label to the old suite bootstrap only if needed later
+
+Recommended strategy:
+
+- do not duplicate the common table entries
+- instantiate the same tables with a V2-aware provider setup
+
+### 10. Migrate V2-only Kubernetes tests into provider suite
+
+Move the following test coverage into `e2e/suites/provider/cases/kubernetes` or a sibling under `e2e/suites/provider`:
+
+- capabilities tests
+- push tests
+- metrics tests
+- any cluster-provider tests that express Kubernetes V2 behavior not already covered by the legacy tables
+
+Suggested placement:
+
+- `e2e/suites/provider/cases/kubernetes/capabilities_v2_test.go`
+- `e2e/suites/provider/cases/kubernetes/push_v2_test.go`
+- `e2e/suites/provider/cases/kubernetes/metrics_v2_test.go`
+- `e2e/suites/provider/cases/kubernetes/cluster_provider_v2_test.go`
+
+Guideline:
+
+- keep table-driven sync assertions in the existing `kubernetes.go`
+- keep new V2-only behavior in separate files so the legacy flow stays readable
+
+### 11. Metrics migration approach
+
+The metrics coverage in `e2e/suites/v2/metrics_test.go` is useful and should remain, but it should be narrowed to Kubernetes-only assumptions for this first step.
+
+First migration scope:
+
+- provider readiness metrics for `Provider`
+- readiness metrics for `ClusterProvider`
+- gRPC client/server request metrics after an `ExternalSecret` sync
+- any Kubernetes-specific cache metrics that remain stable enough for CI
+
+Avoid in the first pass:
+
+- fake-provider metrics dependencies
+- broad provider-agnostic abstractions that are not needed yet
+
+## CI / Build Changes
+
+### 12. Switch `test.e2e.v2` to run the provider suite
+
+Update the E2E execution path so V2 uses the provider suite binary instead of `v2.test`.
+
+Planned changes:
+
+- `e2e/Makefile`
+  - keep loading the controller image
+  - load only `provider-kubernetes` for V2 initially
+  - run `TEST_SUITES="provider"`
+  - run with `E2E_PROVIDER_MODE=v2`
+  - keep `GINKGO_LABELS="v2"`
+- `e2e/Dockerfile`
+  - remove `ADD e2e/suites/v2/v2.test /v2.test` once the old suite is gone
+
+Target command shape:
+
+```bash
+GINKGO_LABELS="v2" E2E_PROVIDER_MODE="v2" TEST_SUITES="provider" ./run.sh
+```
+
+### 13. Delete `e2e/suites/v2` only after parity is reached
+
+Deletion should happen only after:
+
+- V2 bootstrap moved out of `e2e/suites/v2/suite_test.go`
+- helpers moved out of `e2e/suites/v2/helpers.go`
+- metrics helpers moved out of `e2e/suites/v2/metrics_helpers.go`
+- Kubernetes V2 tests live under `e2e/suites/provider`
+- `test.e2e.v2` no longer depends on `v2.test`
+
+At that point remove:
+
+- `e2e/suites/v2/`
+- `v2.test` build/copy path
+- any stale docs or make targets referencing the old suite layout
+
+## Suggested Implementation Sequence
+
+1. Add provider-mode env handling to the provider suite bootstrap.
+2. Extract V2 Helm mutators from `e2e/suites/v2/suite_test.go` into `e2e/framework/addon`.
+3. Make framework testcase defaults mode-aware for `Provider` refs.
+4. Move reusable V2 helpers into a framework package.
+5. Extend Kubernetes provider setup to support legacy and V2.
+6. Reuse the existing table-driven Kubernetes tests in V2 mode.
+7. Migrate V2 capabilities tests into the provider suite.
+8. Migrate V2 push tests into the provider suite.
+9. Migrate V2 metrics tests into the provider suite.
+10. Update `test.e2e.v2` to run `provider.test`.
+11. Delete `e2e/suites/v2` and remove `v2.test` packaging.
+
+## Acceptance Criteria
+
+The migration is complete when all of the following are true:
+
+- `make test.e2e` still runs the legacy provider suite unchanged.
+- `make test.e2e.v2` runs `provider.test` with `E2E_PROVIDER_MODE=v2`.
+- the Kubernetes table-driven tests pass in V2 mode using `Provider` / `ClusterProvider` resources.
+- V2 push, capabilities, and metrics tests pass from within `e2e/suites/provider`.
+- only the Kubernetes V2 provider deployment is enabled in the initial V2 run.
+- `e2e/suites/v2` is deleted.
+- the e2e image no longer bundles `v2.test`.
+
+## Risks / Watchouts
+
+- The existing `e2e/framework/addon/eso_v2.go` appears to describe a second installation approach; keeping both active will create drift. Consolidate on the Helm-mutation path.
+- Metrics tests may be brittle if they assert on counters that can vary with retries or background reconciliation. Prefer existence and lower-bound assertions over exact counts.
+- `ClusterProvider` resources are cluster-scoped and must be cleaned up carefully to avoid cross-test leakage.
+- The default testcase ref-kind switch must not affect non-V2 suites; it should be entirely gated by `E2E_PROVIDER_MODE`.
+- Referent-auth semantics should be validated carefully: for V2 this migration assumes `AuthenticationScopeManifestNamespace` is the intended equivalent.
+
+## Out of Scope for This First Step
+
+- Migrating all other providers to V2.
+- Enabling AWS, Fake, or other provider deployments in the V2 provider-suite bootstrap.
+- Building a generic V2 abstraction for every provider before the Kubernetes migration proves the pattern.

+ 0 - 1
e2e/Dockerfile

@@ -25,6 +25,5 @@ ADD e2e/suites/provider/provider.test /provider.test
 ADD e2e/suites/argocd/argocd.test /argocd.test
 ADD e2e/suites/argocd/argocd.test /argocd.test
 ADD e2e/suites/flux/flux.test /flux.test
 ADD e2e/suites/flux/flux.test /flux.test
 ADD e2e/suites/generator/generator.test /generator.test
 ADD e2e/suites/generator/generator.test /generator.test
-ADD e2e/suites/v2/v2.test /v2.test
 
 
 CMD [ "/entrypoint.sh" ]
 CMD [ "/entrypoint.sh" ]

+ 3 - 2
e2e/Makefile

@@ -6,7 +6,7 @@ KIND_IMG       = "kindest/node:v1.33.4@sha256:25a6018e48dfcaee478f4a59af81157a43
 DOCKER_BUILD_ARGS     ?=
 DOCKER_BUILD_ARGS     ?=
 
 
 export E2E_IMAGE_NAME ?= ghcr.io/external-secrets/external-secrets-e2e
 export E2E_IMAGE_NAME ?= ghcr.io/external-secrets/external-secrets-e2e
-export GINKGO_LABELS ?= !managed
+export GINKGO_LABELS ?= !managed && !v2
 export TEST_SUITES ?= provider generator flux argocd
 export TEST_SUITES ?= provider generator flux argocd
 
 
 export OCI_IMAGE_NAME = ghcr.io/external-secrets/external-secrets
 export OCI_IMAGE_NAME = ghcr.io/external-secrets/external-secrets
@@ -52,7 +52,8 @@ test.v2: e2e-image ## Run v2 e2e tests against current kube context
 	kind load docker-image --name="external-secrets" $(IMAGE_NAME):$(VERSION)
 	kind load docker-image --name="external-secrets" $(IMAGE_NAME):$(VERSION)
 	kind load docker-image --name="external-secrets" $(OCI_IMAGE_NAME):$(VERSION)
 	kind load docker-image --name="external-secrets" $(OCI_IMAGE_NAME):$(VERSION)
 	kind load docker-image --name="external-secrets" $(E2E_IMAGE_NAME):$(VERSION)
 	kind load docker-image --name="external-secrets" $(E2E_IMAGE_NAME):$(VERSION)
-	GINKGO_LABELS="v2" TEST_SUITES="v2" ./run.sh
+	kind load docker-image --name="external-secrets" ghcr.io/external-secrets/provider-kubernetes:$(VERSION)
+	GINKGO_LABELS="v2" E2E_PROVIDER_MODE="v2" TEST_SUITES="provider" ./run.sh
 
 
 test.managed: e2e-image ## Run e2e tests against current kube context
 test.managed: e2e-image ## Run e2e tests against current kube context
 	$(MAKE) -C ../ docker.build \
 	$(MAKE) -C ../ docker.build \

+ 77 - 0
e2e/framework/addon/eso_v2_mutators.go

@@ -0,0 +1,77 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package addon
+
+import "os"
+
+const (
+	v2HelmNamespace   = "external-secrets-system"
+	v2HelmReleaseName = "external-secrets"
+)
+
+func WithV2Namespace() MutationFunc {
+	return func(eso *ESO) {
+		eso.HelmChart.Namespace = v2HelmNamespace
+		eso.HelmChart.ReleaseName = v2HelmReleaseName
+		if !containsArg(eso.HelmChart.Args, "--create-namespace") {
+			eso.HelmChart.Args = append(eso.HelmChart.Args, "--create-namespace")
+		}
+	}
+}
+
+func WithV2KubernetesProvider() MutationFunc {
+	return func(eso *ESO) {
+		version := os.Getenv("VERSION")
+		vars := []StringTuple{
+			{Key: "replicaCount", Value: "1"},
+			{Key: "v2.enabled", Value: "true"},
+			{Key: "crds.createProvider", Value: "true"},
+			{Key: "crds.createClusterProvider", Value: "true"},
+			{Key: "providers.enabled", Value: "true"},
+			{Key: "providerDefaults.replicaCount", Value: "1"},
+			{Key: "providers.list[0].name", Value: "kubernetes"},
+			{Key: "providers.list[0].type", Value: "kubernetes"},
+			{Key: "providers.list[0].enabled", Value: "true"},
+			{Key: "providers.list[0].replicaCount", Value: "1"},
+			{Key: "providers.list[0].image.repository", Value: "ghcr.io/external-secrets/provider-kubernetes"},
+			{Key: "providers.list[0].image.tag", Value: version},
+			{Key: "providers.list[0].image.pullPolicy", Value: "IfNotPresent"},
+		}
+		for _, variable := range vars {
+			setOrAppendVar(eso.HelmChart, variable)
+		}
+	}
+}
+
+func setOrAppendVar(chart *HelmChart, variable StringTuple) {
+	for i := range chart.Vars {
+		if chart.Vars[i].Key == variable.Key {
+			chart.Vars[i].Value = variable.Value
+			return
+		}
+	}
+	chart.Vars = append(chart.Vars, variable)
+}
+
+func containsArg(args []string, target string) bool {
+	for _, arg := range args {
+		if arg == target {
+			return true
+		}
+	}
+	return false
+}

+ 9 - 2
e2e/framework/eso.go

@@ -75,9 +75,16 @@ func (f *Framework) printESDebugLogs(esName, esNamespace string) {
 	}
 	}
 
 
 	// print most recent logs of default eso installation
 	// print most recent logs of default eso installation
-	podList, err := f.KubeClientSet.CoreV1().Pods("default").List(
+	esoNamespace := "default"
+	labelSelector := "app.kubernetes.io/instance=eso,app.kubernetes.io/name=external-secrets"
+	if IsV2ProviderMode() {
+		esoNamespace = "external-secrets-system"
+		labelSelector = "app.kubernetes.io/instance=external-secrets,app.kubernetes.io/name=external-secrets"
+	}
+
+	podList, err := f.KubeClientSet.CoreV1().Pods(esoNamespace).List(
 		GinkgoT().Context(),
 		GinkgoT().Context(),
-		metav1.ListOptions{LabelSelector: "app.kubernetes.io/instance=eso,app.kubernetes.io/name=external-secrets"})
+		metav1.ListOptions{LabelSelector: labelSelector})
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 	numLines := int64(60)
 	numLines := int64(60)
 	for i := range podList.Items {
 	for i := range podList.Items {

+ 14 - 2
e2e/framework/framework.go

@@ -34,6 +34,7 @@ import (
 	"github.com/external-secrets/external-secrets-e2e/framework/addon"
 	"github.com/external-secrets/external-secrets-e2e/framework/addon"
 	"github.com/external-secrets/external-secrets-e2e/framework/log"
 	"github.com/external-secrets/external-secrets-e2e/framework/log"
 	"github.com/external-secrets/external-secrets-e2e/framework/util"
 	"github.com/external-secrets/external-secrets-e2e/framework/util"
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 )
 )
 
 
 type Framework struct {
 type Framework struct {
@@ -54,13 +55,24 @@ type Framework struct {
 	Addons []addon.Addon
 	Addons []addon.Addon
 
 
 	MakeRemoteRefKey func(base string) string
 	MakeRemoteRefKey func(base string) string
+
+	ProviderMode                        string
+	DefaultSecretStoreRefKind           string
+	DefaultPushSecretStoreRefKind       string
+	DefaultPushSecretStoreRefAPIVersion string
 }
 }
 
 
 // New returns a new framework instance with defaults.
 // New returns a new framework instance with defaults.
 func New(baseName string) *Framework {
 func New(baseName string) *Framework {
 	f := &Framework{
 	f := &Framework{
-		BaseName:         baseName,
-		MakeRemoteRefKey: func(base string) string { return base },
+		BaseName:                            baseName,
+		MakeRemoteRefKey:                    func(base string) string { return base },
+		ProviderMode:                        GetProviderMode(),
+		DefaultPushSecretStoreRefAPIVersion: esv1.SchemeGroupVersion.String(),
+	}
+	if f.ProviderMode == ProviderModeV2 {
+		f.DefaultSecretStoreRefKind = esv1.ProviderKindStr
+		f.DefaultPushSecretStoreRefKind = esv1.ProviderKindStr
 	}
 	}
 	f.KubeConfig, f.KubeClientSet, f.CRClient = util.NewConfig()
 	f.KubeConfig, f.KubeClientSet, f.CRClient = util.NewConfig()
 
 

+ 39 - 0
e2e/framework/provider_mode.go

@@ -0,0 +1,39 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package framework
+
+import (
+	"os"
+	"strings"
+)
+
+const (
+	ProviderModeEnvVar = "E2E_PROVIDER_MODE"
+	ProviderModeLegacy = "legacy"
+	ProviderModeV2     = "v2"
+)
+
+func GetProviderMode() string {
+	if strings.EqualFold(os.Getenv(ProviderModeEnvVar), ProviderModeV2) {
+		return ProviderModeV2
+	}
+	return ProviderModeLegacy
+}
+
+func IsV2ProviderMode() bool {
+	return GetProviderMode() == ProviderModeV2
+}

+ 4 - 1
e2e/framework/testcase.go

@@ -174,6 +174,7 @@ func makeDefaultExternalSecretTestCase(f *Framework) *TestCase {
 				RefreshInterval: &metav1.Duration{Duration: time.Second * 5},
 				RefreshInterval: &metav1.Duration{Duration: time.Second * 5},
 				SecretStoreRef: esv1.SecretStoreRef{
 				SecretStoreRef: esv1.SecretStoreRef{
 					Name: f.Namespace.Name,
 					Name: f.Namespace.Name,
+					Kind: f.DefaultSecretStoreRefKind,
 				},
 				},
 				Target: esv1.ExternalSecretTarget{
 				Target: esv1.ExternalSecretTarget{
 					Name: TargetSecretName,
 					Name: TargetSecretName,
@@ -195,7 +196,9 @@ func makeDefaultPushSecretTestCase(f *Framework) *TestCase {
 				RefreshInterval: &metav1.Duration{Duration: time.Second * 5},
 				RefreshInterval: &metav1.Duration{Duration: time.Second * 5},
 				SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
 				SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
 					{
 					{
-						Name: f.Namespace.Name,
+						Name:       f.Namespace.Name,
+						Kind:       f.DefaultPushSecretStoreRefKind,
+						APIVersion: f.DefaultPushSecretStoreRefAPIVersion,
 					},
 					},
 				},
 				},
 			},
 			},

+ 238 - 0
e2e/framework/v2/helpers.go

@@ -0,0 +1,238 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v2
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/external-secrets/external-secrets-e2e/framework"
+	"github.com/external-secrets/external-secrets-e2e/framework/log"
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	k8sv2alpha1 "github.com/external-secrets/external-secrets/apis/provider/kubernetes/v2alpha1"
+)
+
+const (
+	ProviderNamespace = "external-secrets-system"
+	DefaultSAName     = "default"
+)
+
+func ProviderAddress(providerName string) string {
+	return fmt.Sprintf("provider-%s.%s.svc:8080", providerName, ProviderNamespace)
+}
+
+func GetClusterCABundle(f *framework.Framework, namespace string) []byte {
+	var caBundle []byte
+	krc := &corev1.ConfigMap{}
+	err := f.CRClient.Get(context.Background(),
+		types.NamespacedName{Name: "kube-root-ca.crt", Namespace: namespace},
+		krc)
+	if err == nil {
+		caBundle = []byte(krc.Data["ca.crt"])
+	}
+	return caBundle
+}
+
+func CreateKubernetesAccessRole(f *framework.Framework, name, serviceAccountName, serviceAccountNamespace, remoteNamespace string) {
+	role := &rbacv1.Role{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: remoteNamespace,
+		},
+		Rules: []rbacv1.PolicyRule{
+			{
+				APIGroups: []string{""},
+				Resources: []string{"secrets"},
+				Verbs:     []string{"get", "list", "watch", "create", "update", "patch", "delete"},
+			},
+			{
+				APIGroups: []string{"authorization.k8s.io"},
+				Resources: []string{"selfsubjectrulesreviews", "selfsubjectaccessreviews"},
+				Verbs:     []string{"create"},
+			},
+		},
+	}
+	Expect(createOrIgnoreAlreadyExists(f, role)).To(Succeed())
+
+	roleBinding := &rbacv1.RoleBinding{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: remoteNamespace,
+		},
+		Subjects: []rbacv1.Subject{
+			{
+				Kind:      "ServiceAccount",
+				Name:      serviceAccountName,
+				Namespace: serviceAccountNamespace,
+			},
+		},
+		RoleRef: rbacv1.RoleRef{
+			APIGroup: "rbac.authorization.k8s.io",
+			Kind:     "Role",
+			Name:     name,
+		},
+	}
+	Expect(createOrIgnoreAlreadyExists(f, roleBinding)).To(Succeed())
+}
+
+func CreateKubernetesProvider(f *framework.Framework, namespace, name, remoteNamespace, serviceAccountName string, serviceAccountNamespace *string, caBundle []byte) *k8sv2alpha1.Kubernetes {
+	k8ss := &k8sv2alpha1.Kubernetes{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Kubernetes",
+			APIVersion: "provider.external-secrets.io/v2alpha1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: esv1.KubernetesProvider{
+			Server: esv1.KubernetesServer{
+				URL:      "https://kubernetes.default.svc",
+				CABundle: caBundle,
+			},
+			RemoteNamespace: remoteNamespace,
+			Auth: &esv1.KubernetesAuth{
+				ServiceAccount: &esmeta.ServiceAccountSelector{
+					Name:      serviceAccountName,
+					Namespace: serviceAccountNamespace,
+				},
+			},
+		},
+	}
+	Expect(createOrIgnoreAlreadyExists(f, k8ss)).To(Succeed())
+	log.Logf("created Kubernetes provider: %s/%s", namespace, name)
+	return k8ss
+}
+
+func CreateProviderConnection(f *framework.Framework, namespace, name, address, providerAPIVersion, providerKind, providerName, providerNamespace string) *esv1.Provider {
+	providerConnection := &esv1.Provider{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Spec: esv1.ProviderSpec{
+			Config: esv1.ProviderConfig{
+				Address: address,
+				ProviderRef: esv1.ProviderReference{
+					APIVersion: providerAPIVersion,
+					Kind:       providerKind,
+					Name:       providerName,
+					Namespace:  providerNamespace,
+				},
+			},
+		},
+	}
+	Expect(createOrIgnoreAlreadyExists(f, providerConnection)).To(Succeed())
+	log.Logf("created Provider: %s/%s", namespace, name)
+	return providerConnection
+}
+
+func CreateClusterProviderConnection(f *framework.Framework, name, address, providerAPIVersion, providerKind, providerName, providerNamespace string, authScope esv1.AuthenticationScope, conditions []esv1.ClusterSecretStoreCondition) *esv1.ClusterProvider {
+	clusterProvider := &esv1.ClusterProvider{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: name,
+		},
+		Spec: esv1.ClusterProviderSpec{
+			Config: esv1.ProviderConfig{
+				Address: address,
+				ProviderRef: esv1.ProviderReference{
+					APIVersion: providerAPIVersion,
+					Kind:       providerKind,
+					Name:       providerName,
+					Namespace:  providerNamespace,
+				},
+			},
+			AuthenticationScope: authScope,
+			Conditions:          conditions,
+		},
+	}
+	Expect(createOrIgnoreAlreadyExists(f, clusterProvider)).To(Succeed())
+	log.Logf("created ClusterProvider: %s", name)
+	return clusterProvider
+}
+
+func WaitForProviderConnectionReady(f *framework.Framework, namespace, name string, timeout time.Duration) *esv1.Provider {
+	var providerConnection esv1.Provider
+	Eventually(func() bool {
+		err := f.CRClient.Get(context.Background(),
+			types.NamespacedName{Name: name, Namespace: namespace},
+			&providerConnection)
+		if err != nil {
+			log.Logf("failed to get Provider: %v", err)
+			return false
+		}
+
+		for _, condition := range providerConnection.Status.Conditions {
+			if condition.Type == "Ready" && condition.Status == metav1.ConditionTrue {
+				return true
+			}
+		}
+		return false
+	}, timeout, time.Second).Should(BeTrue(), "Provider should become ready")
+
+	return &providerConnection
+}
+
+func WaitForClusterProviderReady(f *framework.Framework, name string, timeout time.Duration) *esv1.ClusterProvider {
+	var clusterProvider esv1.ClusterProvider
+	Eventually(func() bool {
+		err := f.CRClient.Get(context.Background(),
+			types.NamespacedName{Name: name},
+			&clusterProvider)
+		if err != nil {
+			log.Logf("failed to get ClusterProvider: %v", err)
+			return false
+		}
+
+		for _, condition := range clusterProvider.Status.Conditions {
+			if condition.Type == "Ready" && condition.Status == metav1.ConditionTrue {
+				return true
+			}
+		}
+		return false
+	}, timeout, time.Second).Should(BeTrue(), "ClusterProvider should become ready")
+
+	return &clusterProvider
+}
+
+func VerifyProviderConnectionCapabilities(f *framework.Framework, namespace, name string, expected esv1.ProviderCapabilities) {
+	var provider esv1.Provider
+	Expect(f.CRClient.Get(context.Background(),
+		types.NamespacedName{Name: name, Namespace: namespace},
+		&provider)).To(Succeed())
+
+	Expect(provider.Status.Capabilities).NotTo(BeEmpty())
+	Expect(provider.Status.Capabilities).To(Equal(expected))
+}
+
+func createOrIgnoreAlreadyExists(f *framework.Framework, obj client.Object) error {
+	err := f.CRClient.Create(context.Background(), obj)
+	if err == nil || apierrors.IsAlreadyExists(err) {
+		return nil
+	}
+	return err
+}

+ 130 - 236
e2e/suites/v2/metrics_helpers.go → e2e/framework/v2/metrics.go

@@ -1,9 +1,11 @@
 /*
 /*
+Copyright © 2025 ESO Maintainer Team
+
 Licensed under the Apache License, Version 2.0 (the "License");
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 You may obtain a copy of the License at
 
 
-    http://www.apache.org/licenses/LICENSE-2.0
+    https://www.apache.org/licenses/LICENSE-2.0
 
 
 Unless required by applicable law or agreed to in writing, software
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 distributed under the License is distributed on an "AS IS" BASIS,
@@ -25,50 +27,146 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	v1 "k8s.io/api/core/v1"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/portforward"
 	"k8s.io/client-go/tools/portforward"
 	"k8s.io/client-go/transport/spdy"
 	"k8s.io/client-go/transport/spdy"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
 )
 )
 
 
-// MetricSample represents a single Prometheus metric sample
 type MetricSample struct {
 type MetricSample struct {
 	Name   string
 	Name   string
 	Labels map[string]string
 	Labels map[string]string
 	Value  float64
 	Value  float64
 }
 }
 
 
-// MetricsMap is a map of metric names to their samples
 type MetricsMap map[string][]MetricSample
 type MetricsMap map[string][]MetricSample
 
 
-// setupPortForward creates a port-forward to a pod and returns the local address and cleanup function
-func setupPortForward(ctx context.Context, config *rest.Config, clientset *kubernetes.Clientset, namespace, podName string, podPort int) (string, func(), error) {
-	// Find an available local port
-	localPort := 0 // Let the system choose
+func ScrapeControllerMetrics(ctx context.Context, config *rest.Config, clientset kubernetes.Interface, namespace string) (MetricsMap, error) {
+	podName, err := findPod(ctx, clientset, namespace, "app.kubernetes.io/name=external-secrets")
+	if err != nil {
+		return nil, err
+	}
+
+	return scrapePodMetrics(ctx, config, clientset, namespace, podName, 8080)
+}
+
+func ScrapeProviderMetrics(ctx context.Context, config *rest.Config, clientset kubernetes.Interface, namespace, providerName string) (MetricsMap, error) {
+	labelSelector := fmt.Sprintf("app.kubernetes.io/name=external-secrets-provider-%s", providerName)
+	podName, err := findPod(ctx, clientset, namespace, labelSelector)
+	if err != nil {
+		return nil, err
+	}
+
+	return scrapePodMetrics(ctx, config, clientset, namespace, podName, 8081)
+}
+
+func GetMetricValue(metrics MetricsMap, metricName string, matchLabels map[string]string) (float64, bool) {
+	samples, exists := metrics[metricName]
+	if !exists {
+		return 0, false
+	}
+
+	for _, sample := range samples {
+		if labelsMatch(sample.Labels, matchLabels) {
+			return sample.Value, true
+		}
+	}
 
 
-	// Get the pod
+	return 0, false
+}
+
+func ExpectMetricExists(metrics MetricsMap, metricName string) {
+	_, exists := metrics[metricName]
+	if !exists {
+		availableMetrics := []string{}
+		for name := range metrics {
+			availableMetrics = append(availableMetrics, name)
+		}
+		GinkgoWriter.Printf("Available metrics: %v\n", availableMetrics)
+	}
+	Expect(exists).To(BeTrue(), "metric %s should exist", metricName)
+}
+
+func ExpectMetricValue(metrics MetricsMap, metricName string, matchLabels map[string]string, expectedValue float64) {
+	value, found := GetMetricValue(metrics, metricName, matchLabels)
+	Expect(found).To(BeTrue(), "metric %s with labels %v should exist", metricName, matchLabels)
+	Expect(value).To(Equal(expectedValue), "metric %s value mismatch", metricName)
+}
+
+func ExpectMetricGreaterThan(metrics MetricsMap, metricName string, matchLabels map[string]string, threshold float64) {
+	value, found := GetMetricValue(metrics, metricName, matchLabels)
+	Expect(found).To(BeTrue(), "metric %s with labels %v should exist", metricName, matchLabels)
+	Expect(value).To(BeNumerically(">", threshold), "metric %s should be greater than %f", metricName, threshold)
+}
+
+func WaitForMetric(ctx context.Context, scraper func() (MetricsMap, error), metricName string, matchLabels map[string]string, minValue float64, timeout time.Duration) error {
+	deadline := time.Now().Add(timeout)
+	for time.Now().Before(deadline) {
+		metrics, err := scraper()
+		if err == nil {
+			value, found := GetMetricValue(metrics, metricName, matchLabels)
+			if found && value >= minValue {
+				return nil
+			}
+		}
+		time.Sleep(time.Second)
+	}
+
+	return fmt.Errorf("timeout waiting for metric %s with labels %v to reach %f", metricName, matchLabels, minValue)
+}
+
+func scrapePodMetrics(ctx context.Context, config *rest.Config, clientset kubernetes.Interface, namespace, podName string, podPort int) (MetricsMap, error) {
+	address, cleanup, err := setupPortForward(ctx, config, clientset, namespace, podName, podPort)
+	if err != nil {
+		return nil, fmt.Errorf("failed to setup port forward: %w", err)
+	}
+	defer cleanup()
+
+	time.Sleep(time.Second)
+
+	body, err := scrapeMetrics(ctx, address)
+	if err != nil {
+		return nil, err
+	}
+
+	return parsePrometheusMetrics(body), nil
+}
+
+func findPod(ctx context.Context, clientset kubernetes.Interface, namespace, labelSelector string) (string, error) {
+	pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
+		LabelSelector: labelSelector,
+	})
+	if err != nil {
+		return "", fmt.Errorf("failed to list pods: %w", err)
+	}
+
+	for _, pod := range pods.Items {
+		if pod.Status.Phase == corev1.PodRunning {
+			return pod.Name, nil
+		}
+	}
+
+	return "", fmt.Errorf("no running pod found for selector %s", labelSelector)
+}
+
+func setupPortForward(ctx context.Context, config *rest.Config, clientset kubernetes.Interface, namespace, podName string, podPort int) (string, func(), error) {
 	pod, err := clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
 	pod, err := clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
 	if err != nil {
 	if err != nil {
 		return "", nil, fmt.Errorf("failed to get pod: %w", err)
 		return "", nil, fmt.Errorf("failed to get pod: %w", err)
 	}
 	}
-
-	// Ensure pod is running
-	if pod.Status.Phase != v1.PodRunning {
+	if pod.Status.Phase != corev1.PodRunning {
 		return "", nil, fmt.Errorf("pod %s is not running: %s", podName, pod.Status.Phase)
 		return "", nil, fmt.Errorf("pod %s is not running: %s", podName, pod.Status.Phase)
 	}
 	}
 
 
-	// Create the port-forward request
 	transport, upgrader, err := spdy.RoundTripperFor(config)
 	transport, upgrader, err := spdy.RoundTripperFor(config)
 	if err != nil {
 	if err != nil {
 		return "", nil, fmt.Errorf("failed to create round tripper: %w", err)
 		return "", nil, fmt.Errorf("failed to create round tripper: %w", err)
 	}
 	}
 
 
-	// Build the URL for port forwarding
 	url := clientset.CoreV1().RESTClient().Post().
 	url := clientset.CoreV1().RESTClient().Post().
 		Resource("pods").
 		Resource("pods").
 		Namespace(namespace).
 		Namespace(namespace).
@@ -80,49 +178,36 @@ func setupPortForward(ctx context.Context, config *rest.Config, clientset *kuber
 
 
 	stopChan := make(chan struct{}, 1)
 	stopChan := make(chan struct{}, 1)
 	readyChan := make(chan struct{}, 1)
 	readyChan := make(chan struct{}, 1)
+	ports := []string{fmt.Sprintf("0:%d", podPort)}
 
 
-	// Create port forwarder
-	ports := []string{fmt.Sprintf("%d:%d", localPort, podPort)}
-	
-	out := GinkgoWriter
-	errOut := GinkgoWriter
-
-	pf, err := portforward.New(dialer, ports, stopChan, readyChan, out, errOut)
+	pf, err := portforward.New(dialer, ports, stopChan, readyChan, GinkgoWriter, GinkgoWriter)
 	if err != nil {
 	if err != nil {
 		return "", nil, fmt.Errorf("failed to create port forwarder: %w", err)
 		return "", nil, fmt.Errorf("failed to create port forwarder: %w", err)
 	}
 	}
 
 
-	// Start port forwarding in background
 	errChan := make(chan error, 1)
 	errChan := make(chan error, 1)
 	go func() {
 	go func() {
-		if err := pf.ForwardPorts(); err != nil {
-			errChan <- err
+		if forwardErr := pf.ForwardPorts(); forwardErr != nil {
+			errChan <- forwardErr
 		}
 		}
 	}()
 	}()
 
 
-	// Wait for ready or error
 	select {
 	select {
 	case <-readyChan:
 	case <-readyChan:
-		// Port forward is ready
-		forwardedPorts, err := pf.GetPorts()
-		if err != nil {
+		forwardedPorts, portErr := pf.GetPorts()
+		if portErr != nil {
 			close(stopChan)
 			close(stopChan)
-			return "", nil, fmt.Errorf("failed to get forwarded ports: %w", err)
+			return "", nil, fmt.Errorf("failed to get forwarded ports: %w", portErr)
 		}
 		}
-		
 		if len(forwardedPorts) == 0 {
 		if len(forwardedPorts) == 0 {
 			close(stopChan)
 			close(stopChan)
 			return "", nil, fmt.Errorf("no ports were forwarded")
 			return "", nil, fmt.Errorf("no ports were forwarded")
 		}
 		}
-
-		localAddr := fmt.Sprintf("localhost:%d", forwardedPorts[0].Local)
-		
 		cleanup := func() {
 		cleanup := func() {
 			close(stopChan)
 			close(stopChan)
 		}
 		}
-
-		return localAddr, cleanup, nil
-	case err := <-errChan:
+		return fmt.Sprintf("localhost:%d", forwardedPorts[0].Local), cleanup, nil
+	case err = <-errChan:
 		close(stopChan)
 		close(stopChan)
 		return "", nil, fmt.Errorf("port forward failed: %w", err)
 		return "", nil, fmt.Errorf("port forward failed: %w", err)
 	case <-time.After(30 * time.Second):
 	case <-time.After(30 * time.Second):
@@ -131,20 +216,13 @@ func setupPortForward(ctx context.Context, config *rest.Config, clientset *kuber
 	}
 	}
 }
 }
 
 
-// scrapeMetrics fetches metrics from an HTTP endpoint
 func scrapeMetrics(ctx context.Context, address string) (string, error) {
 func scrapeMetrics(ctx context.Context, address string) (string, error) {
-	url := fmt.Sprintf("http://%s/metrics", address)
-	
-	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("http://%s/metrics", address), nil)
 	if err != nil {
 	if err != nil {
 		return "", fmt.Errorf("failed to create request: %w", err)
 		return "", fmt.Errorf("failed to create request: %w", err)
 	}
 	}
 
 
-	client := &http.Client{
-		Timeout: 10 * time.Second,
-	}
-
-	resp, err := client.Do(req)
+	resp, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
 	if err != nil {
 	if err != nil {
 		return "", fmt.Errorf("failed to scrape metrics: %w", err)
 		return "", fmt.Errorf("failed to scrape metrics: %w", err)
 	}
 	}
@@ -158,24 +236,16 @@ func scrapeMetrics(ctx context.Context, address string) (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", fmt.Errorf("failed to read response body: %w", err)
 		return "", fmt.Errorf("failed to read response body: %w", err)
 	}
 	}
-
 	return string(body), nil
 	return string(body), nil
 }
 }
 
 
-// parsePrometheusMetrics parses Prometheus text format into a structured map
 func parsePrometheusMetrics(body string) MetricsMap {
 func parsePrometheusMetrics(body string) MetricsMap {
 	metrics := make(MetricsMap)
 	metrics := make(MetricsMap)
-	
-	// Regular expression to parse metric lines
-	// Format: metric_name{label1="value1",label2="value2"} value
-	// or: metric_name value
 	metricRegex := regexp.MustCompile(`^([a-zA-Z_:][a-zA-Z0-9_:]*?)(?:\{([^}]*)\})?\s+([^\s]+)`)
 	metricRegex := regexp.MustCompile(`^([a-zA-Z_:][a-zA-Z0-9_:]*?)(?:\{([^}]*)\})?\s+([^\s]+)`)
 
 
 	scanner := bufio.NewScanner(strings.NewReader(body))
 	scanner := bufio.NewScanner(strings.NewReader(body))
 	for scanner.Scan() {
 	for scanner.Scan() {
 		line := scanner.Text()
 		line := scanner.Text()
-		
-		// Skip comments and empty lines
 		if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
 		if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
 			continue
 			continue
 		}
 		}
@@ -185,67 +255,38 @@ func parsePrometheusMetrics(body string) MetricsMap {
 			continue
 			continue
 		}
 		}
 
 
-		name := matches[1]
-		labelsStr := matches[2]
-		valueStr := matches[3]
-
-		value, err := strconv.ParseFloat(valueStr, 64)
+		value, err := strconv.ParseFloat(matches[3], 64)
 		if err != nil {
 		if err != nil {
 			continue
 			continue
 		}
 		}
 
 
-		labels := parseLabels(labelsStr)
-
 		sample := MetricSample{
 		sample := MetricSample{
-			Name:   name,
-			Labels: labels,
+			Name:   matches[1],
+			Labels: parseLabels(matches[2]),
 			Value:  value,
 			Value:  value,
 		}
 		}
-
-		metrics[name] = append(metrics[name], sample)
+		metrics[sample.Name] = append(metrics[sample.Name], sample)
 	}
 	}
 
 
 	return metrics
 	return metrics
 }
 }
 
 
-// parseLabels parses label string into a map
 func parseLabels(labelsStr string) map[string]string {
 func parseLabels(labelsStr string) map[string]string {
 	labels := make(map[string]string)
 	labels := make(map[string]string)
-	
 	if labelsStr == "" {
 	if labelsStr == "" {
 		return labels
 		return labels
 	}
 	}
 
 
-	// Split by comma, but respect quotes
 	labelRegex := regexp.MustCompile(`([a-zA-Z_][a-zA-Z0-9_]*)="([^"]*)"`)
 	labelRegex := regexp.MustCompile(`([a-zA-Z_][a-zA-Z0-9_]*)="([^"]*)"`)
 	matches := labelRegex.FindAllStringSubmatch(labelsStr, -1)
 	matches := labelRegex.FindAllStringSubmatch(labelsStr, -1)
-	
 	for _, match := range matches {
 	for _, match := range matches {
 		if len(match) == 3 {
 		if len(match) == 3 {
 			labels[match[1]] = match[2]
 			labels[match[1]] = match[2]
 		}
 		}
 	}
 	}
-
 	return labels
 	return labels
 }
 }
 
 
-// getMetricValue finds a metric with specific labels and returns its value
-func getMetricValue(metrics MetricsMap, metricName string, matchLabels map[string]string) (float64, bool) {
-	samples, exists := metrics[metricName]
-	if !exists {
-		return 0, false
-	}
-
-	for _, sample := range samples {
-		if labelsMatch(sample.Labels, matchLabels) {
-			return sample.Value, true
-		}
-	}
-
-	return 0, false
-}
-
-// labelsMatch checks if sample labels match the required labels
 func labelsMatch(sampleLabels, matchLabels map[string]string) bool {
 func labelsMatch(sampleLabels, matchLabels map[string]string) bool {
 	for key, value := range matchLabels {
 	for key, value := range matchLabels {
 		if sampleLabels[key] != value {
 		if sampleLabels[key] != value {
@@ -254,150 +295,3 @@ func labelsMatch(sampleLabels, matchLabels map[string]string) bool {
 	}
 	}
 	return true
 	return true
 }
 }
-
-// findControllerPod finds the external-secrets controller pod
-func findControllerPod(ctx context.Context, clientset *kubernetes.Clientset, namespace string) (string, error) {
-	pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
-		LabelSelector: "app.kubernetes.io/name=external-secrets",
-	})
-	if err != nil {
-		return "", fmt.Errorf("failed to list controller pods: %w", err)
-	}
-
-	if len(pods.Items) == 0 {
-		return "", fmt.Errorf("no controller pods found")
-	}
-
-	// Return the first running pod
-	for _, pod := range pods.Items {
-		if pod.Status.Phase == v1.PodRunning {
-			return pod.Name, nil
-		}
-	}
-
-	return "", fmt.Errorf("no running controller pods found")
-}
-
-// findProviderPod finds a provider pod by label
-func findProviderPod(ctx context.Context, clientset *kubernetes.Clientset, namespace string, providerType string) (string, error) {
-	labelSelector := fmt.Sprintf("app.kubernetes.io/name=external-secrets-provider-%s", providerType)
-	
-	pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
-		LabelSelector: labelSelector,
-	})
-	if err != nil {
-		return "", fmt.Errorf("failed to list provider pods: %w", err)
-	}
-
-	if len(pods.Items) == 0 {
-		return "", fmt.Errorf("no %s provider pods found", providerType)
-	}
-
-	// Return the first running pod
-	for _, pod := range pods.Items {
-		if pod.Status.Phase == v1.PodRunning {
-			return pod.Name, nil
-		}
-	}
-
-	return "", fmt.Errorf("no running %s provider pods found", providerType)
-}
-
-// scrapeControllerMetrics scrapes metrics from the controller pod
-func scrapeControllerMetrics(ctx context.Context, config *rest.Config, clientset *kubernetes.Clientset, namespace string) (MetricsMap, error) {
-	podName, err := findControllerPod(ctx, clientset, namespace)
-	if err != nil {
-		return nil, err
-	}
-
-	address, cleanup, err := setupPortForward(ctx, config, clientset, namespace, podName, 8080)
-	if err != nil {
-		return nil, fmt.Errorf("failed to setup port forward: %w", err)
-	}
-	defer cleanup()
-
-	// Give port-forward a moment to stabilize
-	time.Sleep(1 * time.Second)
-
-	body, err := scrapeMetrics(ctx, address)
-	if err != nil {
-		return nil, err
-	}
-
-	return parsePrometheusMetrics(body), nil
-}
-
-// scrapeProviderMetrics scrapes metrics from a provider pod
-func scrapeProviderMetrics(ctx context.Context, config *rest.Config, clientset *kubernetes.Clientset, namespace string, providerType string) (MetricsMap, error) {
-	podName, err := findProviderPod(ctx, clientset, namespace, providerType)
-	if err != nil {
-		return nil, err
-	}
-
-	address, cleanup, err := setupPortForward(ctx, config, clientset, namespace, podName, 8081)
-	if err != nil {
-		return nil, fmt.Errorf("failed to setup port forward: %w", err)
-	}
-	defer cleanup()
-
-	// Give port-forward a moment to stabilize
-	time.Sleep(1 * time.Second)
-
-	body, err := scrapeMetrics(ctx, address)
-	if err != nil {
-		return nil, err
-	}
-
-	return parsePrometheusMetrics(body), nil
-}
-
-// waitForMetric polls until a metric reaches a minimum value or times out
-func waitForMetric(ctx context.Context, scraper func() (MetricsMap, error), metricName string, matchLabels map[string]string, minValue float64, timeout time.Duration) error {
-	deadline := time.Now().Add(timeout)
-	
-	for time.Now().Before(deadline) {
-		metrics, err := scraper()
-		if err != nil {
-			time.Sleep(1 * time.Second)
-			continue
-		}
-
-		value, found := getMetricValue(metrics, metricName, matchLabels)
-		if found && value >= minValue {
-			return nil
-		}
-
-		time.Sleep(1 * time.Second)
-	}
-
-	return fmt.Errorf("timeout waiting for metric %s with labels %v to reach %f", metricName, matchLabels, minValue)
-}
-
-// ExpectMetricExists asserts that a metric exists
-func ExpectMetricExists(metrics MetricsMap, metricName string) {
-	_, exists := metrics[metricName]
-	if !exists {
-		// Debug: print available metrics for troubleshooting
-		availableMetrics := []string{}
-		for name := range metrics {
-			availableMetrics = append(availableMetrics, name)
-		}
-		GinkgoWriter.Printf("Available metrics: %v\n", availableMetrics)
-	}
-	Expect(exists).To(BeTrue(), "metric %s should exist", metricName)
-}
-
-// ExpectMetricValue asserts that a metric has a specific value with given labels
-func ExpectMetricValue(metrics MetricsMap, metricName string, matchLabels map[string]string, expectedValue float64) {
-	value, found := getMetricValue(metrics, metricName, matchLabels)
-	Expect(found).To(BeTrue(), "metric %s with labels %v should exist", metricName, matchLabels)
-	Expect(value).To(Equal(expectedValue), "metric %s value mismatch", metricName)
-}
-
-// ExpectMetricGreaterThan asserts that a metric value is greater than a threshold
-func ExpectMetricGreaterThan(metrics MetricsMap, metricName string, matchLabels map[string]string, threshold float64) {
-	value, found := getMetricValue(metrics, metricName, matchLabels)
-	Expect(found).To(BeTrue(), "metric %s with labels %v should exist", metricName, matchLabels)
-	Expect(value).To(BeNumerically(">", threshold), "metric %s should be greater than %f", metricName, threshold)
-}
-

+ 1 - 0
e2e/run.sh

@@ -93,6 +93,7 @@ kubectl run --rm \
   --env="SECRETSERVER_URL=${SECRETSERVER_URL:-}" \
   --env="SECRETSERVER_URL=${SECRETSERVER_URL:-}" \
   --env="GRAFANA_URL=${GRAFANA_URL:-}" \
   --env="GRAFANA_URL=${GRAFANA_URL:-}" \
   --env="GRAFANA_TOKEN=${GRAFANA_TOKEN:-}" \
   --env="GRAFANA_TOKEN=${GRAFANA_TOKEN:-}" \
+  --env="E2E_PROVIDER_MODE=${E2E_PROVIDER_MODE:-}" \
   --env="VERSION=${VERSION}" \
   --env="VERSION=${VERSION}" \
   --env="TEST_SUITES=${TEST_SUITES}" \
   --env="TEST_SUITES=${TEST_SUITES}" \
   --overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \
   --overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \

+ 41 - 0
e2e/suites/provider/cases/kubernetes/capabilities_v2_test.go

@@ -0,0 +1,41 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+
+	"github.com/external-secrets/external-secrets-e2e/framework"
+	frameworkv2 "github.com/external-secrets/external-secrets-e2e/framework/v2"
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+)
+
+var _ = Describe("[kubernetes] v2 capabilities", Label("kubernetes", "v2", "capabilities"), func() {
+	f := framework.New("eso-kubernetes-v2-capabilities")
+	NewProvider(f)
+
+	BeforeEach(func() {
+		if !framework.IsV2ProviderMode() {
+			Skip("v2 mode only")
+		}
+	})
+
+	It("reports READ_WRITE capabilities for the Kubernetes provider", func() {
+		frameworkv2.WaitForProviderConnectionReady(f, f.Namespace.Name, f.Namespace.Name, defaultV2WaitTimeout)
+		frameworkv2.VerifyProviderConnectionCapabilities(f, f.Namespace.Name, f.Namespace.Name, esv1.ProviderReadWrite)
+	})
+})

+ 14 - 2
e2e/suites/provider/cases/kubernetes/kubernetes.go

@@ -30,7 +30,15 @@ import (
 
 
 const referentAuth = "with referent auth"
 const referentAuth = "with referent auth"
 
 
-var _ = Describe("[kubernetes] ", Label("kubernetes"), func() {
+func describeLabels() []any {
+	labels := []any{Label("kubernetes")}
+	if framework.IsV2ProviderMode() {
+		labels = append(labels, Label("v2"))
+	}
+	return labels
+}
+
+var _ = Describe("[kubernetes] ", append(describeLabels(), func() {
 	f := framework.New("eso-kubernetes")
 	f := framework.New("eso-kubernetes")
 	prov := NewProvider(f)
 	prov := NewProvider(f)
 
 
@@ -50,10 +58,14 @@ var _ = Describe("[kubernetes] ", Label("kubernetes"), func() {
 		framework.Compose(referentAuth, f, common.JSONDataWithProperty, withReferentStore),
 		framework.Compose(referentAuth, f, common.JSONDataWithProperty, withReferentStore),
 		framework.Compose(referentAuth, f, common.JSONDataWithoutTargetName, withReferentStore),
 		framework.Compose(referentAuth, f, common.JSONDataWithoutTargetName, withReferentStore),
 	)
 	)
-})
+})...)
 
 
 func withReferentStore(tc *framework.TestCase) {
 func withReferentStore(tc *framework.TestCase) {
 	tc.ExternalSecret.Spec.SecretStoreRef.Name = referentStoreName(tc.Framework)
 	tc.ExternalSecret.Spec.SecretStoreRef.Name = referentStoreName(tc.Framework)
+	if framework.IsV2ProviderMode() {
+		tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterProviderKindStr
+		return
+	}
 	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
 	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
 }
 }
 
 

+ 200 - 0
e2e/suites/provider/cases/kubernetes/metrics_v2_test.go

@@ -0,0 +1,200 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"context"
+	"fmt"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+
+	"github.com/external-secrets/external-secrets-e2e/framework"
+	frameworkv2 "github.com/external-secrets/external-secrets-e2e/framework/v2"
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+)
+
+var _ = Describe("[kubernetes] v2 metrics", Label("kubernetes", "v2", "metrics"), func() {
+	f := framework.New("eso-kubernetes-v2-metrics")
+	NewProvider(f)
+
+	controllerClientMethods := []string{"GetSecret", "GetSecretMap"}
+	providerClientMethods := []string{
+		"/provider.v1.SecretStoreProvider/GetSecret",
+		"/provider.v1.SecretStoreProvider/GetSecretMap",
+	}
+
+	hasSuccessfulClientRequest := func(metrics frameworkv2.MetricsMap, methods []string) bool {
+		for _, method := range methods {
+			value, found := frameworkv2.GetMetricValue(metrics, "grpc_client_requests_total", map[string]string{
+				"method": method,
+				"status": "success",
+			})
+			if found && value >= 1.0 {
+				return true
+			}
+		}
+		return false
+	}
+
+	hasSuccessfulServerRequest := func(metrics frameworkv2.MetricsMap, methods []string) bool {
+		for _, method := range methods {
+			value, found := frameworkv2.GetMetricValue(metrics, "grpc_server_requests_total", map[string]string{
+				"method": method,
+				"status": "success",
+			})
+			if found && value >= 1.0 {
+				return true
+			}
+		}
+		return false
+	}
+
+	BeforeEach(func() {
+		if !framework.IsV2ProviderMode() {
+			Skip("v2 mode only")
+		}
+		frameworkv2.WaitForProviderConnectionReady(f, f.Namespace.Name, f.Namespace.Name, defaultV2WaitTimeout)
+		frameworkv2.WaitForClusterProviderReady(f, referentStoreName(f), defaultV2WaitTimeout)
+	})
+
+	It("exposes Provider and ClusterProvider controller metrics", func() {
+		metrics, err := frameworkv2.ScrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet, frameworkv2.ProviderNamespace)
+		Expect(err).ToNot(HaveOccurred())
+
+		frameworkv2.ExpectMetricExists(metrics, "provider_status_condition")
+		frameworkv2.ExpectMetricValue(metrics, "provider_status_condition", map[string]string{
+			"name":      f.Namespace.Name,
+			"namespace": f.Namespace.Name,
+			"condition": "Ready",
+			"status":    "True",
+		}, 1.0)
+		frameworkv2.ExpectMetricGreaterThan(metrics, "provider_reconcile_duration", map[string]string{
+			"name":      f.Namespace.Name,
+			"namespace": f.Namespace.Name,
+		}, 0.0)
+
+		frameworkv2.ExpectMetricExists(metrics, "clusterprovider_status_condition")
+		frameworkv2.ExpectMetricValue(metrics, "clusterprovider_status_condition", map[string]string{
+			"name":      referentStoreName(f),
+			"condition": "Ready",
+			"status":    "True",
+		}, 1.0)
+		frameworkv2.ExpectMetricGreaterThan(metrics, "clusterprovider_reconcile_duration", map[string]string{
+			"name": referentStoreName(f),
+		}, 0.0)
+	})
+
+	It("tracks client, server, and cache metrics during secret sync", func() {
+		externalSecretName := "test-es-metrics"
+		targetSecretName := "test-secret-metrics"
+		tcSecretOne := fmt.Sprintf("%s-one", f.Namespace.Name)
+		tcSecretTwo := fmt.Sprintf("%s-two", f.Namespace.Name)
+
+		secretOne := &corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      tcSecretOne,
+				Namespace: f.Namespace.Name,
+			},
+			Data: map[string][]byte{
+				"foo": []byte("bar"),
+			},
+		}
+		secretTwo := &corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      tcSecretTwo,
+				Namespace: f.Namespace.Name,
+			},
+			Data: map[string][]byte{
+				"baz": []byte("qux"),
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), secretOne)).To(Succeed())
+		Expect(f.CRClient.Create(context.Background(), secretTwo)).To(Succeed())
+
+		es := &esv1.ExternalSecret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      externalSecretName,
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1.ExternalSecretSpec{
+				RefreshInterval: &metav1.Duration{Duration: defaultV2RefreshInterval},
+				SecretStoreRef: esv1.SecretStoreRef{
+					Name: f.Namespace.Name,
+					Kind: esv1.ProviderKindStr,
+				},
+				Target: esv1.ExternalSecretTarget{
+					Name: targetSecretName,
+				},
+				Data: []esv1.ExternalSecretData{
+					{
+						SecretKey: "one",
+						RemoteRef: esv1.ExternalSecretDataRemoteRef{
+							Key:      tcSecretOne,
+							Property: "foo",
+						},
+					},
+					{
+						SecretKey: "two",
+						RemoteRef: esv1.ExternalSecretDataRemoteRef{
+							Key:      tcSecretTwo,
+							Property: "baz",
+						},
+					},
+				},
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
+
+		Eventually(func(g Gomega) {
+			var secret corev1.Secret
+			g.Expect(f.CRClient.Get(context.Background(), types.NamespacedName{Name: targetSecretName, Namespace: f.Namespace.Name}, &secret)).To(Succeed())
+			g.Expect(secret.Data["one"]).To(Equal([]byte("bar")))
+			g.Expect(secret.Data["two"]).To(Equal([]byte("qux")))
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(Succeed())
+
+		Eventually(func() bool {
+			metrics, err := frameworkv2.ScrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet, frameworkv2.ProviderNamespace)
+			if err != nil {
+				return false
+			}
+			return hasSuccessfulClientRequest(metrics, controllerClientMethods)
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(BeTrue())
+
+		Eventually(func() bool {
+			metrics, err := frameworkv2.ScrapeProviderMetrics(context.Background(), f.KubeConfig, f.KubeClientSet, frameworkv2.ProviderNamespace, "kubernetes")
+			if err != nil {
+				return false
+			}
+			return hasSuccessfulServerRequest(metrics, providerClientMethods)
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(BeTrue())
+
+		Eventually(func() bool {
+			metrics, err := frameworkv2.ScrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet, frameworkv2.ProviderNamespace)
+			if err != nil {
+				return false
+			}
+			value, found := frameworkv2.GetMetricValue(metrics, "clientmanager_cache_hits_total", map[string]string{
+				"provider_type": "provider",
+			})
+			return found && value >= 1.0
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(BeTrue())
+	})
+})

+ 99 - 4
e2e/suites/provider/cases/kubernetes/provider.go

@@ -19,6 +19,7 @@ package kubernetes
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
+	"time"
 
 
 	// nolint
 	// nolint
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/ginkgo/v2"
@@ -31,10 +32,13 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework"
+	frameworkv2 "github.com/external-secrets/external-secrets-e2e/framework/v2"
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 )
 )
 
 
+const kubernetesProviderAPIVersion = "provider.external-secrets.io/v2alpha1"
+
 type Provider struct {
 type Provider struct {
 	framework *framework.Framework
 	framework *framework.Framework
 }
 }
@@ -68,6 +72,12 @@ func (s *Provider) CreateSecret(key string, val framework.SecretEntry) {
 }
 }
 
 
 func (s *Provider) BeforeEach() {
 func (s *Provider) BeforeEach() {
+	if framework.IsV2ProviderMode() {
+		s.CreateStoreV2()
+		s.CreateReferentStoreV2()
+		return
+	}
+
 	s.CreateStore()
 	s.CreateStore()
 	s.CreateReferentStore()
 	s.CreateReferentStore()
 }
 }
@@ -153,12 +163,12 @@ func makeDefaultStore(suffix, namespace string) (*rbac.Role, *rbac.RoleBinding,
 }
 }
 
 
 func (s *Provider) CreateStore() {
 func (s *Provider) CreateStore() {
-	rb, role, store := makeDefaultStore("", s.framework.Namespace.Name)
+	role, roleBinding, store := makeDefaultStore("", s.framework.Namespace.Name)
 
 
 	err := s.framework.CRClient.Create(GinkgoT().Context(), role)
 	err := s.framework.CRClient.Create(GinkgoT().Context(), role)
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 
 
-	err = s.framework.CRClient.Create(GinkgoT().Context(), rb)
+	err = s.framework.CRClient.Create(GinkgoT().Context(), roleBinding)
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 
 
 	err = s.framework.CRClient.Create(GinkgoT().Context(), store)
 	err = s.framework.CRClient.Create(GinkgoT().Context(), store)
@@ -166,7 +176,7 @@ func (s *Provider) CreateStore() {
 }
 }
 
 
 func (s *Provider) CreateReferentStore() {
 func (s *Provider) CreateReferentStore() {
-	rb, role, store := makeDefaultStore("referent", s.framework.Namespace.Name)
+	role, roleBinding, store := makeDefaultStore("referent", s.framework.Namespace.Name)
 	// ServiceAccount Namespace is not set, this will be inferred
 	// ServiceAccount Namespace is not set, this will be inferred
 	// from the ExternalSecret
 	// from the ExternalSecret
 	css := &esv1.ClusterSecretStore{
 	css := &esv1.ClusterSecretStore{
@@ -180,13 +190,98 @@ func (s *Provider) CreateReferentStore() {
 	err := s.framework.CRClient.Create(GinkgoT().Context(), role)
 	err := s.framework.CRClient.Create(GinkgoT().Context(), role)
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 
 
-	err = s.framework.CRClient.Create(GinkgoT().Context(), rb)
+	err = s.framework.CRClient.Create(GinkgoT().Context(), roleBinding)
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 
 
 	err = s.framework.CRClient.Create(GinkgoT().Context(), css)
 	err = s.framework.CRClient.Create(GinkgoT().Context(), css)
 	Expect(err).ToNot(HaveOccurred())
 	Expect(err).ToNot(HaveOccurred())
 }
 }
 
 
+func (s *Provider) CreateStoreV2() {
+	namespace := s.framework.Namespace.Name
+
+	frameworkv2.CreateKubernetesAccessRole(
+		s.framework,
+		storeAccessName(namespace, ""),
+		frameworkv2.DefaultSAName,
+		namespace,
+		namespace,
+	)
+
+	serviceAccountNamespace := namespace
+	frameworkv2.CreateKubernetesProvider(
+		s.framework,
+		namespace,
+		providerConfigName(namespace, ""),
+		namespace,
+		frameworkv2.DefaultSAName,
+		&serviceAccountNamespace,
+		frameworkv2.GetClusterCABundle(s.framework, namespace),
+	)
+
+	frameworkv2.CreateProviderConnection(
+		s.framework,
+		namespace,
+		namespace,
+		frameworkv2.ProviderAddress("kubernetes"),
+		kubernetesProviderAPIVersion,
+		"Kubernetes",
+		providerConfigName(namespace, ""),
+		namespace,
+	)
+	frameworkv2.WaitForProviderConnectionReady(s.framework, namespace, namespace, 30*time.Second)
+}
+
+func (s *Provider) CreateReferentStoreV2() {
+	namespace := s.framework.Namespace.Name
+	referentName := referentStoreName(s.framework)
+
+	frameworkv2.CreateKubernetesAccessRole(
+		s.framework,
+		storeAccessName(namespace, "referent"),
+		frameworkv2.DefaultSAName,
+		namespace,
+		namespace,
+	)
+
+	frameworkv2.CreateKubernetesProvider(
+		s.framework,
+		namespace,
+		providerConfigName(namespace, "referent"),
+		namespace,
+		frameworkv2.DefaultSAName,
+		nil,
+		frameworkv2.GetClusterCABundle(s.framework, namespace),
+	)
+
+	frameworkv2.CreateClusterProviderConnection(
+		s.framework,
+		referentName,
+		frameworkv2.ProviderAddress("kubernetes"),
+		kubernetesProviderAPIVersion,
+		"Kubernetes",
+		providerConfigName(namespace, "referent"),
+		namespace,
+		esv1.AuthenticationScopeManifestNamespace,
+		nil,
+	)
+	frameworkv2.WaitForClusterProviderReady(s.framework, referentName, 30*time.Second)
+}
+
 func referentStoreName(f *framework.Framework) string {
 func referentStoreName(f *framework.Framework) string {
 	return fmt.Sprintf("%s-referent", f.Namespace.Name)
 	return fmt.Sprintf("%s-referent", f.Namespace.Name)
 }
 }
+
+func providerConfigName(namespace, suffix string) string {
+	if suffix == "" {
+		return fmt.Sprintf("%s-kubernetes", namespace)
+	}
+	return fmt.Sprintf("%s-kubernetes-%s", namespace, suffix)
+}
+
+func storeAccessName(namespace, suffix string) string {
+	if suffix == "" {
+		return fmt.Sprintf("%s-provider-access", namespace)
+	}
+	return fmt.Sprintf("%s-provider-access-%s", namespace, suffix)
+}

+ 183 - 0
e2e/suites/provider/cases/kubernetes/push_v2_test.go

@@ -0,0 +1,183 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"context"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+
+	"github.com/external-secrets/external-secrets-e2e/framework"
+	frameworkv2 "github.com/external-secrets/external-secrets-e2e/framework/v2"
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+)
+
+var _ = Describe("[kubernetes] v2 push secret", Label("kubernetes", "v2", "push-secret"), func() {
+	f := framework.New("eso-kubernetes-v2-push")
+	NewProvider(f)
+
+	BeforeEach(func() {
+		if !framework.IsV2ProviderMode() {
+			Skip("v2 mode only")
+		}
+		frameworkv2.WaitForProviderConnectionReady(f, f.Namespace.Name, f.Namespace.Name, defaultV2WaitTimeout)
+	})
+
+	It("pushes a secret to the Kubernetes provider", func() {
+		sourceSecret := &corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "source-secret",
+				Namespace: f.Namespace.Name,
+			},
+			Data: map[string][]byte{
+				"username": []byte("admin"),
+				"password": []byte("secret123"),
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), sourceSecret)).To(Succeed())
+
+		pushSecret := &esv1alpha1.PushSecret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-pushsecret",
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1alpha1.PushSecretSpec{
+				RefreshInterval: &metav1.Duration{Duration: defaultV2RefreshInterval},
+				SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
+					{
+						Name:       f.Namespace.Name,
+						Kind:       f.DefaultPushSecretStoreRefKind,
+						APIVersion: f.DefaultPushSecretStoreRefAPIVersion,
+					},
+				},
+				Selector: esv1alpha1.PushSecretSelector{
+					Secret: &esv1alpha1.PushSecretSecret{
+						Name: sourceSecret.Name,
+					},
+				},
+				Data: []esv1alpha1.PushSecretData{
+					{
+						Match: esv1alpha1.PushSecretMatch{
+							SecretKey: "username",
+							RemoteRef: esv1alpha1.PushSecretRemoteRef{
+								RemoteKey: "pushed-secret",
+								Property:  "username",
+							},
+						},
+					},
+					{
+						Match: esv1alpha1.PushSecretMatch{
+							SecretKey: "password",
+							RemoteRef: esv1alpha1.PushSecretRemoteRef{
+								RemoteKey: "pushed-secret",
+								Property:  "password",
+							},
+						},
+					},
+				},
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), pushSecret)).To(Succeed())
+
+		Eventually(func(g Gomega) {
+			var ps esv1alpha1.PushSecret
+			g.Expect(f.CRClient.Get(context.Background(), types.NamespacedName{Name: pushSecret.Name, Namespace: f.Namespace.Name}, &ps)).To(Succeed())
+			g.Expect(ps.Status.Conditions).NotTo(BeEmpty())
+			ready := false
+			for _, condition := range ps.Status.Conditions {
+				if condition.Type == esv1alpha1.PushSecretReady && condition.Status == corev1.ConditionTrue {
+					ready = true
+				}
+			}
+			g.Expect(ready).To(BeTrue())
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(Succeed())
+
+		var pushedSecret corev1.Secret
+		Eventually(func(g Gomega) {
+			g.Expect(f.CRClient.Get(context.Background(), types.NamespacedName{Name: "pushed-secret", Namespace: f.Namespace.Name}, &pushedSecret)).To(Succeed())
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(Succeed())
+
+		Expect(string(pushedSecret.Data["username"])).To(Equal("admin"))
+		Expect(string(pushedSecret.Data["password"])).To(Equal("secret123"))
+	})
+
+	It("deletes pushed secrets when DeletionPolicy=Delete", func() {
+		sourceSecret := &corev1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "source-secret-delete",
+				Namespace: f.Namespace.Name,
+			},
+			Data: map[string][]byte{
+				"key1": []byte("value1"),
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), sourceSecret)).To(Succeed())
+
+		pushSecret := &esv1alpha1.PushSecret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "test-pushsecret-delete",
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1alpha1.PushSecretSpec{
+				RefreshInterval: &metav1.Duration{Duration: defaultV2RefreshInterval},
+				DeletionPolicy:  esv1alpha1.PushSecretDeletionPolicyDelete,
+				SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
+					{
+						Name:       f.Namespace.Name,
+						Kind:       f.DefaultPushSecretStoreRefKind,
+						APIVersion: f.DefaultPushSecretStoreRefAPIVersion,
+					},
+				},
+				Selector: esv1alpha1.PushSecretSelector{
+					Secret: &esv1alpha1.PushSecretSecret{
+						Name: sourceSecret.Name,
+					},
+				},
+				Data: []esv1alpha1.PushSecretData{
+					{
+						Match: esv1alpha1.PushSecretMatch{
+							SecretKey: "key1",
+							RemoteRef: esv1alpha1.PushSecretRemoteRef{
+								RemoteKey: "pushed-secret-delete",
+								Property:  "key1",
+							},
+						},
+					},
+				},
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), pushSecret)).To(Succeed())
+
+		Eventually(func(g Gomega) {
+			var pushedSecret corev1.Secret
+			g.Expect(f.CRClient.Get(context.Background(), types.NamespacedName{Name: "pushed-secret-delete", Namespace: f.Namespace.Name}, &pushedSecret)).To(Succeed())
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(Succeed())
+
+		Expect(f.CRClient.Delete(context.Background(), pushSecret)).To(Succeed())
+
+		Eventually(func() bool {
+			var pushedSecret corev1.Secret
+			err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: "pushed-secret-delete", Namespace: f.Namespace.Name}, &pushedSecret)
+			return apierrors.IsNotFound(err)
+		}, defaultV2WaitTimeout, defaultV2PollInterval).Should(BeTrue())
+	})
+})

+ 9 - 11
e2e/suites/v2/suite_test.go → e2e/suites/provider/cases/kubernetes/v2_constants_test.go

@@ -1,9 +1,11 @@
 /*
 /*
+Copyright © 2025 ESO Maintainer Team
+
 Licensed under the Apache License, Version 2.0 (the "License");
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 You may obtain a copy of the License at
 
 
-	http://www.apache.org/licenses/LICENSE-2.0
+    https://www.apache.org/licenses/LICENSE-2.0
 
 
 Unless required by applicable law or agreed to in writing, software
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,16 +14,12 @@ See the License for the specific language governing permissions and
 limitations under the License.
 limitations under the License.
 */
 */
 
 
-package v2
+package kubernetes
 
 
-import (
-	"testing"
+import "time"
 
 
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
+const (
+	defaultV2WaitTimeout     = 60 * time.Second
+	defaultV2PollInterval    = 2 * time.Second
+	defaultV2RefreshInterval = 10 * time.Second
 )
 )
-
-func TestV2Suite(t *testing.T) {
-	RegisterFailHandler(Fail)
-	RunSpecs(t, "V2 E2E Suite")
-}

+ 11 - 0
e2e/suites/provider/suite_test.go

@@ -25,6 +25,7 @@ import (
 	// nolint
 	// nolint
 	. "github.com/onsi/gomega"
 	. "github.com/onsi/gomega"
 
 
+	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework/addon"
 	"github.com/external-secrets/external-secrets-e2e/framework/addon"
 	"github.com/external-secrets/external-secrets-e2e/framework/util"
 	"github.com/external-secrets/external-secrets-e2e/framework/util"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases"
@@ -33,6 +34,16 @@ import (
 )
 )
 
 
 var _ = SynchronizedBeforeSuite(func() []byte {
 var _ = SynchronizedBeforeSuite(func() []byte {
+	if framework.IsV2ProviderMode() {
+		By("installing eso in provider v2 mode")
+		addon.InstallGlobalAddon(addon.NewESO(
+			addon.WithCRDs(),
+			addon.WithV2Namespace(),
+			addon.WithV2KubernetesProvider(),
+		))
+		return nil
+	}
+
 	By("installing eso")
 	By("installing eso")
 	addon.InstallGlobalAddon(addon.NewESO(addon.WithCRDs()))
 	addon.InstallGlobalAddon(addon.NewESO(addon.WithCRDs()))
 
 

+ 0 - 322
e2e/suites/v2/README.md

@@ -1,322 +0,0 @@
-# External Secrets Operator V2 E2E Test Suite
-
-This directory contains End-to-End tests for ESO V2.
-
-## Test Coverage
-
-### ✅ Implemented Tests
-
-| Test Case | Description | Status |
-|-----------|-------------|--------|
-| **Basic Secret Sync** | Cross-namespace secret synchronization | ✅ |
-| **Key Extraction** | Extract specific keys with `data[]` | ✅ |
-| **DataFrom** | Full secret extraction with `dataFrom[]` | ✅ |
-| **Secret Updates** | Automatic refresh when source changes | ✅ |
-| **Deletion Cleanup** | Owner policy cleanup on deletion | ✅ |
-| **Error Handling** | Error conditions for missing secrets | ✅ |
-
-### 🚧 Future Test Coverage
-
-| Test Case | Priority | Notes |
-|-----------|----------|-------|
-| ClusterSecretStore | P1 | Cross-namespace store |
-| Multiple providers | P1 | AWS, GCP, Azure providers |
-| Secret templates | P2 | Template transformations |
-| Generators | P2 | Generator integration |
-| PushSecret | P2 | Reverse sync |
-| Concurrency | P2 | Multiple ExternalSecrets |
-| TLS/mTLS | P3 | Provider authentication |
-| Metrics | P3 | Prometheus metrics validation |
-| Performance | P3 | Latency and throughput |
-
-## Running Tests
-
-### All V2 Tests
-
-```bash
-make test.e2e.v2
-```
-
-### Specific Tests
-
-```bash
-cd e2e
-
-# Run single test
-ginkgo -v --focus="should sync secrets across namespaces" ./suites/v2/
-
-# Run with labels
-ginkgo -v --label-filter="v2 && !slow" ./suites/v2/
-
-# Verbose output
-ginkgo -vv ./suites/v2/
-```
-
-## Test Architecture
-
-### Framework Components
-
-```
-┌─────────────────────────────────────┐
-│       Ginkgo Test Suite             │
-│   (suite_test.go, v2_test.go)       │
-└─────────────────┬───────────────────┘
-                  │
-                  ▼
-┌─────────────────────────────────────┐
-│      E2E Framework                  │
-│   (framework/framework.go)          │
-│   - Kubernetes client               │
-│   - Test utilities                  │
-└─────────────────┬───────────────────┘
-                  │
-                  ▼
-┌─────────────────────────────────────┐
-│      ESO V2 Addon                   │
-│   (framework/addon/eso_v2.go)       │
-│   - Controller installation         │
-│   - Provider installation           │
-│   - RBAC setup                      │
-└─────────────────────────────────────┘
-```
-
-### Test Flow
-
-```
-1. BeforeSuite
-   ├── Initialize framework
-   ├── Install ESO V2 controller
-   ├── Install Kubernetes provider
-   └── Wait for ready
-
-2. BeforeEach (per test)
-   ├── Create test namespace
-   └── Create source secret
-
-3. Test Execution
-   ├── Create SecretStore
-   ├── Create ExternalSecret
-   ├── Wait for sync (Eventually)
-   └── Assert results (Gomega)
-
-4. AfterEach (per test)
-   └── Delete test namespace
-
-5. AfterSuite
-   └── Uninstall ESO V2
-```
-
-## Test Patterns
-
-### Creating Resources
-
-```go
-secretStore := &ssv2alpha1.SecretStore{
-    ObjectMeta: metav1.ObjectMeta{
-        Name:      "test-store",
-        Namespace: namespace,
-    },
-    Spec: ssv2alpha1.SecretStoreSpec{
-        Provider: ssv2alpha1.ProviderConfig{
-            Address: "kubernetes-provider:5000",
-        },
-    },
-}
-Expect(f.CRClient.Create(ctx, secretStore)).To(Succeed())
-```
-
-### Waiting for Conditions
-
-```go
-Eventually(func() bool {
-    var ss ssv2alpha1.SecretStore
-    err := f.CRClient.Get(ctx, client.ObjectKeyFromObject(secretStore), &ss)
-    if err != nil {
-        return false
-    }
-    
-    for _, cond := range ss.Status.Conditions {
-        if cond.Type == "Ready" && cond.Status == metav1.ConditionTrue {
-            return true
-        }
-    }
-    return false
-}, 30*time.Second, 1*time.Second).Should(BeTrue())
-```
-
-### Validating Data
-
-```go
-var targetSecret corev1.Secret
-Expect(f.CRClient.Get(ctx, targetKey, &targetSecret)).To(Succeed())
-Expect(targetSecret.Data).To(HaveKeyWithValue("username", []byte("admin")))
-```
-
-## Debugging
-
-### Enable Verbose Logging
-
-```bash
-# Ginkgo verbose
-ginkgo -v ./suites/v2/
-
-# Very verbose (includes test internals)
-ginkgo -vv ./suites/v2/
-
-# Trace level (framework logs)
-ginkgo -v -trace ./suites/v2/
-```
-
-### View Controller Logs
-
-```bash
-kubectl logs -n external-secrets-system \
-  -l app.kubernetes.io/name=external-secrets-v2 \
-  --tail=100 -f
-```
-
-### View Provider Logs
-
-```bash
-kubectl logs -n external-secrets-system \
-  -l app.kubernetes.io/name=kubernetes-provider \
-  --tail=100 -f
-```
-
-### Inspect Resources
-
-```bash
-# List all test namespaces
-kubectl get ns | grep v2-test
-
-# Check SecretStores
-kubectl get secretstore --all-namespaces
-
-# Check ExternalSecrets
-kubectl get externalsecret --all-namespaces
-
-# Describe for details
-kubectl describe externalsecret <name> -n <namespace>
-```
-
-### Keep Environment After Failure
-
-```bash
-# Run without cleanup
-ginkgo -v ./suites/v2/ || true
-
-# Inspect manually
-kubectl get all --all-namespaces | grep v2-test
-
-# Manual cleanup when done
-kubectl delete ns external-secrets-system
-```
-
-## Adding New Tests
-
-### Test Template
-
-```go
-It("should <test description>", Label("v2", "feature-name"), func() {
-    By("step 1: setup")
-    // Create resources
-    
-    By("step 2: action")
-    // Trigger behavior
-    
-    By("step 3: verification")
-    Eventually(func() bool {
-        // Check condition
-        return true
-    }, timeout, interval).Should(BeTrue())
-    
-    By("step 4: assert")
-    // Final assertions
-    Expect(actual).To(Equal(expected))
-})
-```
-
-### Checklist
-
-- [ ] Descriptive test name
-- [ ] Appropriate labels
-- [ ] Clear `By()` steps
-- [ ] Use `Eventually()` for async
-- [ ] Proper cleanup in `AfterEach()`
-- [ ] Meaningful assertions
-- [ ] Error messages for failures
-
-## CI Integration
-
-Tests run automatically on:
-- Pull requests touching V2 code
-- Nightly builds
-- Release branches
-
-### GitHub Actions
-
-```yaml
-- name: Run V2 E2E
-  run: make test.e2e.v2
-```
-
-### Required Checks
-
-- All tests pass
-- No resource leaks
-- Controller logs clean
-- No memory/CPU spikes
-
-## Metrics
-
-### Current Stats
-
-- **Test Files**: 2
-- **Test Cases**: 6
-- **Coverage**: Core functionality
-- **Duration**: ~2-3 minutes
-
-### Performance Benchmarks
-
-| Operation | P50 | P95 | P99 |
-|-----------|-----|-----|-----|
-| SecretStore ready | 2s | 5s | 10s |
-| ExternalSecret sync | 3s | 8s | 15s |
-| Secret update | 5s | 12s | 20s |
-
-## Troubleshooting
-
-### Test Fails: "SecretStore not ready"
-
-**Cause**: Provider not reachable  
-**Fix**: Check provider pod status
-
-```bash
-kubectl get pods -n external-secrets-system
-kubectl logs -n external-secrets-system -l app.kubernetes.io/name=kubernetes-provider
-```
-
-### Test Fails: "Secret not synced"
-
-**Cause**: Source secret missing or permissions  
-**Fix**: Verify source secret exists
-
-```bash
-kubectl get secret -n <source-namespace>
-kubectl get sa -n external-secrets-system
-```
-
-### Test Timeout
-
-**Cause**: Slow cluster or image pull  
-**Fix**: Increase timeout or pre-pull images
-
-```go
-Eventually(..., 60*time.Second, ...).Should(...)
-```
-
-## Resources
-
-- [V2 E2E Testing Guide](../../../docs/contributing/v2/e2e-testing.md)
-- [V2 Design Doc](../../../design/014-secretstore-generator-v2.md)
-- [Ginkgo Documentation](https://onsi.github.io/ginkgo/)

+ 0 - 408
e2e/suites/v2/fake_cluster_provider_test.go

@@ -1,408 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-	v1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("[v2] Fake ClusterProvider", Label("v2", "fake", "cluster-provider"), func() {
-	f := framework.New("v2-fake-cluster-provider")
-
-	Context("GetSecret with ClusterProvider", func() {
-		var testNamespace *corev1.Namespace
-
-		BeforeEach(func() {
-			testNamespace = SetupTestNamespace(f, "v2-fake-cluster-")
-
-			// Create Fake provider with test data
-			CreateFakeProvider(f, testNamespace.Name, "fake-provider-cluster", []v1.FakeProviderData{
-				{Key: "cluster-username", Value: "cluster-user"},
-				{Key: "cluster-password", Value: "cluster-password"},
-				{Key: "cluster-token", Value: "cluster-token-12345"},
-			})
-		})
-
-		AfterEach(func() {
-			if testNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-			}
-		})
-
-		It("should sync secrets from ClusterProvider", func() {
-			By("creating a ClusterProvider pointing to Fake provider")
-			CreateClusterProvider(f, "cluster-fake-provider",
-				"provider-fake.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Fake",
-				"fake-provider-cluster",
-				testNamespace.Name,
-				esv1.AuthenticationScopeProviderNamespace,
-				nil)
-
-			By("waiting for ClusterProvider to be ready")
-			WaitForClusterProviderReady(f, "cluster-fake-provider", 30*time.Second)
-
-			By("creating an ExternalSecret")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-cluster",
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "cluster-fake-provider",
-						Kind: "ClusterProvider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "synced-cluster-secret",
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "cluster-username",
-							},
-						},
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "cluster-password",
-							},
-						},
-						{
-							SecretKey: "token",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "cluster-token",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
-
-			By("waiting for secret to be synced")
-			var syncedSecret corev1.Secret
-			Eventually(func() bool {
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "synced-cluster-secret", Namespace: testNamespace.Name},
-					&syncedSecret)
-				return err == nil
-			}, 30*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("verifying the synced secret data")
-			Expect(syncedSecret.Data["username"]).To(Equal([]byte("cluster-user")))
-			Expect(syncedSecret.Data["password"]).To(Equal([]byte("cluster-password")))
-			Expect(syncedSecret.Data["token"]).To(Equal([]byte("cluster-token-12345")))
-		})
-
-		It("should work from multiple namespaces", func() {
-			testNamespace2 := SetupTestNamespace(f, "v2-fake-cluster-2-")
-			defer func() {
-				Expect(f.CRClient.Delete(context.Background(), testNamespace2)).To(Succeed())
-			}()
-
-			By("creating a ClusterProvider")
-			CreateClusterProvider(f, "cluster-fake-multi-ns",
-				"provider-fake.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Fake",
-				"fake-provider-cluster",
-				testNamespace.Name,
-				esv1.AuthenticationScopeProviderNamespace,
-				nil)
-
-			WaitForClusterProviderReady(f, "cluster-fake-multi-ns", 30*time.Second)
-
-			By("creating ExternalSecrets in both namespaces")
-			for _, ns := range []string{testNamespace.Name, testNamespace2.Name} {
-				es := &esv1.ExternalSecret{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "test-es-multi",
-						Namespace: ns,
-					},
-					Spec: esv1.ExternalSecretSpec{
-						SecretStoreRef: esv1.SecretStoreRef{
-							Name: "cluster-fake-multi-ns",
-							Kind: "ClusterProvider",
-						},
-						Target: esv1.ExternalSecretTarget{
-							Name: "multi-ns-secret",
-						},
-						Data: []esv1.ExternalSecretData{
-							{
-								SecretKey: "username",
-								RemoteRef: esv1.ExternalSecretDataRemoteRef{
-									Key: "cluster-username",
-								},
-							},
-						},
-					},
-				}
-				Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
-			}
-
-			By("verifying secrets are synced in both namespaces")
-			for _, ns := range []string{testNamespace.Name, testNamespace2.Name} {
-				var syncedSecret corev1.Secret
-				Eventually(func() bool {
-					err := f.CRClient.Get(context.Background(),
-						types.NamespacedName{Name: "multi-ns-secret", Namespace: ns},
-						&syncedSecret)
-					return err == nil
-				}, 30*time.Second, 1*time.Second).Should(BeTrue(), "Secret should be synced in namespace "+ns)
-
-				Expect(syncedSecret.Data["username"]).To(Equal([]byte("cluster-user")))
-			}
-		})
-	})
-
-	Context("Generator Support with ClusterProvider", func() {
-		var testNamespace *corev1.Namespace
-
-		BeforeEach(func() {
-			testNamespace = SetupTestNamespace(f, "v2-fake-cluster-gen-")
-			CreateFakeProvider(f, testNamespace.Name, "fake-provider-gen", []v1.FakeProviderData{})
-		})
-
-		AfterEach(func() {
-			if testNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-			}
-		})
-
-		It("should generate secrets from Fake generator with ClusterProvider", func() {
-			By("creating a Fake generator")
-			CreateFakeGenerator(f, testNamespace.Name, "test-cluster-generator", map[string]string{
-				"gen-username": "generated-cluster-user",
-				"gen-password": "generated-cluster-password",
-				"gen-api-key":  "generated-cluster-api-key",
-			})
-
-			By("creating a ClusterProvider for generator support")
-			CreateClusterProvider(f, "cluster-fake-generator",
-				"provider-fake.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Fake",
-				"fake-provider-gen",
-				testNamespace.Name,
-				esv1.AuthenticationScopeProviderNamespace,
-				nil)
-
-			WaitForClusterProviderReady(f, "cluster-fake-generator", 30*time.Second)
-
-			By("creating an ExternalSecret with dataFrom referencing the generator")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-cluster-generator",
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "cluster-fake-generator",
-						Kind: "ClusterProvider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "cluster-generated-secret",
-					},
-					DataFrom: []esv1.ExternalSecretDataFromRemoteRef{
-						{
-							SourceRef: &esv1.StoreGeneratorSourceRef{
-								GeneratorRef: &esv1.GeneratorRef{
-									APIVersion: "generators.external-secrets.io/v1alpha1",
-									Kind:       "Fake",
-									Name:       "test-cluster-generator",
-								},
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
-
-			By("waiting for secret to be synced")
-			var syncedSecret corev1.Secret
-			Eventually(func() bool {
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "cluster-generated-secret", Namespace: testNamespace.Name},
-					&syncedSecret)
-				return err == nil
-			}, 30*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("verifying the generated secret data")
-			Expect(syncedSecret.Data["gen-username"]).To(Equal([]byte("generated-cluster-user")))
-			Expect(syncedSecret.Data["gen-password"]).To(Equal([]byte("generated-cluster-password")))
-			Expect(syncedSecret.Data["gen-api-key"]).To(Equal([]byte("generated-cluster-api-key")))
-		})
-	})
-
-	Context("Namespace Conditions", func() {
-		var (
-			testNamespaceAllowed *corev1.Namespace
-			testNamespaceDenied  *corev1.Namespace
-		)
-
-		BeforeEach(func() {
-			testNamespaceAllowed = SetupTestNamespace(f, "v2-fake-cluster-allowed-")
-			testNamespaceDenied = SetupTestNamespace(f, "v2-fake-cluster-denied-")
-
-			// Label the allowed namespace
-			testNamespaceAllowed.Labels = map[string]string{"team": "platform"}
-			Expect(f.CRClient.Update(context.Background(), testNamespaceAllowed)).To(Succeed())
-
-			// Create Fake provider
-			CreateFakeProvider(f, testNamespaceAllowed.Name, "fake-provider-conditions", []v1.FakeProviderData{
-				{Key: "test-key", Value: "test-value"},
-			})
-		})
-
-		AfterEach(func() {
-			if testNamespaceAllowed != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespaceAllowed)).To(Succeed())
-			}
-			if testNamespaceDenied != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespaceDenied)).To(Succeed())
-			}
-		})
-
-		It("should enforce namespace label selectors", func() {
-			By("creating a ClusterProvider with namespace selector")
-			conditions := []esv1.ClusterSecretStoreCondition{
-				{
-					NamespaceSelector: &metav1.LabelSelector{
-						MatchLabels: map[string]string{"team": "platform"},
-					},
-				},
-			}
-			CreateClusterProvider(f, "cluster-fake-labeled",
-				"provider-fake.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Fake",
-				"fake-provider-conditions",
-				testNamespaceAllowed.Name,
-				esv1.AuthenticationScopeProviderNamespace,
-				conditions)
-
-			WaitForClusterProviderReady(f, "cluster-fake-labeled", 30*time.Second)
-
-			By("creating ExternalSecret in allowed namespace")
-			esAllowed := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-allowed",
-					Namespace: testNamespaceAllowed.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "cluster-fake-labeled",
-						Kind: "ClusterProvider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "allowed-secret",
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "test-key",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "test-key",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), esAllowed)).To(Succeed())
-
-			By("verifying ExternalSecret in allowed namespace succeeds")
-			var allowedSecret corev1.Secret
-			Eventually(func() bool {
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "allowed-secret", Namespace: testNamespaceAllowed.Name},
-					&allowedSecret)
-				return err == nil
-			}, 30*time.Second, 1*time.Second).Should(BeTrue())
-
-			Expect(allowedSecret.Data["test-key"]).To(Equal([]byte("test-value")))
-
-			By("creating ExternalSecret in denied namespace")
-			esDenied := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-denied",
-					Namespace: testNamespaceDenied.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "cluster-fake-labeled",
-						Kind: "ClusterProvider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "denied-secret",
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "test-key",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "test-key",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), esDenied)).To(Succeed())
-
-			By("verifying ExternalSecret in denied namespace fails")
-			// First wait for the ExternalSecret to be reconciled and have a condition
-			Eventually(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-denied", Namespace: testNamespaceDenied.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-				return len(es.Status.Conditions) > 0
-			}, 10*time.Second, 1*time.Second).Should(BeTrue(), "ExternalSecret should have conditions")
-
-			// Then verify it stays in error state
-			Consistently(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-denied", Namespace: testNamespaceDenied.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-				// Check for error condition
-				for _, condition := range es.Status.Conditions {
-					if condition.Type == "Ready" {
-						// Should be False (error state)
-						return condition.Status == corev1.ConditionFalse
-					}
-				}
-				return false
-			}, 5*time.Second, 1*time.Second).Should(BeTrue(), "ExternalSecret should have error condition")
-		})
-	})
-})
-

+ 0 - 274
e2e/suites/v2/fake_test.go

@@ -1,274 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-	v1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("[v2] Fake Provider", Label("v2", "fake"), func() {
-	f := framework.New("v2-fake-provider")
-
-	Context("GetSecret", func() {
-		var testNamespace *corev1.Namespace
-
-		BeforeEach(func() {
-			testNamespace = SetupTestNamespace(f, "v2-fake-")
-
-			// Create Fake provider with test data
-			CreateFakeProvider(f, testNamespace.Name, "fake-provider", []v1.FakeProviderData{
-				{Key: "username", Value: "test-user"},
-				{Key: "password", Value: "test-password"},
-			})
-
-			// Create ProviderConnection
-			CreateFakeProviderConnection(f, testNamespace.Name, "test-secretstore", "fake-provider", testNamespace.Name)
-		})
-
-		AfterEach(func() {
-			if testNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-			}
-		})
-
-		It("should sync secrets from Fake provider", func() {
-			By("waiting for ProviderConnection to be ready")
-			WaitForProviderConnectionReady(f, testNamespace.Name, "test-secretstore", 30*time.Second)
-
-			By("creating an ExternalSecret")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es",
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "test-secretstore",
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "synced-secret",
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "username",
-							},
-						},
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "password",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
-
-			By("waiting for secret to be synced")
-			var syncedSecret corev1.Secret
-			Eventually(func() bool {
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "synced-secret", Namespace: testNamespace.Name},
-					&syncedSecret)
-				return err == nil
-			}, 30*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("verifying the synced secret data")
-			Expect(syncedSecret.Data["username"]).To(Equal([]byte("test-user")))
-			Expect(syncedSecret.Data["password"]).To(Equal([]byte("test-password")))
-		})
-	})
-
-	Context("Capabilities", func() {
-		var testNamespace *corev1.Namespace
-
-		BeforeEach(func() {
-			testNamespace = SetupTestNamespace(f, "v2-fake-capabilities-")
-
-			CreateFakeProvider(f, testNamespace.Name, "fake-provider", []v1.FakeProviderData{
-				{Key: "test-key", Value: "test-value"},
-			})
-
-			CreateFakeProviderConnection(f, testNamespace.Name, "test-secretstore", "fake-provider", testNamespace.Name)
-		})
-
-		AfterEach(func() {
-			if testNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-			}
-		})
-
-		It("should report READ_WRITE capabilities", func() {
-			By("waiting for ProviderConnection to be ready")
-			WaitForProviderConnectionReady(f, testNamespace.Name, "test-secretstore", 30*time.Second)
-
-			By("verifying capabilities")
-			VerifyProviderConnectionCapabilities(f, testNamespace.Name, "test-secretstore", esv1.ProviderReadWrite)
-		})
-	})
-
-	Context("Generator Support", func() {
-		var testNamespace *corev1.Namespace
-
-		BeforeEach(func() {
-			testNamespace = SetupTestNamespace(f, "v2-fake-generator-")
-			CreateFakeProvider(f, testNamespace.Name, "fake-provider", []v1.FakeProviderData{})
-		})
-
-		AfterEach(func() {
-			if testNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-			}
-		})
-
-		It("should generate secrets from Fake generator", func() {
-			By("creating a Fake generator")
-			CreateFakeGenerator(f, testNamespace.Name, "test-generator", map[string]string{
-				"username": "generated-user",
-				"password": "generated-password",
-				"token":    "generated-token",
-			})
-
-			By("creating a ProviderConnection to the fake provider for generator support")
-			CreateFakeProviderConnection(f, testNamespace.Name, "fake-generator-connection", "fake-provider", testNamespace.Name)
-
-			By("waiting for ProviderConnection to be ready")
-			WaitForProviderConnectionReady(f, testNamespace.Name, "fake-generator-connection", 30*time.Second)
-
-			By("creating an ExternalSecret with dataFrom referencing the generator")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-generator",
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "fake-generator-connection",
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "generated-secret",
-					},
-					DataFrom: []esv1.ExternalSecretDataFromRemoteRef{
-						{
-							SourceRef: &esv1.StoreGeneratorSourceRef{
-								GeneratorRef: &esv1.GeneratorRef{
-									APIVersion: "generators.external-secrets.io/v1alpha1",
-									Kind:       "Fake",
-									Name:       "test-generator",
-								},
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
-
-			By("waiting for secret to be synced")
-			var syncedSecret corev1.Secret
-			Eventually(func() bool {
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "generated-secret", Namespace: testNamespace.Name},
-					&syncedSecret)
-				return err == nil
-			}, 30*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("verifying the generated secret data")
-			Expect(syncedSecret.Data["username"]).To(Equal([]byte("generated-user")))
-			Expect(syncedSecret.Data["password"]).To(Equal([]byte("generated-password")))
-			Expect(syncedSecret.Data["token"]).To(Equal([]byte("generated-token")))
-		})
-
-		It("should generate secrets with rewrite rules", func() {
-			By("creating a Fake generator")
-			CreateFakeGenerator(f, testNamespace.Name, "test-generator-rewrite", map[string]string{
-				"db-host": "localhost",
-				"db-port": "5432",
-				"db-name": "mydb",
-			})
-
-			By("creating a ProviderConnection to the fake provider for generator support")
-			CreateFakeProviderConnection(f, testNamespace.Name, "fake-generator-connection-rewrite", "fake-provider", testNamespace.Name)
-
-			By("waiting for ProviderConnection to be ready")
-			WaitForProviderConnectionReady(f, testNamespace.Name, "fake-generator-connection-rewrite", 30*time.Second)
-
-			By("creating an ExternalSecret with rewrite rules")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-generator-rewrite",
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: "fake-generator-connection-rewrite",
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "generated-secret-rewrite",
-					},
-					DataFrom: []esv1.ExternalSecretDataFromRemoteRef{
-						{
-							SourceRef: &esv1.StoreGeneratorSourceRef{
-								GeneratorRef: &esv1.GeneratorRef{
-									APIVersion: "generators.external-secrets.io/v1alpha1",
-									Kind:       "Fake",
-									Name:       "test-generator-rewrite",
-								},
-							},
-							Rewrite: []esv1.ExternalSecretRewrite{
-								{
-									Regexp: &esv1.ExternalSecretRewriteRegexp{
-										Source: "db-(.*)",
-										Target: "database_$1",
-									},
-								},
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), es)).To(Succeed())
-
-			By("waiting for secret to be synced")
-			var syncedSecret corev1.Secret
-			Eventually(func() bool {
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "generated-secret-rewrite", Namespace: testNamespace.Name},
-					&syncedSecret)
-				return err == nil
-			}, 30*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("verifying the rewritten secret data")
-			Expect(syncedSecret.Data["database_host"]).To(Equal([]byte("localhost")))
-			Expect(syncedSecret.Data["database_port"]).To(Equal([]byte("5432")))
-			Expect(syncedSecret.Data["database_name"]).To(Equal([]byte("mydb")))
-		})
-	})
-})

+ 0 - 335
e2e/suites/v2/helpers.go

@@ -1,335 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"strings"
-	"time"
-
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	rbacv1 "k8s.io/api/rbac/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	"github.com/external-secrets/external-secrets-e2e/framework/log"
-	v1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-	genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
-	fakev2alpha1 "github.com/external-secrets/external-secrets/apis/provider/fake/v2alpha1"
-	k8sv2alpha1 "github.com/external-secrets/external-secrets/apis/provider/kubernetes/v2alpha1"
-)
-
-// GetClusterCABundle retrieves the cluster CA certificate from the kube-root-ca.crt ConfigMap.
-// Returns empty []byte if not found (non-blocking).
-func GetClusterCABundle(f *framework.Framework) []byte {
-	var caBundle []byte
-	krc := &corev1.ConfigMap{}
-	err := f.CRClient.Get(context.Background(),
-		types.NamespacedName{Name: "kube-root-ca.crt", Namespace: "default"},
-		krc)
-	if err == nil {
-		caBundle = []byte(krc.Data["ca.crt"])
-	}
-	return caBundle
-}
-
-func CreateProviderSecretWriterRole(f *framework.Framework, namespace, remoteNamespace string) {
-	role := &rbacv1.Role{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "provider-secret-writer",
-			Namespace: remoteNamespace,
-		},
-		Rules: []rbacv1.PolicyRule{
-			{
-				APIGroups: []string{""},
-				Resources: []string{"secrets"},
-				Verbs:     []string{"get", "list", "watch", "create", "update", "patch", "delete"},
-			},
-			{
-				APIGroups: []string{"authorization.k8s.io"},
-				Resources: []string{"selfsubjectrulesreviews", "selfsubjectaccessreviews"},
-				Verbs:     []string{"create"},
-			},
-		},
-	}
-	// Try to create the role, ignore if it already exists
-	err := f.CRClient.Create(context.Background(), role)
-	if err != nil && !strings.Contains(err.Error(), "already exists") {
-		Expect(err).To(Succeed())
-	}
-
-	// Create a RoleBinding that grants the provider service account these permissions
-	roleBinding := &rbacv1.RoleBinding{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "provider-secret-writer-binding",
-			Namespace: remoteNamespace,
-		},
-		Subjects: []rbacv1.Subject{
-			{
-				Kind:      "ServiceAccount",
-				Name:      "default",
-				Namespace: namespace,
-			},
-		},
-		RoleRef: rbacv1.RoleRef{
-			APIGroup: "rbac.authorization.k8s.io",
-			Kind:     "Role",
-			Name:     "provider-secret-writer",
-		},
-	}
-	// Try to create the role binding, ignore if it already exists
-	err = f.CRClient.Create(context.Background(), roleBinding)
-	if err != nil && !strings.Contains(err.Error(), "already exists") {
-		Expect(err).To(Succeed())
-	}
-}
-
-// CreateKubernetes creates a Kubernetes provider CRD with standard configuration.
-// Uses default service account auth and returns the created provider object.
-func CreateKubernetes(f *framework.Framework, namespace, name, remoteNamespace string, caBundle []byte) *k8sv2alpha1.Kubernetes {
-	k8ss := &k8sv2alpha1.Kubernetes{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Kubernetes",
-			APIVersion: "provider.external-secrets.io/v2alpha1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-		Spec: v1.KubernetesProvider{
-			Server: v1.KubernetesServer{
-				URL:      "https://kubernetes.default.svc",
-				CABundle: caBundle,
-			},
-			RemoteNamespace: remoteNamespace,
-			Auth: &v1.KubernetesAuth{
-				ServiceAccount: &esmeta.ServiceAccountSelector{
-					Name:      "default",
-					Namespace: &namespace,
-				},
-			},
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), k8ss)).To(Succeed())
-	log.Logf("created Kubernetes provider: %s/%s", namespace, name)
-	return k8ss
-}
-
-// CreateProvider creates a ProviderConnection pointing to the specified provider.
-// Uses standard provider-kubernetes service address and returns the created ProviderConnection object.
-func CreateProvider(f *framework.Framework, namespace, name, providerName, providerNamespace string) *v1.Provider {
-	providerConnection := &v1.Provider{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-		Spec: v1.ProviderSpec{
-			Config: v1.ProviderConfig{
-				Address: "provider-kubernetes.external-secrets-system.svc:8080",
-				ProviderRef: v1.ProviderReference{
-					APIVersion: "provider.external-secrets.io/v2alpha1",
-					Kind:       "Kubernetes",
-					Name:       providerName,
-					Namespace:  providerNamespace,
-				},
-			},
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), providerConnection)).To(Succeed())
-	log.Logf("created ProviderConnection: %s/%s", namespace, name)
-	return providerConnection
-}
-
-// WaitForProviderConnectionReady polls until the ProviderConnection has Ready=True condition.
-// Returns the ready ProviderConnection object.
-func WaitForProviderConnectionReady(f *framework.Framework, namespace, name string, timeout time.Duration) *v1.Provider {
-	var providerConnection v1.Provider
-	Eventually(func() bool {
-		err := f.CRClient.Get(context.Background(),
-			types.NamespacedName{Name: name, Namespace: namespace},
-			&providerConnection)
-		if err != nil {
-			log.Logf("failed to get ProviderConnection: %v", err)
-			return false
-		}
-
-		for _, condition := range providerConnection.Status.Conditions {
-			if condition.Type == "Ready" && condition.Status == metav1.ConditionTrue {
-				return true
-			}
-		}
-		return false
-	}, timeout, 1*time.Second).Should(BeTrue(), "ProviderConnection should become ready")
-
-	log.Logf("ProviderConnection %s/%s is ready", namespace, name)
-	return &providerConnection
-}
-
-// VerifyProviderConnectionCapabilities gets the ProviderConnection and checks its capabilities field.
-// Asserts capabilities match the expected value and logs the result.
-func VerifyProviderConnectionCapabilities(f *framework.Framework, namespace, name string, expected v1.ProviderCapabilities) {
-	var pc v1.Provider
-	Expect(f.CRClient.Get(context.Background(),
-		types.NamespacedName{Name: name, Namespace: namespace},
-		&pc)).To(Succeed())
-
-	Expect(pc.Status.Capabilities).NotTo(BeEmpty(), "Capabilities should be set")
-	Expect(string(pc.Status.Capabilities)).To(Equal(string(expected)), "Capabilities should match expected value")
-	log.Logf("successfully verified capabilities: %s", pc.Status.Capabilities)
-}
-
-// SetupTestNamespace creates a namespace with the given generateName prefix.
-// Logs creation and returns the namespace object.
-func SetupTestNamespace(f *framework.Framework, generateName string) *corev1.Namespace {
-	testNamespace := &corev1.Namespace{
-		ObjectMeta: metav1.ObjectMeta{
-			GenerateName: generateName,
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), testNamespace)).To(Succeed())
-	log.Logf("created test namespace: %s", testNamespace.Name)
-	return testNamespace
-}
-
-// CreateFakeProvider creates a Fake provider CRD with specified data.
-// Returns the created provider object.
-func CreateFakeProvider(f *framework.Framework, namespace, name string, data []v1.FakeProviderData) *fakev2alpha1.Fake {
-	fakeProvider := &fakev2alpha1.Fake{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Fake",
-			APIVersion: "provider.external-secrets.io/v2alpha1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-		Spec: v1.FakeProvider{
-			Data: data,
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), fakeProvider)).To(Succeed())
-	log.Logf("created Fake provider: %s/%s", namespace, name)
-	return fakeProvider
-}
-
-// CreateFakeProviderConnection creates a ProviderConnection pointing to the Fake provider.
-// Uses standard provider-fake service address and returns the created ProviderConnection object.
-func CreateFakeProviderConnection(f *framework.Framework, namespace, name, providerName, providerNamespace string) *v1.Provider {
-	providerConnection := &v1.Provider{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-		Spec: v1.ProviderSpec{
-			Config: v1.ProviderConfig{
-				Address: "provider-fake.external-secrets-system.svc:8080",
-				ProviderRef: v1.ProviderReference{
-					APIVersion: "provider.external-secrets.io/v2alpha1",
-					Kind:       "Fake",
-					Name:       providerName,
-					Namespace:  providerNamespace,
-				},
-			},
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), providerConnection)).To(Succeed())
-	log.Logf("created Fake ProviderConnection: %s/%s", namespace, name)
-	return providerConnection
-}
-
-// CreateFakeGenerator creates a Fake generator CR with specified data.
-// Returns the created generator object.
-func CreateFakeGenerator(f *framework.Framework, namespace, name string, data map[string]string) *genv1alpha1.Fake {
-	fakeGenerator := &genv1alpha1.Fake{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       string(genv1alpha1.GeneratorKindFake),
-			APIVersion: genv1alpha1.SchemeGroupVersion.String(),
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-		Spec: genv1alpha1.FakeSpec{
-			Data: data,
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), fakeGenerator)).To(Succeed())
-	log.Logf("created Fake generator: %s/%s", namespace, name)
-	return fakeGenerator
-}
-
-// CreateClusterProvider creates a ClusterProvider pointing to the specified provider.
-// Returns the created ClusterProvider object.
-func CreateClusterProvider(f *framework.Framework, name, address, providerAPIVersion, providerKind, providerName, providerNamespace string, authScope v1.AuthenticationScope, conditions []v1.ClusterSecretStoreCondition) *v1.ClusterProvider {
-	existing := &v1.ClusterProvider{}
-	err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: name}, existing)
-	if err == nil {
-		Expect(f.CRClient.Delete(context.Background(), existing)).To(Succeed())
-		Eventually(func() bool {
-			lookupErr := f.CRClient.Get(context.Background(), types.NamespacedName{Name: name}, &v1.ClusterProvider{})
-			return lookupErr != nil && strings.Contains(lookupErr.Error(), "not found")
-		}, 15*time.Second, 500*time.Millisecond).Should(BeTrue(), "existing ClusterProvider should be deleted before recreation")
-	}
-
-	clusterProvider := &v1.ClusterProvider{
-		ObjectMeta: metav1.ObjectMeta{
-			Name: name,
-		},
-		Spec: v1.ClusterProviderSpec{
-			Config: v1.ProviderConfig{
-				Address: address,
-				ProviderRef: v1.ProviderReference{
-					APIVersion: providerAPIVersion,
-					Kind:       providerKind,
-					Name:       providerName,
-					Namespace:  providerNamespace,
-				},
-			},
-			AuthenticationScope: authScope,
-			Conditions:          conditions,
-		},
-	}
-	Expect(f.CRClient.Create(context.Background(), clusterProvider)).To(Succeed())
-	log.Logf("created ClusterProvider: %s", name)
-	return clusterProvider
-}
-
-// WaitForClusterProviderReady polls until the ClusterProvider has Ready=True condition.
-// Returns the ready ClusterProvider object.
-func WaitForClusterProviderReady(f *framework.Framework, name string, timeout time.Duration) *v1.ClusterProvider {
-	var clusterProvider v1.ClusterProvider
-	Eventually(func() bool {
-		err := f.CRClient.Get(context.Background(),
-			types.NamespacedName{Name: name},
-			&clusterProvider)
-		if err != nil {
-			log.Logf("failed to get ClusterProvider: %v", err)
-			return false
-		}
-
-		for _, condition := range clusterProvider.Status.Conditions {
-			if condition.Type == "Ready" && condition.Status == metav1.ConditionTrue {
-				return true
-			}
-		}
-		return false
-	}, timeout, 1*time.Second).Should(BeTrue(), "ClusterProvider should become ready")
-
-	log.Logf("ClusterProvider %s is ready", name)
-	return &clusterProvider
-}

+ 0 - 55
e2e/suites/v2/kubernetes_capabilities_test.go

@@ -1,55 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("[v2] Capabilities", Label("v2", "capabilities"), func() {
-	f := framework.New("v2-capabilities")
-
-	var (
-		testNamespace *corev1.Namespace
-	)
-
-	BeforeEach(func() {
-		testNamespace = SetupTestNamespace(f, "v2-capabilities-")
-		CreateProviderSecretWriterRole(f, testNamespace.Name, testNamespace.Name)
-	})
-
-	AfterEach(func() {
-		// Cleanup namespace
-		if testNamespace != nil {
-			Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-		}
-	})
-
-	It("should report READ_WRITE capabilities for Kubernetes provider", func() {
-		caBundle := GetClusterCABundle(f)
-		CreateKubernetes(f, testNamespace.Name, "k8s-provider", testNamespace.Name, caBundle)
-		CreateProvider(f, testNamespace.Name, "test-secretstore", "k8s-provider", testNamespace.Name)
-		WaitForProviderConnectionReady(f, testNamespace.Name, "test-secretstore", 5*time.Second)
-		VerifyProviderConnectionCapabilities(f, testNamespace.Name, "test-secretstore", esv1.ProviderReadWrite)
-	})
-})

+ 0 - 390
e2e/suites/v2/kubernetes_cluster_provider_test.go

@@ -1,390 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("V2 ClusterProvider Tests", Label("v2", "cluster-provider", "e2e"), func() {
-	f := framework.New("v2-cluster-provider")
-
-	Describe("Kubernetes ClusterProvider", func() {
-		const (
-			sourceSecretName = "source-secret-cluster"
-			targetSecretName = "target-secret-cluster"
-		)
-
-		var (
-			sourceNamespace *corev1.Namespace
-			targetNamespaceA *corev1.Namespace
-			targetNamespaceB *corev1.Namespace
-		)
-
-		BeforeEach(func() {
-			sourceNamespace = SetupTestNamespace(f, "v2-cluster-source-")
-			targetNamespaceA = SetupTestNamespace(f, "v2-cluster-target-a-")
-			targetNamespaceB = SetupTestNamespace(f, "v2-cluster-target-b-")
-
-			// Create RBAC roles for provider access
-			// For ClusterProvider with ProviderNamespace scope, the service account
-			// in sourceNamespace needs to access secrets in sourceNamespace
-			CreateProviderSecretWriterRole(f, sourceNamespace.Name, sourceNamespace.Name)
-
-			// Create source secret
-			sourceSecret := &corev1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      sourceSecretName,
-					Namespace: sourceNamespace.Name,
-				},
-				Type: corev1.SecretTypeOpaque,
-				Data: map[string][]byte{
-					"username": []byte("cluster-admin"),
-					"password": []byte("cluster-secret-password"),
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), sourceSecret)).To(Succeed())
-		})
-
-		AfterEach(func() {
-			if sourceNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), sourceNamespace)).To(Succeed())
-			}
-			if targetNamespaceA != nil {
-				Expect(f.CRClient.Delete(context.Background(), targetNamespaceA)).To(Succeed())
-			}
-			if targetNamespaceB != nil {
-				Expect(f.CRClient.Delete(context.Background(), targetNamespaceB)).To(Succeed())
-			}
-		})
-
-		It("should sync secrets with ProviderNamespace authentication scope", func() {
-			caBundle := GetClusterCABundle(f)
-			k8sStore := CreateKubernetes(f, sourceNamespace.Name, "k8s-store-cluster", sourceNamespace.Name, caBundle)
-
-			By("creating a ClusterProvider with ProviderNamespace authentication scope")
-			CreateClusterProvider(f, "cluster-k8s-provider",
-				"provider-kubernetes.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Kubernetes",
-				k8sStore.Name,
-				sourceNamespace.Name,
-				esv1.AuthenticationScopeProviderNamespace,
-				nil)
-
-			WaitForClusterProviderReady(f, "cluster-k8s-provider", 10*time.Second)
-
-			By("creating an ExternalSecret in target namespace A")
-			externalSecretA := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-cluster-a",
-					Namespace: targetNamespaceA.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Kind: "ClusterProvider",
-						Name: "cluster-k8s-provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name:           targetSecretName,
-						CreationPolicy: esv1.CreatePolicyOwner,
-					},
-					RefreshInterval: &metav1.Duration{Duration: 1 * time.Hour},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      sourceSecretName,
-								Property: "username",
-							},
-						},
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      sourceSecretName,
-								Property: "password",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), externalSecretA)).To(Succeed())
-
-			By("waiting for ExternalSecret A to sync")
-			Eventually(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-cluster-a", Namespace: targetNamespaceA.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-
-				for _, condition := range es.Status.Conditions {
-					if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
-						return true
-					}
-				}
-				return false
-			}, 15*time.Second, 2*time.Second).Should(BeTrue(), "ExternalSecret should become ready")
-
-			By("verifying the synced secret in namespace A")
-			var targetSecret corev1.Secret
-			Expect(f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: targetSecretName, Namespace: targetNamespaceA.Name},
-				&targetSecret)).To(Succeed())
-
-			Expect(targetSecret.Data).To(HaveKeyWithValue("username", []byte("cluster-admin")))
-			Expect(targetSecret.Data).To(HaveKeyWithValue("password", []byte("cluster-secret-password")))
-		})
-
-		It("should sync secrets with ManifestNamespace authentication scope", func() {
-			caBundle := GetClusterCABundle(f)
-
-			// For ManifestNamespace scope, each namespace authenticates as itself
-			// Create RBAC for target namespace B
-			CreateProviderSecretWriterRole(f, targetNamespaceB.Name, targetNamespaceB.Name)
-
-			// Create a Kubernetes provider in any namespace (we'll use target A) with remoteNamespace set to B
-			CreateKubernetes(f, targetNamespaceA.Name, "k8s-store", targetNamespaceB.Name, caBundle)
-
-			// Create secret in namespace B (where we'll read from)
-			secretB := &corev1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "secret-b",
-					Namespace: targetNamespaceB.Name,
-				},
-				Type: corev1.SecretTypeOpaque,
-				Data: map[string][]byte{
-					"data": []byte("from-namespace-b"),
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), secretB)).To(Succeed())
-
-			By("creating a ClusterProvider with ManifestNamespace authentication scope")
-			// Point to Kubernetes provider in namespace A, but use ManifestNamespace auth
-			// This means auth will use namespace B's service account, which has RBAC in namespace B
-			CreateClusterProvider(f, "cluster-k8s-manifest-scope",
-				"provider-kubernetes.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Kubernetes",
-				"k8s-store",
-				targetNamespaceA.Name,
-				esv1.AuthenticationScopeManifestNamespace,
-				nil)
-
-			WaitForClusterProviderReady(f, "cluster-k8s-manifest-scope", 10*time.Second)
-
-			By("creating ExternalSecret in namespace B")
-			// Should authenticate using namespace B's credentials and access secrets in namespace B
-			externalSecretB := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-manifest-scope",
-					Namespace: targetNamespaceB.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Kind: "ClusterProvider",
-						Name: "cluster-k8s-manifest-scope",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name:           "synced-secret-b",
-						CreationPolicy: esv1.CreatePolicyOwner,
-					},
-					RefreshInterval: &metav1.Duration{Duration: 1 * time.Hour},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "data",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      "secret-b",
-								Property: "data",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), externalSecretB)).To(Succeed())
-
-			By("waiting for ExternalSecret B to sync")
-			Eventually(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-manifest-scope", Namespace: targetNamespaceB.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-
-				for _, condition := range es.Status.Conditions {
-					if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
-						return true
-					}
-				}
-				return false
-			}, 15*time.Second, 2*time.Second).Should(BeTrue(), "ExternalSecret should become ready")
-
-			By("verifying the synced secret has data from namespace B")
-			var syncedSecret corev1.Secret
-			Expect(f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "synced-secret-b", Namespace: targetNamespaceB.Name},
-				&syncedSecret)).To(Succeed())
-
-			Expect(syncedSecret.Data).To(HaveKeyWithValue("data", []byte("from-namespace-b")))
-		})
-
-		It("should enforce namespace conditions", func() {
-			caBundle := GetClusterCABundle(f)
-			k8sStore := CreateKubernetes(f, sourceNamespace.Name, "k8s-store-conditions", sourceNamespace.Name, caBundle)
-
-			// Add label to namespace A
-			targetNamespaceA.Labels = map[string]string{"env": "prod"}
-			Expect(f.CRClient.Update(context.Background(), targetNamespaceA)).To(Succeed())
-
-			By("creating a ClusterProvider with namespace selector")
-			conditions := []esv1.ClusterSecretStoreCondition{
-				{
-					NamespaceSelector: &metav1.LabelSelector{
-						MatchLabels: map[string]string{"env": "prod"},
-					},
-				},
-			}
-			CreateClusterProvider(f, "cluster-k8s-conditions",
-				"provider-kubernetes.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1",
-				"Kubernetes",
-				k8sStore.Name,
-				sourceNamespace.Name,
-				esv1.AuthenticationScopeProviderNamespace,
-				conditions)
-
-			WaitForClusterProviderReady(f, "cluster-k8s-conditions", 10*time.Second)
-
-			By("creating ExternalSecret in matching namespace A")
-			esA := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-allowed",
-					Namespace: targetNamespaceA.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Kind: "ClusterProvider",
-						Name: "cluster-k8s-conditions",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name:           "allowed-secret",
-						CreationPolicy: esv1.CreatePolicyOwner,
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      sourceSecretName,
-								Property: "username",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), esA)).To(Succeed())
-
-			By("verifying ExternalSecret in namespace A succeeds")
-			Eventually(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-allowed", Namespace: targetNamespaceA.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-				for _, condition := range es.Status.Conditions {
-					if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
-						return true
-					}
-				}
-				return false
-			}, 15*time.Second, 2*time.Second).Should(BeTrue())
-
-			By("creating ExternalSecret in non-matching namespace B")
-			esB := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-es-denied",
-					Namespace: targetNamespaceB.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Kind: "ClusterProvider",
-						Name: "cluster-k8s-conditions",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: "denied-secret",
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      sourceSecretName,
-								Property: "username",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), esB)).To(Succeed())
-
-			By("verifying ExternalSecret in namespace B fails with condition error")
-			// First wait for the ExternalSecret to be reconciled and have a condition
-			Eventually(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-denied", Namespace: targetNamespaceB.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-				return len(es.Status.Conditions) > 0
-			}, 10*time.Second, 1*time.Second).Should(BeTrue(), "ExternalSecret should have conditions")
-
-			// Then verify it stays in error state
-			Consistently(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-es-denied", Namespace: targetNamespaceB.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-				// Check if it has an error condition
-				for _, condition := range es.Status.Conditions {
-					if condition.Type == "Ready" {
-						// Should be False (error state)
-						return condition.Status == corev1.ConditionFalse
-					}
-				}
-				return false
-			}, 5*time.Second, 1*time.Second).Should(BeTrue(), "ExternalSecret should have error condition")
-		})
-	})
-})
-

+ 0 - 70
e2e/suites/v2/kubernetes_delete_test.go

@@ -1,70 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("[v2] DeleteSecret", Label("v2", "kubernetes", "delete-secret"), func() {
-	f := framework.New("eso-v2-delete-secret")
-
-	var (
-		testNamespace *corev1.Namespace
-	)
-
-	BeforeEach(func() {
-		testNamespace = SetupTestNamespace(f, "v2-delete-secret-")
-		CreateProviderSecretWriterRole(f, testNamespace.Name, testNamespace.Name)
-	})
-
-	AfterEach(func() {
-		// Cleanup namespace
-		if testNamespace != nil {
-			Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-		}
-	})
-
-	It("should delete secret from Kubernetes provider", func() {
-		caBundle := GetClusterCABundle(f)
-		CreateKubernetes(f, testNamespace.Name, "k8s-provider", testNamespace.Name, caBundle)
-		CreateProvider(f, testNamespace.Name, "test-secretstore", "k8s-provider", testNamespace.Name)
-		WaitForProviderConnectionReady(f, testNamespace.Name, "test-secretstore", 5*time.Second)
-
-		By("creating test secret")
-		testSecret := &corev1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "test-secret",
-				Namespace: testNamespace.Name,
-			},
-			Data: map[string][]byte{
-				"key1": []byte("value1"),
-				"key2": []byte("value2"),
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), testSecret)).To(Succeed())
-
-		VerifyProviderConnectionCapabilities(f, testNamespace.Name, "test-secretstore", esv1.ProviderReadWrite)
-	})
-})

+ 0 - 240
e2e/suites/v2/kubernetes_find_test.go

@@ -1,240 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("[v2] GetAllSecrets", Label("v2", "get-all-secrets"), func() {
-	f := framework.New("v2-get-all-secrets")
-
-	var (
-		sourceNamespace *corev1.Namespace
-		targetNamespace *corev1.Namespace
-	)
-
-	BeforeEach(func() {
-		sourceNamespace = SetupTestNamespace(f, "v2-get-all-source-")
-		targetNamespace = SetupTestNamespace(f, "v2-get-all-target-")
-		CreateProviderSecretWriterRole(f, targetNamespace.Name, sourceNamespace.Name)
-
-		// Create test secrets with different labels
-		secrets := []struct {
-			name   string
-			labels map[string]string
-			data   map[string][]byte
-		}{
-			{
-				name:   "app-secret-1",
-				labels: map[string]string{"app": "myapp", "env": "prod"},
-				data:   map[string][]byte{"key1": []byte("value1")},
-			},
-			{
-				name:   "app-secret-2",
-				labels: map[string]string{"app": "myapp", "env": "dev"},
-				data:   map[string][]byte{"key2": []byte("value2")},
-			},
-			{
-				name:   "db-secret-1",
-				labels: map[string]string{"app": "database", "env": "prod"},
-				data:   map[string][]byte{"password": []byte("dbpass")},
-			},
-			{
-				name:   "other-secret",
-				labels: map[string]string{"type": "config"},
-				data:   map[string][]byte{"config": []byte("data")},
-			},
-		}
-
-		for _, s := range secrets {
-			secret := &corev1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      s.name,
-					Namespace: sourceNamespace.Name,
-					Labels:    s.labels,
-				},
-				Type: corev1.SecretTypeOpaque,
-				Data: s.data,
-			}
-			Expect(f.CRClient.Create(context.Background(), secret)).To(Succeed())
-		}
-
-	})
-
-	AfterEach(func() {
-		// Cleanup namespaces
-		if sourceNamespace != nil {
-			Expect(f.CRClient.Delete(context.Background(), sourceNamespace)).To(Succeed())
-		}
-		if targetNamespace != nil {
-			Expect(f.CRClient.Delete(context.Background(), targetNamespace)).To(Succeed())
-		}
-	})
-
-	It("should find secrets by tags (labels)", func() {
-		caBundle := GetClusterCABundle(f)
-		CreateKubernetes(f, targetNamespace.Name, "k8s-provider", sourceNamespace.Name, caBundle)
-		CreateProvider(f, targetNamespace.Name, "test-secretstore", "k8s-provider", targetNamespace.Name)
-		WaitForProviderConnectionReady(f, targetNamespace.Name, "test-secretstore", 5*time.Second)
-
-		By("creating an ExternalSecret with dataFrom using tags")
-		externalSecret := &esv1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "test-external-secret-tags",
-				Namespace: targetNamespace.Name,
-			},
-			Spec: esv1.ExternalSecretSpec{
-				SecretStoreRef: esv1.SecretStoreRef{
-					Kind: "Provider",
-					Name: "test-secretstore",
-				},
-				Target: esv1.ExternalSecretTarget{
-					Name:           "synced-secret-tags",
-					CreationPolicy: esv1.CreatePolicyOwner,
-				},
-				RefreshInterval: &metav1.Duration{Duration: 1 * time.Hour},
-				DataFrom: []esv1.ExternalSecretDataFromRemoteRef{
-					{
-						Find: &esv1.ExternalSecretFind{
-							Tags: map[string]string{
-								"app": "myapp",
-							},
-						},
-					},
-				},
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), externalSecret)).To(Succeed())
-
-		By("waiting for ExternalSecret to sync")
-		Eventually(func() bool {
-			var es esv1.ExternalSecret
-			err := f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "test-external-secret-tags", Namespace: targetNamespace.Name},
-				&es)
-			if err != nil {
-				return false
-			}
-
-			for _, condition := range es.Status.Conditions {
-				if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
-					return true
-				}
-			}
-			return false
-		}, 10*time.Second, 2*time.Second).Should(BeTrue(), "ExternalSecret should become ready")
-
-		By("verifying the synced secret contains data from secrets with matching tags")
-		var syncedSecret corev1.Secret
-		Expect(f.CRClient.Get(context.Background(),
-			types.NamespacedName{Name: "synced-secret-tags", Namespace: targetNamespace.Name},
-			&syncedSecret)).To(Succeed())
-
-		// GetAllSecrets returns secret name -> JSON data
-		// Should contain keys for app-secret-1 and app-secret-2 (both have app=myapp)
-		Expect(syncedSecret.Data).To(HaveKey("app-secret-1"))
-		Expect(syncedSecret.Data).To(HaveKey("app-secret-2"))
-		// Should NOT contain data from db-secret-1 or other-secret
-		Expect(syncedSecret.Data).NotTo(HaveKey("db-secret-1"))
-		Expect(syncedSecret.Data).NotTo(HaveKey("other-secret"))
-
-		// Verify the values are JSON-encoded secret data
-		Expect(string(syncedSecret.Data["app-secret-1"])).To(ContainSubstring("key1"))
-		Expect(string(syncedSecret.Data["app-secret-2"])).To(ContainSubstring("key2"))
-	})
-
-	It("should find secrets by name regexp", func() {
-		caBundle := GetClusterCABundle(f)
-		CreateKubernetes(f, targetNamespace.Name, "k8s-provider-regex", sourceNamespace.Name, caBundle)
-		CreateProvider(f, targetNamespace.Name, "test-secretstore-regex", "k8s-provider-regex", targetNamespace.Name)
-		WaitForProviderConnectionReady(f, targetNamespace.Name, "test-secretstore-regex", 5*time.Second)
-
-		By("creating an ExternalSecret with dataFrom using name regexp")
-		externalSecret := &esv1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "test-external-secret-regex",
-				Namespace: targetNamespace.Name,
-			},
-			Spec: esv1.ExternalSecretSpec{
-				SecretStoreRef: esv1.SecretStoreRef{
-					Kind: "Provider",
-					Name: "test-secretstore-regex",
-				},
-				Target: esv1.ExternalSecretTarget{
-					Name:           "synced-secret-regex",
-					CreationPolicy: esv1.CreatePolicyOwner,
-				},
-				RefreshInterval: &metav1.Duration{Duration: 1 * time.Hour},
-				DataFrom: []esv1.ExternalSecretDataFromRemoteRef{
-					{
-						Find: &esv1.ExternalSecretFind{
-							Name: &esv1.FindName{
-								RegExp: "^app-secret-.*",
-							},
-						},
-					},
-				},
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), externalSecret)).To(Succeed())
-
-		By("waiting for ExternalSecret to sync")
-		Eventually(func() bool {
-			var es esv1.ExternalSecret
-			err := f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "test-external-secret-regex", Namespace: targetNamespace.Name},
-				&es)
-			if err != nil {
-				return false
-			}
-
-			for _, condition := range es.Status.Conditions {
-				if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
-					return true
-				}
-			}
-			return false
-		}, 10*time.Second, 2*time.Second).Should(BeTrue(), "ExternalSecret should become ready")
-
-		By("verifying the synced secret contains data from secrets matching the regexp")
-		var syncedSecret corev1.Secret
-		Expect(f.CRClient.Get(context.Background(),
-			types.NamespacedName{Name: "synced-secret-regex", Namespace: targetNamespace.Name},
-			&syncedSecret)).To(Succeed())
-
-		// GetAllSecrets returns secret name -> JSON data
-		// Should contain keys for app-secret-1 and app-secret-2 (match ^app-secret-.*)
-		Expect(syncedSecret.Data).To(HaveKey("app-secret-1"))
-		Expect(syncedSecret.Data).To(HaveKey("app-secret-2"))
-		// Should NOT contain data from db-secret-1 or other-secret
-		Expect(syncedSecret.Data).NotTo(HaveKey("db-secret-1"))
-		Expect(syncedSecret.Data).NotTo(HaveKey("other-secret"))
-
-		// Verify the values are JSON-encoded secret data
-		Expect(string(syncedSecret.Data["app-secret-1"])).To(ContainSubstring("key1"))
-		Expect(string(syncedSecret.Data["app-secret-2"])).To(ContainSubstring("key2"))
-	})
-})

+ 0 - 158
e2e/suites/v2/kubernetes_get_test.go

@@ -1,158 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("V2 End-to-End Tests", Label("v2", "e2e"), func() {
-	f := framework.New("v2-e2e")
-
-	Describe("Kubernetes Provider", func() {
-		const (
-			sourceSecretName = "source-secret"
-			targetSecretName = "target-secret"
-			secretStoreName  = "kubernetes-secretstore"
-		)
-
-		var (
-			sourceNamespace *corev1.Namespace
-			targetNamespace *corev1.Namespace
-		)
-
-		BeforeEach(func() {
-			sourceNamespace = SetupTestNamespace(f, "v2-source-")
-			targetNamespace = SetupTestNamespace(f, "v2-target-")
-			CreateProviderSecretWriterRole(f, targetNamespace.Name, sourceNamespace.Name)
-
-			// Create source secret
-			sourceSecret := &corev1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      sourceSecretName,
-					Namespace: sourceNamespace.Name,
-				},
-				Type: corev1.SecretTypeOpaque,
-				Data: map[string][]byte{
-					"username": []byte("admin"),
-					"password": []byte("super-secret-password"),
-					"api-key":  []byte("abc123xyz789"),
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), sourceSecret)).To(Succeed())
-		})
-
-		AfterEach(func() {
-			// Cleanup namespaces
-			if sourceNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), sourceNamespace)).To(Succeed())
-			}
-			if targetNamespace != nil {
-				Expect(f.CRClient.Delete(context.Background(), targetNamespace)).To(Succeed())
-			}
-		})
-
-		It("should sync secrets across namespaces", func() {
-			caBundle := GetClusterCABundle(f)
-			CreateKubernetes(f, targetNamespace.Name, "k8s-store", sourceNamespace.Name, caBundle)
-			CreateProvider(f, targetNamespace.Name, secretStoreName, "k8s-store", targetNamespace.Name)
-			WaitForProviderConnectionReady(f, targetNamespace.Name, secretStoreName, 5*time.Second)
-
-			By("creating an ExternalSecret")
-			externalSecret := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "test-external-secret",
-					Namespace: targetNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					SecretStoreRef: esv1.SecretStoreRef{
-						Kind: "Provider",
-						Name: secretStoreName,
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name:           targetSecretName,
-						CreationPolicy: esv1.CreatePolicyOwner,
-					},
-					RefreshInterval: &metav1.Duration{Duration: 1 * time.Hour},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      sourceSecretName,
-								Property: "username",
-							},
-						},
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key:      sourceSecretName,
-								Property: "password",
-							},
-						},
-					},
-				},
-			}
-			Expect(f.CRClient.Create(context.Background(), externalSecret)).To(Succeed())
-
-			By("waiting for ExternalSecret to sync")
-			Eventually(func() bool {
-				var es esv1.ExternalSecret
-				err := f.CRClient.Get(context.Background(),
-					types.NamespacedName{Name: "test-external-secret", Namespace: targetNamespace.Name},
-					&es)
-				if err != nil {
-					return false
-				}
-
-				for _, condition := range es.Status.Conditions {
-					if condition.Type == "Ready" && condition.Status == corev1.ConditionTrue {
-						return true
-					}
-				}
-				return false
-			}, 10*time.Second, 2*time.Second).Should(BeTrue(), "ExternalSecret should become ready")
-
-			By("verifying the synced secret")
-			var targetSecret corev1.Secret
-			Expect(f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: targetSecretName, Namespace: targetNamespace.Name},
-				&targetSecret)).To(Succeed())
-
-			Expect(targetSecret.Data).To(HaveKeyWithValue("username", []byte("admin")))
-			Expect(targetSecret.Data).To(HaveKeyWithValue("password", []byte("super-secret-password")))
-
-			By("verifying ExternalSecret status")
-			var es esv1.ExternalSecret
-			Expect(f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "test-external-secret", Namespace: targetNamespace.Name},
-				&es)).To(Succeed())
-
-			Expect(es.Status.SyncedResourceVersion).NotTo(BeEmpty())
-			Expect(es.Status.RefreshTime).NotTo(BeNil())
-			Expect(es.Status.Conditions).NotTo(BeEmpty())
-		})
-
-	})
-})

+ 0 - 253
e2e/suites/v2/kubernetes_push_test.go

@@ -1,253 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	"github.com/external-secrets/external-secrets-e2e/framework/log"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
-)
-
-var _ = Describe("[v2] PushSecret", Label("v2", "kubernetes", "push-secret"), func() {
-	f := framework.New("eso-v2-push-secret")
-
-	var (
-		testNamespace *corev1.Namespace
-	)
-
-	BeforeEach(func() {
-		testNamespace = SetupTestNamespace(f, "v2-push-secret-")
-		CreateProviderSecretWriterRole(f, testNamespace.Name, testNamespace.Name)
-	})
-
-	AfterEach(func() {
-		// Cleanup namespace
-		if testNamespace != nil {
-			Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-		}
-	})
-
-	It("should push secret to Kubernetes provider", func() {
-		caBundle := GetClusterCABundle(f)
-		CreateKubernetes(f, testNamespace.Name, "k8s-provider", testNamespace.Name, caBundle)
-		CreateProvider(f, testNamespace.Name, "test-secretstore", "k8s-provider", testNamespace.Name)
-		WaitForProviderConnectionReady(f, testNamespace.Name, "test-secretstore", 5*time.Second)
-
-		By("creating source secret")
-		sourceSecret := &corev1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "source-secret",
-				Namespace: testNamespace.Name,
-			},
-			Data: map[string][]byte{
-				"username": []byte("admin"),
-				"password": []byte("secret123"),
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), sourceSecret)).To(Succeed())
-		log.Logf("created source secret: %s/%s", testNamespace.Name, "source-secret")
-
-		VerifyProviderConnectionCapabilities(f, testNamespace.Name, "test-secretstore", esv1.ProviderReadWrite)
-
-		By("creating PushSecret")
-		pushSecret := &esv1alpha1.PushSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "test-pushsecret",
-				Namespace: testNamespace.Name,
-			},
-			Spec: esv1alpha1.PushSecretSpec{
-				RefreshInterval: &metav1.Duration{Duration: 10 * time.Second},
-				SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
-					{
-						Name:       "test-secretstore",
-						Kind:       "Provider",
-						APIVersion: "external-secrets.io/v1",
-					},
-				},
-				Selector: esv1alpha1.PushSecretSelector{
-					Secret: &esv1alpha1.PushSecretSecret{
-						Name: "source-secret",
-					},
-				},
-				Data: []esv1alpha1.PushSecretData{
-					{
-						Match: esv1alpha1.PushSecretMatch{
-							SecretKey: "username",
-							RemoteRef: esv1alpha1.PushSecretRemoteRef{
-								RemoteKey: "pushed-secret",
-								Property:  "username",
-							},
-						},
-					},
-					{
-						Match: esv1alpha1.PushSecretMatch{
-							SecretKey: "password",
-							RemoteRef: esv1alpha1.PushSecretRemoteRef{
-								RemoteKey: "pushed-secret",
-								Property:  "password",
-							},
-						},
-					},
-				},
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), pushSecret)).To(Succeed())
-		log.Logf("created PushSecret: %s/%s", testNamespace.Name, "test-pushsecret")
-
-		By("verifying PushSecret is synced")
-		Eventually(func() bool {
-			var ps esv1alpha1.PushSecret
-			err := f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "test-pushsecret", Namespace: testNamespace.Name},
-				&ps)
-			if err != nil {
-				log.Logf("failed to get PushSecret: %v", err)
-				return false
-			}
-
-			for _, condition := range ps.Status.Conditions {
-				if condition.Type == esv1alpha1.PushSecretReady && condition.Status == corev1.ConditionTrue {
-					log.Logf("PushSecret is ready with status: %s", condition.Reason)
-					return true
-				}
-			}
-			log.Logf("PushSecret not ready yet, conditions: %+v", ps.Status.Conditions)
-			return false
-		}, 10*time.Second, 2*time.Second).Should(BeTrue(), "PushSecret should become ready")
-
-		By("verifying pushed secret exists in target namespace")
-		var pushedSecret corev1.Secret
-		Eventually(func() bool {
-			err := f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "pushed-secret", Namespace: testNamespace.Name},
-				&pushedSecret)
-			if err != nil {
-				log.Logf("pushed secret not found yet: %v", err)
-				return false
-			}
-			return true
-		}, 10*time.Second, 2*time.Second).Should(BeTrue(), "pushed secret should exist")
-
-		By("verifying pushed secret has correct data")
-		Expect(pushedSecret.Data).To(HaveKey("username"))
-		Expect(pushedSecret.Data).To(HaveKey("password"))
-		Expect(string(pushedSecret.Data["username"])).To(Equal("admin"))
-		Expect(string(pushedSecret.Data["password"])).To(Equal("secret123"))
-		log.Logf("successfully verified pushed secret data")
-	})
-
-	It("should delete secrets when DeletionPolicy=Delete", func() {
-		caBundle := GetClusterCABundle(f)
-		CreateKubernetes(f, testNamespace.Name, "k8s-provider", testNamespace.Name, caBundle)
-		CreateProvider(f, testNamespace.Name, "test-secretstore", "k8s-provider", testNamespace.Name)
-		WaitForProviderConnectionReady(f, testNamespace.Name, "test-secretstore", 5*time.Second)
-
-		By("creating source secret")
-		sourceSecret := &corev1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "source-secret-delete",
-				Namespace: testNamespace.Name,
-			},
-			Data: map[string][]byte{
-				"key1": []byte("value1"),
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), sourceSecret)).To(Succeed())
-
-		By("creating PushSecret with DeletionPolicy=Delete")
-		pushSecret := &esv1alpha1.PushSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "test-pushsecret-delete",
-				Namespace: testNamespace.Name,
-			},
-			Spec: esv1alpha1.PushSecretSpec{
-				RefreshInterval: &metav1.Duration{Duration: 10 * time.Second},
-				DeletionPolicy:  esv1alpha1.PushSecretDeletionPolicyDelete,
-				SecretStoreRefs: []esv1alpha1.PushSecretStoreRef{
-					{
-						Name:       "test-secretstore",
-						Kind:       "Provider",
-						APIVersion: "external-secrets.io/v1",
-					},
-				},
-				Selector: esv1alpha1.PushSecretSelector{
-					Secret: &esv1alpha1.PushSecretSecret{
-						Name: "source-secret-delete",
-					},
-				},
-				Data: []esv1alpha1.PushSecretData{
-					{
-						Match: esv1alpha1.PushSecretMatch{
-							SecretKey: "key1",
-							RemoteRef: esv1alpha1.PushSecretRemoteRef{
-								RemoteKey: "pushed-secret-delete",
-								Property:  "key1",
-							},
-						},
-					},
-				},
-			},
-		}
-		Expect(f.CRClient.Create(context.Background(), pushSecret)).To(Succeed())
-		log.Logf("created PushSecret with Delete policy")
-
-		By("waiting for PushSecret to sync")
-		Eventually(func() bool {
-			var ps esv1alpha1.PushSecret
-			err := f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "test-pushsecret-delete", Namespace: testNamespace.Name},
-				&ps)
-			if err != nil {
-				return false
-			}
-			for _, condition := range ps.Status.Conditions {
-				if condition.Type == esv1alpha1.PushSecretReady && condition.Status == corev1.ConditionTrue {
-					return true
-				}
-			}
-			return false
-		}, 30*time.Second, 2*time.Second).Should(BeTrue())
-
-		By("verifying pushed secret was created")
-		var pushedSecret corev1.Secret
-		Expect(f.CRClient.Get(context.Background(),
-			types.NamespacedName{Name: "pushed-secret-delete", Namespace: testNamespace.Name},
-			&pushedSecret)).To(Succeed())
-
-		By("deleting PushSecret")
-		Expect(f.CRClient.Delete(context.Background(), pushSecret)).To(Succeed())
-
-		By("verifying pushed secret is deleted due to DeletionPolicy=Delete")
-		Eventually(func() bool {
-			err := f.CRClient.Get(context.Background(),
-				types.NamespacedName{Name: "pushed-secret-delete", Namespace: testNamespace.Name},
-				&pushedSecret)
-			return apierrors.IsNotFound(err)
-		}, 30*time.Second, 2*time.Second).Should(BeTrue(), "pushed secret should be deleted when PushSecret is deleted")
-
-		log.Logf("successfully verified Delete policy removes secrets")
-	})
-})

+ 0 - 500
e2e/suites/v2/metrics_test.go

@@ -1,500 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
-	"context"
-	"time"
-
-	. "github.com/onsi/ginkgo/v2"
-	. "github.com/onsi/gomega"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/client-go/kubernetes"
-
-	"github.com/external-secrets/external-secrets-e2e/framework"
-	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
-)
-
-var _ = Describe("V2 Provider Metrics", Label("v2", "metrics"), func() {
-	f := framework.New("v2-metrics")
-
-	var (
-		testNamespace  *corev1.Namespace
-		providerCRName string
-		providerName   string
-		secretName     string
-		externalSecret string
-		fakeData       []esv1.FakeProviderData
-	)
-
-	BeforeEach(func() {
-		testNamespace = SetupTestNamespace(f, "v2-metrics-")
-		providerCRName = "fake-provider-cr"
-		providerName = "fake-provider-conn"
-		secretName = "test-secret-metrics"
-		externalSecret = "test-es-metrics"
-
-		// Create fake provider configuration
-		fakeData = []esv1.FakeProviderData{
-			{
-				Key:   "password",
-				Value: "supersecret123",
-			},
-			{
-				Key:   "username",
-				Value: "admin",
-			},
-		}
-	})
-
-	AfterEach(func() {
-		if testNamespace != nil {
-			Expect(f.CRClient.Delete(context.Background(), testNamespace)).To(Succeed())
-		}
-	})
-
-	Describe("Controller Metrics", func() {
-		It("should expose Provider controller metrics", func() {
-			By("Creating a Fake provider CRD")
-			CreateFakeProvider(f, testNamespace.Name, providerCRName, fakeData)
-
-			By("Creating a Provider connection")
-			CreateFakeProviderConnection(f, testNamespace.Name, providerName, providerCRName, testNamespace.Name)
-
-			By("Waiting for Provider to be ready")
-			WaitForProviderConnectionReady(f, testNamespace.Name, providerName, 60*time.Second)
-
-			By("Scraping controller metrics")
-			metrics, err := scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying provider_status_condition metric exists")
-			ExpectMetricExists(metrics, "provider_status_condition")
-
-			By("Verifying provider_status_condition shows Ready=True")
-			ExpectMetricValue(metrics, "provider_status_condition", map[string]string{
-				"name":      providerName,
-				"namespace": testNamespace.Name,
-				"condition": "Ready",
-				"status":    "True",
-			}, 1.0)
-
-			By("Verifying provider_reconcile_duration exists and is > 0")
-			ExpectMetricGreaterThan(metrics, "provider_reconcile_duration", map[string]string{
-				"name":      providerName,
-				"namespace": testNamespace.Name,
-			}, 0.0)
-		})
-
-		It("should expose ClusterProvider controller metrics", func() {
-			clusterProviderName := "fake-cluster-provider-metrics"
-
-			By("Creating a Fake provider CRD")
-			CreateFakeProvider(f, testNamespace.Name, providerCRName, fakeData)
-
-			By("Creating a ClusterProvider resource")
-			CreateClusterProvider(f, clusterProviderName, "provider-fake.external-secrets-system.svc:8080",
-				"provider.external-secrets.io/v2alpha1", "Fake", providerCRName, testNamespace.Name,
-				esv1.AuthenticationScopeProviderNamespace, nil)
-
-			By("Waiting for ClusterProvider to be ready")
-			WaitForClusterProviderReady(f, clusterProviderName, 60*time.Second)
-
-			By("Scraping controller metrics")
-			metrics, err := scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying clusterprovider_status_condition metric exists")
-			ExpectMetricExists(metrics, "clusterprovider_status_condition")
-
-			By("Verifying clusterprovider_status_condition shows Ready=True")
-			ExpectMetricValue(metrics, "clusterprovider_status_condition", map[string]string{
-				"name":      clusterProviderName,
-				"condition": "Ready",
-				"status":    "True",
-			}, 1.0)
-
-			By("Verifying clusterprovider_reconcile_duration exists and is > 0")
-			ExpectMetricGreaterThan(metrics, "clusterprovider_reconcile_duration", map[string]string{
-				"name": clusterProviderName,
-			}, 0.0)
-		})
-
-		It("should track clientmanager cache hits and misses", func() {
-			By("Creating a Fake provider CRD")
-			CreateFakeProvider(f, testNamespace.Name, providerCRName, fakeData)
-
-			By("Creating a Provider connection")
-			CreateFakeProviderConnection(f, testNamespace.Name, providerName, providerCRName, testNamespace.Name)
-			WaitForProviderConnectionReady(f, testNamespace.Name, providerName, 60*time.Second)
-
-			By("Creating an ExternalSecret with multiple data entries to trigger cache hits")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      externalSecret,
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					RefreshInterval: &metav1.Duration{Duration: 60 * time.Second},
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: providerName,
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: secretName,
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "password",
-							},
-						},
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "username",
-							},
-						},
-					},
-				},
-			}
-			err := f.CRClient.Create(context.Background(), es)
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Waiting for secret to be created")
-			Eventually(func() bool {
-				secret := &corev1.Secret{}
-				err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: testNamespace.Name}, secret)
-				return err == nil && len(secret.Data) >= 2
-			}, 60*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("Waiting a moment for metrics to be recorded")
-			time.Sleep(2 * time.Second)
-
-			By("Scraping controller metrics")
-			metrics, err := scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying clientmanager_cache_hits_total metric exists")
-			ExpectMetricExists(metrics, "clientmanager_cache_hits_total")
-
-			By("Verifying cache hits occurred within the reconcile")
-			// With 2 data entries, the second Get() call should hit the cache
-			value, found := getMetricValue(metrics, "clientmanager_cache_hits_total", map[string]string{
-				"provider_type": "provider",
-			})
-			Expect(found).To(BeTrue())
-			Expect(value).To(BeNumerically(">=", 1.0), "should have at least one cache hit from multiple data entries")
-		})
-	})
-
-	Describe("Provider Pod Metrics", func() {
-		BeforeEach(func() {
-			By("Creating a Fake provider CRD")
-			CreateFakeProvider(f, testNamespace.Name, providerCRName, fakeData)
-
-			By("Creating a Provider connection")
-			CreateFakeProviderConnection(f, testNamespace.Name, providerName, providerCRName, testNamespace.Name)
-			WaitForProviderConnectionReady(f, testNamespace.Name, providerName, 60*time.Second)
-		})
-
-		It("should expose connection pool metrics", func() {
-			By("Creating an ExternalSecret")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      externalSecret,
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					RefreshInterval: &metav1.Duration{Duration: 10 * time.Second},
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: providerName,
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: secretName,
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "password",
-							},
-						},
-					},
-				},
-			}
-			err := f.CRClient.Create(context.Background(), es)
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Waiting for secret to be created")
-			Eventually(func() bool {
-				secret := &corev1.Secret{}
-				err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: testNamespace.Name}, secret)
-				return err == nil && len(secret.Data) > 0
-			}, 60*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("Scraping controller metrics for pool stats")
-			metrics, err := scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying grpc_pool_misses_total exists (first connection)")
-			ExpectMetricExists(metrics, "grpc_pool_misses_total")
-
-			By("Verifying at least one cache miss occurred")
-			ExpectMetricGreaterThan(metrics, "grpc_pool_misses_total", map[string]string{}, 0.0)
-
-			By("Verifying grpc_pool_connections_total exists")
-			ExpectMetricExists(metrics, "grpc_pool_connections_total")
-
-			By("Triggering another reconcile to test cache hit")
-			// Update the ExternalSecret to trigger reconciliation
-			err = f.CRClient.Get(context.Background(), types.NamespacedName{Name: externalSecret, Namespace: testNamespace.Name}, es)
-			Expect(err).ToNot(HaveOccurred())
-			es.Annotations = map[string]string{"test": "trigger-reconcile"}
-			err = f.CRClient.Update(context.Background(), es)
-			Expect(err).ToNot(HaveOccurred())
-
-			time.Sleep(5 * time.Second)
-
-			By("Scraping controller metrics again for pool hits")
-			metrics, err = scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying grpc_pool_hits_total incremented")
-			ExpectMetricGreaterThan(metrics, "grpc_pool_hits_total", map[string]string{}, 0.0)
-		})
-
-		It("should expose gRPC client metrics", func() {
-			By("Creating an ExternalSecret")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      externalSecret,
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					RefreshInterval: &metav1.Duration{Duration: 60 * time.Second},
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: providerName,
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: secretName,
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "password",
-							},
-						},
-					},
-				},
-			}
-			err := f.CRClient.Create(context.Background(), es)
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Waiting for secret to be created")
-			Eventually(func() bool {
-				secret := &corev1.Secret{}
-				err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: testNamespace.Name}, secret)
-				return err == nil && len(secret.Data) > 0
-			}, 60*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("Scraping controller metrics for client stats")
-			metrics, err := scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying grpc_client_requests_total exists")
-			ExpectMetricExists(metrics, "grpc_client_requests_total")
-
-			By("Verifying GetSecret requests were made successfully")
-			value, found := getMetricValue(metrics, "grpc_client_requests_total", map[string]string{
-				"method": "GetSecret",
-				"status": "success",
-			})
-			Expect(found).To(BeTrue())
-			Expect(value).To(BeNumerically(">=", 1.0), "should have at least one successful GetSecret call")
-
-			By("Verifying grpc_client_request_duration_seconds exists")
-			ExpectMetricExists(metrics, "grpc_client_request_duration_seconds_count")
-
-			By("Verifying request duration was recorded")
-			value, found = getMetricValue(metrics, "grpc_client_request_duration_seconds_count", map[string]string{
-				"method": "GetSecret",
-				"status": "success",
-			})
-			Expect(found).To(BeTrue())
-			Expect(value).To(BeNumerically(">=", 1.0))
-		})
-
-		It("should expose gRPC server metrics", func() {
-			By("Creating an ExternalSecret")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      externalSecret,
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					RefreshInterval: &metav1.Duration{Duration: 60 * time.Second},
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: providerName,
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: secretName,
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "password",
-							},
-						},
-					},
-				},
-			}
-			err := f.CRClient.Create(context.Background(), es)
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Waiting for secret to be created")
-			Eventually(func() bool {
-				secret := &corev1.Secret{}
-				err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: testNamespace.Name}, secret)
-				return err == nil && len(secret.Data) > 0
-			}, 60*time.Second, 1*time.Second).Should(BeTrue())
-
-			By("Scraping provider pod metrics")
-			metrics, err := scrapeProviderMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system", "fake")
-			Expect(err).ToNot(HaveOccurred())
-
-			By("Verifying grpc_server_requests_total exists")
-			ExpectMetricExists(metrics, "grpc_server_requests_total")
-
-			By("Verifying server handled GetSecret requests")
-			value, found := getMetricValue(metrics, "grpc_server_requests_total", map[string]string{
-				"method": "/provider.v1.SecretStoreProvider/GetSecret",
-				"status": "success",
-			})
-			Expect(found).To(BeTrue())
-			Expect(value).To(BeNumerically(">=", 1.0))
-
-			By("Verifying grpc_server_request_duration_seconds exists")
-			ExpectMetricExists(metrics, "grpc_server_request_duration_seconds_count")
-
-			By("Verifying server request duration was recorded")
-			value, found = getMetricValue(metrics, "grpc_server_request_duration_seconds_count", map[string]string{
-				"method": "/provider.v1.SecretStoreProvider/GetSecret",
-			})
-			Expect(found).To(BeTrue())
-			Expect(value).To(BeNumerically(">=", 1.0))
-		})
-	})
-
-	Describe("End-to-End Metrics Workflow", func() {
-		It("should track metrics through full Provider lifecycle", func() {
-			By("1. Creating Provider and verifying controller metrics")
-			CreateFakeProvider(f, testNamespace.Name, providerCRName, fakeData)
-			CreateFakeProviderConnection(f, testNamespace.Name, providerName, providerCRName, testNamespace.Name)
-			WaitForProviderConnectionReady(f, testNamespace.Name, providerName, 60*time.Second)
-
-			controllerMetrics, err := scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-			ExpectMetricValue(controllerMetrics, "provider_status_condition", map[string]string{
-				"name":      providerName,
-				"namespace": testNamespace.Name,
-				"condition": "Ready",
-				"status":    "True",
-			}, 1.0)
-
-			By("2. Creating ExternalSecret with multiple data entries and verifying metrics")
-			es := &esv1.ExternalSecret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      externalSecret,
-					Namespace: testNamespace.Name,
-				},
-				Spec: esv1.ExternalSecretSpec{
-					RefreshInterval: &metav1.Duration{Duration: 10 * time.Second},
-					SecretStoreRef: esv1.SecretStoreRef{
-						Name: providerName,
-						Kind: "Provider",
-					},
-					Target: esv1.ExternalSecretTarget{
-						Name: secretName,
-					},
-					Data: []esv1.ExternalSecretData{
-						{
-							SecretKey: "password",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "password",
-							},
-						},
-						{
-							SecretKey: "username",
-							RemoteRef: esv1.ExternalSecretDataRemoteRef{
-								Key: "username",
-							},
-						},
-					},
-				},
-			}
-			err = f.CRClient.Create(context.Background(), es)
-			Expect(err).ToNot(HaveOccurred())
-
-			Eventually(func() bool {
-				secret := &corev1.Secret{}
-				err := f.CRClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: testNamespace.Name}, secret)
-				return err == nil && len(secret.Data) >= 2
-			}, 60*time.Second, 1*time.Second).Should(BeTrue())
-
-			// Pool and client metrics are on controller, server metrics on provider pod
-			controllerMetrics, err = scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-			ExpectMetricGreaterThan(controllerMetrics, "grpc_pool_misses_total", map[string]string{}, 0.0)
-			ExpectMetricGreaterThan(controllerMetrics, "grpc_client_requests_total", map[string]string{
-				"method": "GetSecret",
-				"status": "success",
-			}, 0.0)
-			
-			providerMetrics, err := scrapeProviderMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system", "fake")
-			Expect(err).ToNot(HaveOccurred())
-			ExpectMetricGreaterThan(providerMetrics, "grpc_server_requests_total", map[string]string{
-				"method": "/provider.v1.SecretStoreProvider/GetSecret",
-				"status": "success",
-			}, 0.0)
-
-			By("3. Waiting for refresh and verifying pool hits")
-			time.Sleep(15 * time.Second)
-
-			controllerMetrics, err = scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-			ExpectMetricGreaterThan(controllerMetrics, "grpc_pool_hits_total", map[string]string{}, 0.0)
-
-			// Clientmanager cache hits occur within a single reconcile when multiple data entries exist
-			controllerMetrics, err = scrapeControllerMetrics(context.Background(), f.KubeConfig, f.KubeClientSet.(*kubernetes.Clientset), "external-secrets-system")
-			Expect(err).ToNot(HaveOccurred())
-			ExpectMetricGreaterThan(controllerMetrics, "clientmanager_cache_hits_total", map[string]string{
-				"provider_type": "provider",
-			}, 0.0)
-
-			By("4. Workflow completed successfully with all metrics tracked")
-		})
-	})
-})
-