Skip to content

fix(gcpsm): Improve SecretExists method in GCP secret manager provider#5610

Merged
Skarlso merged 5 commits intoexternal-secrets:mainfrom
tosih:improve-gcpsm-provider-secret-exists
Dec 19, 2025
Merged

fix(gcpsm): Improve SecretExists method in GCP secret manager provider#5610
Skarlso merged 5 commits intoexternal-secrets:mainfrom
tosih:improve-gcpsm-provider-secret-exists

Conversation

@tosih
Copy link
Copy Markdown
Contributor

@tosih tosih commented Nov 18, 2025

Problem Statement

This issue is related to a recent change that addressed this issue: #5584. The PR: #5593 does address the issue but does not cover the case where the latest secret version returned by GCP secret manager could be disabled.

Related Issue

Relates to: #5584
Addresses: #5609

Proposed Changes

SecretExists should check for latest enabled secret version. It does not need to call GetSecret, as looking for just the version should suffice. Instead we can use the already available private function getLatestEnabledVersion().

Checklist

  • I have read the contribution guidelines
  • All commits are signed with git commit --signoff
  • My changes have reasonable test coverage
  • All tests pass with make test
  • I ensured my PR is ready for review with make reviewable

Summary by CodeRabbit

  • New Features

    • Regional/location-aware secret handling with helpers for constructing secret and parent paths.
    • Test helper to simulate secret-version listing and iteration.
  • Bug Fixes

    • Secret existence now requires an accessible enabled version; NotFound and API errors propagate correctly.
    • Added observability around secret access calls.
  • Tests

    • Expanded regional tests, context-aware patterns, and per-test mock injection for version flows.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added area/provider kind/bug Categorizes issue or PR as related to a bug. size/m labels Nov 18, 2025
@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from f1a86e4 to d8d189e Compare November 18, 2025 19:54
@tosih tosih changed the title fix(provider): improve SecretExists method in GCP secret manager provider fix(gcpsm): improve SecretExists method in GCP secret manager provider Nov 18, 2025
@tosih tosih changed the title fix(gcpsm): improve SecretExists method in GCP secret manager provider fix(gcpsm): Improve SecretExists method in GCP secret manager provider Nov 18, 2025
@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from 58f3a63 to 17ed6fb Compare November 18, 2025 22:22
@tosih tosih marked this pull request as ready for review November 18, 2025 22:50
@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Nov 18, 2025

/ok-to-test sha=17ed6fbb41c17747b64da40b8065d0aba565fc56

@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Nov 18, 2025

/ok-to-test-managed sha=17ed6fbb41c17747b64da40b8065d0aba565fc56 provider=gcp

@tosih tosih marked this pull request as draft November 18, 2025 23:04
@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from 1448d1e to a4e20c9 Compare November 21, 2025 23:46
@gusfcarvalho
Copy link
Copy Markdown
Member

/ok-to-test sha=17ed6fbb41c17747b64da40b8065d0aba565fc56

@eso-service-account-app
Copy link
Copy Markdown
Contributor

@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch 2 times, most recently from f7bdfd2 to ad4acc7 Compare November 26, 2025 22:36
// This is simplified to only support what our tests actually need:
// - Return 0 or 1 versions
// - Immediately return iterator.Done when exhausted
func NewSecretVersionIterator(versions []*secretmanagerpb.SecretVersion) *secretmanager.SecretVersionIterator {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disclaimer: I used Claude to help me generate this SecretVersionIterator mock

@tosih tosih marked this pull request as ready for review November 26, 2025 22:54
@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Nov 26, 2025

@gusfcarvalho could you fire another e2e? I ended up making a few extra changes.

I was trying to avoid the SecretVersionIterator mock that needed to use reflection, but it seemed to be the only way to properly unit test my usage of getLatestEnabledVersion

@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Dec 3, 2025

/ok-to-test sha=ad4acc7463f54be2a09a92cb79396617c01aa531

@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Dec 3, 2025

/ok-to-test-managed sha=ad4acc7463f54be2a09a92cb79396617c01aa531

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Dec 3, 2025

@tosih I'm afraid you aren't allowed to fire the e2e test. :) Only maintainers ccan.

Also, please take care of the sonar issues before proceeding. :) Thanks!

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Dec 3, 2025

/ok-to-test sha=ad4acc7463f54be2a09a92cb79396617c01aa531

@eso-service-account-app
Copy link
Copy Markdown
Contributor

@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from ad4acc7 to 3d40256 Compare December 16, 2025 17:16
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 16, 2025

Walkthrough

SecretExists now obtains the latest enabled secret version via ListSecretVersions and AccessSecretVersion (using new helpers getName/getParentName/getLatestEnabledVersion) instead of GetSecret. Tests were made location-aware and use per-test contexts. Fake client gained a mock SecretVersionIterator generator for tests.

Changes

Cohort / File(s) Summary
Core client refactor
providers/v1/gcp/secretmanager/client.go
SecretExists now delegates to getLatestEnabledVersion and uses getName/getParentName. Added helpers: getName, getParentName, isErrSecretDestroyedOrDisabled, getLatestEnabledVersion which lists versions, picks newest ENABLED by CreateTime, falls back to .../versions/latest, calls AccessSecretVersion, and observes API metrics. Minor stylistic change in PushSecret.
Tests — regional/location-aware
providers/v1/gcp/secretmanager/client_test.go
Tests switched test types to v1alpha1.PushSecretRemoteRef, added per-test Location scenarios, use t.Context() at call sites, and inject per-test mocks via tc.setupMock for ListSecretVersions and AccessSecretVersion to simulate regional resource names, NotFound, DESTROYED, and permission cases.
Fake/mocks — iterator support
providers/v1/gcp/secretmanager/fake/fake.go
Added ListSecretVersionsMockReturn type and NewSecretVersionIterator(versions []*secretmanagerpb.SecretVersion) *secretmanager.SecretVersionIterator to fabricate a mock SecretVersionIterator (uses reflect, unsafe, and iterator.PageInfo internals). Added MockSMClient.NewListSecretVersionsFn to return the iterator and updated imports.
Dependency
providers/v1/gcp/go.mod
Added direct require: google.golang.org/protobuf v1.36.10 (moved from indirect to direct).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Provider Client
    participant SM as Secret Manager API
    participant Metrics as metrics

    Client->>SM: ListSecretVersions(parent or secret name)
    SM-->>Client: SecretVersion iterator
    Client->>Client: iterate -> pick newest ENABLED by CreateTime
    alt enabled version found
        Client->>Metrics: ObserveAPICall(start)
        Client->>SM: AccessSecretVersion(version name)
        SM-->>Client: Secret payload / error
        Client->>Metrics: ObserveAPICall(end)
    else none enabled
        Client->>Metrics: ObserveAPICall(start)
        Client->>SM: AccessSecretVersion(.../versions/latest)
        SM-->>Client: Secret payload / error
        Client->>Metrics: ObserveAPICall(end)
    end
    Client-->>Caller: return exists? (true/false) or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Inspect reflect/unsafe usage and iterator/PageInfo manipulation in fake/fake.go.
  • Verify getLatestEnabledVersion iteration logic, CreateTime comparison, fallback to latest, and error propagation.
  • Confirm metrics placement around AccessSecretVersion.
  • Validate updated tests: per-test mock wiring, regional name handling, and type change to v1alpha1.PushSecretRemoteRef.

Poem

🐰 I hopped through versions, nose to every trail,

I found the freshest ENABLED tale,
I stitched mock pages with a gentle paw,
Tuned regions, contexts, and the test-sweet law,
A rabbit cheers when CI sings without fail.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: improving the SecretExists method in the GCP secret manager provider to handle disabled versions.
Description check ✅ Passed The PR description includes problem statement, related issues, proposed changes, and a complete checklist. All required template sections are present and substantive.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cc7fc6 and 6a40948.

📒 Files selected for processing (1)
  • providers/v1/gcp/go.mod (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: publish-artifacts (Dockerfile.ubi, CGO_ENABLED=0, amd64 arm64 ppc64le, linux/amd64,linux/arm64,li... / Build and Publish
  • GitHub Check: publish-artifacts (Dockerfile, CGO_ENABLED=0, amd64 arm64 s390x ppc64le, linux/amd64,linux/arm64,... / Build and Publish
  • GitHub Check: publish-artifacts (Dockerfile.ubi, CGO_ENABLED=0 GOEXPERIMENT=boringcrypto, amd64 ppc64le, linux/... / Build and Publish
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: check-diff
  • GitHub Check: Analyze project (go, autobuild)
🔇 Additional comments (1)
providers/v1/gcp/go.mod (1)

20-20: The promotion of google.golang.org/protobuf to a direct dependency is appropriate and verified.

The dependency promotion is justified by the updated implementation's direct use of protobuf APIs (e.g., SecretVersionIterator mocking in client.go). Version v1.36.10 is stable and secure with no known unfixed vulnerabilities, and no orphaned indirect entries remain in the dependency tree.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
providers/v1/gcp/secretmanager/client_test.go (1)

1189-1264: Test may not be validating the new SecretExists behavior.

TestSecretExistsWithRegionalEndpoint mocks GetSecretFn and AccessSecretVersionFn, but the refactored SecretExists method now uses getLatestEnabledVersion, which calls ListSecretVersions before AccessSecretVersion.

Since ListSecretVersionsFn is not mocked in these test cases, the test may either:

  1. Panic with a nil function call, or
  2. Pass accidentally if there's a default behavior

Consider updating this test to use the same setupMock pattern as TestSecretExists to properly mock ListSecretVersionsFn.

 func TestSecretExistsWithRegionalEndpoint(t *testing.T) {
 	tests := []struct {
 		name                          string
 		location                      string
 		ref                           esv1.PushSecretRemoteRef
-		getSecretMockReturn           fakesm.SecretMockReturn
-		accessSecretVersionMockReturn fakesm.AccessSecretVersionMockReturn
+		setupMock                     func(*fakesm.MockSMClient)
 		expectedSecret                bool
 		expectedErr                   func(t *testing.T, err error)
 	}{
🧹 Nitpick comments (1)
providers/v1/gcp/secretmanager/client.go (1)

710-730: Consider edge case when multiple versions have the same CreateTime.

The loop selects the version with the latest CreateTime using After(). If two versions have identical timestamps (unlikely but possible), the selection depends on iteration order, which may not be deterministic.

This is a minor edge case, but worth noting. If deterministic behavior is needed, consider a secondary sort key (e.g., version name/number).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4473156 and 3d40256.

📒 Files selected for processing (3)
  • providers/v1/gcp/secretmanager/client.go (3 hunks)
  • providers/v1/gcp/secretmanager/client_test.go (8 hunks)
  • providers/v1/gcp/secretmanager/fake/fake.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
providers/v1/gcp/secretmanager/client_test.go (1)
providers/v1/gcp/secretmanager/fake/fake.go (5)
  • AddSecretVersionMockReturn (60-63)
  • MockSMClient (34-47)
  • ListSecretVersionsMockReturn (65-67)
  • NewSecretVersionIterator (73-139)
  • AccessSecretVersionMockReturn (55-58)
🔇 Additional comments (7)
providers/v1/gcp/secretmanager/fake/fake.go (3)

69-139: Acknowledge the reflection-based mock approach.

This is a sophisticated mock implementation that uses reflection and unsafe to populate unexported fields of secretmanager.SecretVersionIterator. The approach is well-documented and appropriately scoped for test-only usage.

A few observations:

  1. The setField helper silently ignores invalid field names (when !field.IsValid()). Consider logging or panicking in tests if a field name is incorrect, as this could mask test setup bugs.

  2. The done variable is captured in the InternalFetch closure, meaning the iterator cannot be reused after exhaustion. This is fine for the current test patterns but worth noting.


65-67: LGTM - consistent mock pattern.

The ListSecretVersionsMockReturn struct correctly only contains Res since ListSecretVersions returns an iterator directly (errors are surfaced through iter.Next()), unlike other methods that return (result, error).


292-296: LGTM - standard mock setter.

The NewListSecretVersionsFn follows the established pattern used by other mock setters in this file.

providers/v1/gcp/secretmanager/client_test.go (2)

1145-1164: Test case behavior may not match the new implementation.

This test case "secret version does not exist - latest version is destroyed/disabled" sets up:

  • Empty iterator (no enabled versions)
  • AccessSecretVersion returning FailedPrecondition with "DESTROYED state"

The test expects expectedExists: false and an error containing "DESTROYED state".

However, looking at the implementation in client.go, getLatestEnabledVersion will:

  1. Find no enabled versions (empty iterator)
  2. Fall through with versionName = "latest"
  3. Call AccessSecretVersion with /versions/latest
  4. Return the FailedPrecondition error

Since SecretExists only treats codes.NotFound as "secret doesn't exist", this error will propagate as an error (not "secret exists = false"). The test expectation seems correct, but consider whether this behavior is intended—a secret with all versions destroyed/disabled returns an error rather than (false, nil).

Is this the intended behavior? If a secret exists but has no enabled versions (all destroyed/disabled), should SecretExists return:

  • (false, nil) - treating it as "doesn't exist for practical purposes", or
  • (false, err) - indicating the secret exists but is in an unusable state?

The current implementation returns (false, err), which may be confusing for callers.


1016-1021: LGTM - improved test structure.

The refactored TestSecretExists uses the setupMock pattern which provides better flexibility and clarity. The test cases cover important scenarios including regional secrets, not-found errors, and permission denied errors.

providers/v1/gcp/secretmanager/client.go (2)

149-160: LGTM - cleaner existence check using enabled versions.

The refactored SecretExists now correctly delegates to getLatestEnabledVersion, which checks for accessible (enabled) secret versions rather than just secret existence. This aligns with the PR objective of fixing the issue where the latest version could be disabled.

The NotFound error handling correctly returns (false, nil) indicating the secret doesn't exist from a practical standpoint.


192-196: LGTM - minor style change.

Changing var replication = to replication := is a stylistic improvement with identical semantics.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
providers/v1/gcp/secretmanager/fake/fake.go (1)

69-140: Consider adding defensive checks for field reflection failures.

The reflection-based approach is reasonable for test mocking but has maintenance implications:

  1. Silent failures: The setField helper (lines 77-85) silently does nothing if a field is not found. If the underlying library changes field names, tests may pass with invalid mocks, hiding failures.

  2. Brittleness: The mock assumes specific unexported field names (pageInfo, nextFunc, items) in secretmanager.SecretVersionIterator. Library updates could break this without warning.

  3. Simplified scope: The implementation only supports 0-1 versions (documented on lines 70-72), which may require updates if test coverage expands.

Consider these improvements:

 // Simple helper to set an unexported field using reflection
 setField := func(name string, value any) {
   v := reflect.ValueOf(it).Elem()
   field := v.FieldByName(name)
-  if field.IsValid() {
+  if !field.IsValid() {
+    // In tests, failing to set a field likely means the library structure changed
+    panic(fmt.Sprintf("mock iterator: field %q not found in SecretVersionIterator", name))
+  }
+  if field.CanAddr() {
     //#nosec G103 -- Audited this use and it's only for mocking in testing
     reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
       Elem().Set(reflect.ValueOf(value))
+  } else {
+    panic(fmt.Sprintf("mock iterator: field %q is not addressable", name))
   }
 }

This will fail tests immediately if the library structure changes, rather than silently producing incorrect behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d40256 and c66d59d.

📒 Files selected for processing (1)
  • providers/v1/gcp/secretmanager/fake/fake.go (5 hunks)
🔇 Additional comments (3)
providers/v1/gcp/secretmanager/fake/fake.go (3)

65-67: LGTM!

The ListSecretVersionsMockReturn type follows the established pattern of other mock return types in this file and provides a clean wrapper for the mock iterator.


293-297: LGTM!

The NewListSecretVersionsFn method follows the established pattern of other mock setup methods in this file and correctly wires the mock iterator.


23-24: The reflection-based mock approach relies on unstable internal iterator structure. Verify compatibility and add error handling.

The InternalFetch field in SecretVersionIterator is explicitly documented as not part of the stable interface of the package. Using reflection and unsafe pointers to set unexported fields in a struct with internal implementation details creates brittleness. If Google's implementation changes internal field names or structure in future library versions, this mock will silently fail.

Consider adding defensive error handling to the setField helper to detect and report failures, and add comments documenting the specific library version this mock targets to make future compatibility breaks more discoverable.

@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Dec 17, 2025

I added a fix that CodeRabbitAI was suggesting and it also matched another PR: #5678

@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from 9fa5abb to 98987a3 Compare December 17, 2025 22:05
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
providers/v1/gcp/secretmanager/client_test.go (1)

1028-1047: Consider setting CreateTime in mock versions to fully exercise the comparison logic.

The mocked SecretVersion objects don't set CreateTime. While this works (protobuf handles nil safely), it means the "latest by creation time" comparison logic in getLatestEnabledVersion isn't fully tested. Consider adding CreateTime values to properly validate the version selection:

 mc.NewListSecretVersionsFn(fakesm.ListSecretVersionsMockReturn{
     Res: fakesm.NewSecretVersionIterator([]*secretmanagerpb.SecretVersion{
         {
             Name:  "projects/foo/secrets/bar/versions/1",
             State: secretmanagerpb.SecretVersion_ENABLED,
+            CreateTime: timestamppb.Now(),
         },
     }),
 })
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fa5abb and 98987a3.

📒 Files selected for processing (3)
  • providers/v1/gcp/secretmanager/client.go (3 hunks)
  • providers/v1/gcp/secretmanager/client_test.go (20 hunks)
  • providers/v1/gcp/secretmanager/fake/fake.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • providers/v1/gcp/secretmanager/fake/fake.go
🧰 Additional context used
🧬 Code graph analysis (1)
providers/v1/gcp/secretmanager/client_test.go (4)
providers/v1/gcp/secretmanager/fake/fake.go (4)
  • AddSecretVersionMockReturn (60-63)
  • MockSMClient (34-47)
  • ListSecretVersionsMockReturn (65-67)
  • NewSecretVersionIterator (73-140)
apis/externalsecrets/v1alpha1/pushsecret_types.go (1)
  • PushSecret (261-267)
providers/v1/gcp/secretmanager/client.go (1)
  • Client (88-97)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
🔇 Additional comments (8)
providers/v1/gcp/secretmanager/client.go (3)

149-160: LGTM: SecretExists now correctly checks for accessible versions.

The refactored implementation properly determines secret existence by checking for an enabled/accessible version rather than just checking if the secret resource exists. This addresses the issue where a secret could exist but have only disabled/destroyed versions.


192-196: Minor style improvement noted.

The change from var replication = ... to replication := ... is a minor idiomatic improvement.


710-746: Path construction issue resolved correctly.

The previous critical issue about double-prefixed paths has been properly addressed. The implementation now:

  1. Initializes versionName as empty string
  2. Sets it to the full path from version.Name when enabled versions are found
  3. Only constructs the fallback path when no enabled versions exist (line 734-736)
  4. Uses versionName directly in the request

This ensures valid resource paths in all scenarios.

providers/v1/gcp/secretmanager/client_test.go (5)

82-82: Context usage is appropriate for test setup.

Using context.TODO() in the test setup helper is acceptable since the actual test methods use t.Context() which provides proper test-scoped context handling.


601-619: Minor style improvements noted.

The changes from var x = ... to x := ... are idiomatic Go style improvements with no functional impact.


1189-1280: Regional endpoint tests provide good coverage.

The TestSecretExistsWithRegionalEndpoint test cases properly exercise the regional secret behavior, including existence checks and NotFound scenarios with the updated mock infrastructure.


1145-1164: Good test case for destroyed/disabled state handling.

This test case properly validates the behavior when the latest version is destroyed/disabled - the function correctly returns false with an error containing the state information, ensuring callers can distinguish between "secret doesn't exist" and "secret exists but is inaccessible."


217-217: Correct the Go version reference in t.Context() usage explanation.

Using t.Context() (Go 1.24+) returns a context that's canceled after the test completes, improving test isolation.

Likely an incorrect or invalid review comment.

@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from 98987a3 to d464236 Compare December 17, 2025 22:12
Update SecretExists to call getLatestEnabledVersion instead of directly
checking for the latest version. This provides better handling of secret
versions in different states.

Changes:
- Refactor SecretExists to use getLatestEnabledVersion helper function
- Add comprehensive tests for SecretExists with various scenarios
- Add mock support for ListSecretVersions in fake client
- Improve error handling for destroyed/disabled secret versions
- Add support for regional secrets in SecretExists tests
- Fix linter issues in fake client

Signed-off-by: Sohail Ahmed <sohail@example.com>
Signed-off-by: tosih <somuchhail@gmail.com>
The version.Name field from GCP's SecretVersion returns the full resource
name (e.g., projects/*/secrets/*/versions/*), not just the version number.
Previously, the code was incorrectly appending this full path to another
path construction, resulting in malformed paths like:
projects/.../secrets/.../versions/projects/.../secrets/.../versions/1

This fix changes getLatestEnabledVersion to:
- Use version.Name directly when an enabled version is found
- Only construct the path when falling back to "latest"

This resolves issues when SecretVersionSelectionPolicy is set to
LatestOrFetch and the latest version is destroyed or disabled.

Signed-off-by: tosih <somuchhail@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
providers/v1/gcp/secretmanager/client.go (1)

716-730: Consider edge case: multiple versions with identical CreateTime.

The logic finds the version with the latest CreateTime, but if two versions have identical timestamps, the result depends on iteration order which may not be deterministic. While unlikely in practice, consider using version number as a tiebreaker for deterministic behavior.

providers/v1/gcp/secretmanager/fake/fake.go (1)

76-85: Consider adding error handling for invalid field names in setField.

The setField helper silently ignores invalid field names. While this works for the current use case, if the SecretVersionIterator struct changes in a future library update, the mock could silently break. Consider logging a warning or panicking in tests when a field isn't found.

 	setField := func(name string, value any) {
 		v := reflect.ValueOf(it).Elem()
 		field := v.FieldByName(name)
 		if field.IsValid() {
 			//#nosec G103 -- Audited this use and it's only for mocking in testing
 			reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
 				Elem().Set(reflect.ValueOf(value))
+		} else {
+			panic(fmt.Sprintf("SecretVersionIterator field %q not found - mock may be incompatible with library version", name))
 		}
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98987a3 and d464236.

📒 Files selected for processing (3)
  • providers/v1/gcp/secretmanager/client.go (3 hunks)
  • providers/v1/gcp/secretmanager/client_test.go (20 hunks)
  • providers/v1/gcp/secretmanager/fake/fake.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
providers/v1/gcp/secretmanager/client_test.go (1)
providers/v1/gcp/secretmanager/fake/fake.go (5)
  • AddSecretVersionMockReturn (60-63)
  • MockSMClient (34-47)
  • ListSecretVersionsMockReturn (65-67)
  • NewSecretVersionIterator (73-140)
  • AccessSecretVersionMockReturn (55-58)
🔇 Additional comments (8)
providers/v1/gcp/secretmanager/client.go (2)

149-160: LGTM! SecretExists now correctly uses getLatestEnabledVersion.

The refactored implementation properly determines secret existence by checking for accessible versions rather than just the secret metadata. This addresses the issue where a secret could exist but have all versions disabled/destroyed.


710-746: Previous path construction issue has been addressed.

The implementation now correctly handles the version name:

  • When an enabled version is found, versionName is set to version.Name (already a full resource path)
  • When no enabled versions exist, the fallback constructs the full path: fmt.Sprintf("%s/versions/latest", name)
  • Line 738 uses versionName directly without wrapping it in another fmt.Sprintf

This resolves the double-prefixed path issue raised in previous reviews.

providers/v1/gcp/secretmanager/fake/fake.go (2)

69-140: Reflection-based mock iterator is correctly implemented for testing purposes.

The use of reflect and unsafe to manipulate unexported fields is an accepted pattern when mocking Google Cloud iterators. The #nosec G103 annotation appropriately acknowledges the audited use of unsafe pointers.


293-297: LGTM! NewListSecretVersionsFn provides clean mock injection.

The method follows the established pattern used by other mock setters in this file (e.g., NewAccessSecretVersionFn, NewGetSecretFn).

providers/v1/gcp/secretmanager/client_test.go (4)

1014-1165: LGTM! Comprehensive test coverage for SecretExists.

The test cases properly cover:

  • Secret with accessible enabled versions (global and regional)
  • NotFound scenarios (secret doesn't exist)
  • PermissionDenied error propagation
  • DESTROYED/disabled state handling
  • Regional endpoint behavior

The use of setupMock function pattern improves test readability and maintainability.


1146-1164: Good test case for destroyed/disabled secret version edge case.

This test case correctly validates that when all versions are destroyed/disabled, the error is propagated rather than returning exists=true. This aligns with the PR objective to properly handle disabled secret versions.


1189-1268: LGTM! Regional endpoint tests are well structured.

Tests correctly validate the regional secret path format (projects/{project}/locations/{location}/secrets/{secret}/versions/{version}) and both exists/not-exists scenarios with regional endpoints.


82-82: Consistent use of context.TODO() in test setup helpers.

Using context.TODO() in makeValidSecretManagerTestCase and makeValidSecretManagerTestCaseCustom is appropriate since these helpers run before the test context is available.

Also applies to: 112-112

@tosih tosih force-pushed the improve-gcpsm-provider-secret-exists branch from d464236 to 2cc7fc6 Compare December 17, 2025 22:19
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
providers/v1/gcp/secretmanager/fake/fake.go (1)

80-88: Consider adding error handling for missing fields.

The setField helper silently ignores cases where the field is not valid. While this works for the current use case, consider logging or returning an error if a field is not found, to catch potential issues if the underlying library changes its field names.

 	setField := func(name string, value any) {
 		v := reflect.ValueOf(it).Elem()
 		field := v.FieldByName(name)
-		if field.IsValid() {
-			//#nosec G103 -- Audited this use and it's only for mocking in testing
-			reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
-				Elem().Set(reflect.ValueOf(value))
+		if !field.IsValid() {
+			panic(fmt.Sprintf("SecretVersionIterator field %q not found - library may have changed", name))
 		}
+		//#nosec G103 -- Audited this use and it's only for mocking in testing
+		reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
+			Elem().Set(reflect.ValueOf(value))
 	}
providers/v1/gcp/secretmanager/client.go (1)

192-196: Minor: Unused variable initialization when location is set.

When c.store.Location is set (regional secrets), the replication variable is initialized but never used since regional secrets don't set replication (as per the check on line 230). This is harmless but slightly wasteful.

+	var replication *secretmanagerpb.Replication
+	if c.store.Location == "" {
-	replication := &secretmanagerpb.Replication{
+		replication = &secretmanagerpb.Replication{
-		Replication: &secretmanagerpb.Replication_Automatic_{
-			Automatic: &secretmanagerpb.Replication_Automatic{},
-		},
+			Replication: &secretmanagerpb.Replication_Automatic_{
+				Automatic: &secretmanagerpb.Replication_Automatic{},
+			},
+		}
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d464236 and 2cc7fc6.

📒 Files selected for processing (3)
  • providers/v1/gcp/secretmanager/client.go (3 hunks)
  • providers/v1/gcp/secretmanager/client_test.go (22 hunks)
  • providers/v1/gcp/secretmanager/fake/fake.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
providers/v1/gcp/secretmanager/client.go (1)
providers/v1/oracle/oracle.go (1)
  • SecretExists (110-110)
🔇 Additional comments (11)
providers/v1/gcp/secretmanager/client_test.go (4)

26-35: LGTM: New imports for time-based testing.

The additions of time and timestamppb imports are correctly added to support the new timestamp-based version selection tests.


1016-1233: Well-structured test cases for SecretExists.

The test cases comprehensively cover:

  • Secret with accessible version exists
  • Multiple versions selecting latest by create time
  • Secret not found (NotFound error)
  • Permission denied error propagation
  • Regional secret existence
  • Regional secret not found
  • Destroyed/disabled version handling

The use of setupMock function for per-test mock injection is a clean pattern.


1235-1315: LGTM: Regional endpoint tests properly updated.

The TestSecretExistsWithRegionalEndpoint tests correctly use the new ListSecretVersionsMockReturn and properly construct regional secret paths like projects/foo/locations/us-east1/secrets/bar/versions/1.


1191-1210: Test expectations are correct and align with implementation.

The test case correctly validates that when a secret's latest enabled version is destroyed/disabled, SecretExists returns (false, error) rather than silently returning false.

The implementation flow confirms this: SecretExists calls getLatestEnabledVersion, which falls back to accessing "latest" when no enabled versions exist. When AccessSecretVersion returns a FailedPrecondition error for a destroyed version, this error is propagated back to the caller. The implementation only treats NotFound as a special case (returning false, nil), while other errors—including FailedPrecondition—are returned as-is. This is the intended behavior: distinguishing between "secret doesn't exist" (NotFound) and "secret exists but cannot be accessed" (FailedPrecondition). The test expectations are valid.

providers/v1/gcp/secretmanager/fake/fake.go (3)

23-24: Acknowledge: Reflection and unsafe usage for test mocking.

The use of reflect and unsafe packages is necessary here to mock the Google Cloud SecretVersionIterator which has unexported fields. The #nosec G103 annotation appropriately documents that this unsafe usage has been audited. Since this is test-only code, this approach is acceptable.


69-143: Well-documented mock iterator implementation.

The NewSecretVersionIterator function is well-documented with clear comments explaining:

  • The purpose of the function
  • How the iterator works (returns all versions on first fetch, then iterator.Done)
  • Why reflection is needed (unexported fields)

The implementation correctly uses iterator.NewPageInfo to wire up the pagination mechanics, making it compatible with the Google iterator package.


296-303: LGTM: Clean mock configuration method.

The NewListSecretVersionsFn method provides a clean API for configuring the mock's ListSecretVersions behavior in tests.

providers/v1/gcp/secretmanager/client.go (4)

149-160: LGTM: SecretExists implementation is clean and correct.

The implementation correctly:

  1. Constructs the secret name using getName
  2. Delegates to getLatestEnabledVersion to check for accessible versions
  3. Returns false, nil for NotFound errors (secret doesn't exist)
  4. Returns false, err for other errors (propagates the error)
  5. Returns true, nil when a version is accessible

690-706: LGTM: Well-documented helper functions.

The getName and getParentName helper functions are well-documented and correctly handle both global and regional secret paths.


708-715: LGTM: Error detection for destroyed/disabled states.

The isErrSecretDestroyedOrDisabled function correctly checks for FailedPrecondition status code and message content to detect destroyed or disabled secret versions.


717-760: Path construction issue has been addressed.

The previous review comments flagged a critical path construction bug where version.Name (a full resource path) was being incorrectly wrapped in another path template. This has been fixed:

  1. versionName is now initialized as empty string (Line 731)
  2. When an enabled version is found, versionName is set to version.Name directly (Line 742) - which is already a full path
  3. Only when no enabled versions are found, versionName is constructed with the fallback path (Lines 748-750)
  4. versionName is used directly in the request (Line 752)

This correctly avoids the double-prefixed path issue.

@tosih
Copy link
Copy Markdown
Contributor Author

tosih commented Dec 17, 2025

please let me know what else is needed to get this merged in, thanks!

@gusfcarvalho
Copy link
Copy Markdown
Member

/ok-to-test sha=2cc7fc612b1a1d00e0598c98bb439a795c466617

@gusfcarvalho
Copy link
Copy Markdown
Member

/ok-to-test-managed sha=2cc7fc612b1a1d00e0598c98bb439a795c466617 provider=gcp

@gusfcarvalho
Copy link
Copy Markdown
Member

LGTM. Lets just wait if e2e will actually pass 😄

@eso-service-account-app
Copy link
Copy Markdown
Contributor

Skarlso
Skarlso previously approved these changes Dec 19, 2025
Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
@sonarqubecloud
Copy link
Copy Markdown

@Skarlso Skarlso merged commit c4241b5 into external-secrets:main Dec 19, 2025
34 checks passed
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Dec 20, 2025
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [external-secrets](https://github.com/external-secrets/external-secrets) | minor | `1.1.1` -> `1.2.0` |

---

### Release Notes

<details>
<summary>external-secrets/external-secrets (external-secrets)</summary>

### [`v1.2.0`](https://github.com/external-secrets/external-secrets/releases/tag/v1.2.0)

[Compare Source](external-secrets/external-secrets@v1.1.1...v1.2.0)

Image: `ghcr.io/external-secrets/external-secrets:v1.2.0`
Image: `ghcr.io/external-secrets/external-secrets:v1.2.0-ubi`
Image: `ghcr.io/external-secrets/external-secrets:v1.2.0-ubi-boringssl`

<!-- Release notes generated using configuration in .github/release.yml at main -->

#### What's Changed

##### General

- chore: bump 1.1.1 by [@&#8203;gusfcarvalho](https://github.com/gusfcarvalho) in [#&#8203;5687](external-secrets/external-secrets#5687)
- chore: fix the argocd e2e test case by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5688](external-secrets/external-secrets#5688)
- feat(provider): add Barbican provider support by [@&#8203;rkferreira](https://github.com/rkferreira) in [#&#8203;5398](external-secrets/external-secrets#5398)
- docs(secretserver): promote secretserver provider to beta by [@&#8203;DelineaSahilWankhede](https://github.com/DelineaSahilWankhede) in [#&#8203;5668](external-secrets/external-secrets#5668)
- feat(controller): add flag to enable/disable secretstore reconcile by [@&#8203;Ilhan-Personal](https://github.com/Ilhan-Personal) in [#&#8203;5653](external-secrets/external-secrets#5653)
- fix(aws-secrets-manager): Apply filtering based on both name and tags if provided by [@&#8203;iypetrov](https://github.com/iypetrov) in [#&#8203;5685](external-secrets/external-secrets#5685)
- fix(gcpsm): SecretExists should check for regional secrets when store location is specified by [@&#8203;tokiwong](https://github.com/tokiwong) in [#&#8203;5708](external-secrets/external-secrets#5708)
- feat: introduce store deprecation by [@&#8203;gusfcarvalho](https://github.com/gusfcarvalho) in [#&#8203;5711](external-secrets/external-secrets#5711)
- feat(charts): add global values for common deployment configurations by [@&#8203;Gabryel8818](https://github.com/Gabryel8818) in [#&#8203;5652](external-secrets/external-secrets#5652)
- feat: add Doppler OIDC-based authentication by [@&#8203;mikesellitto](https://github.com/mikesellitto) in [#&#8203;5475](external-secrets/external-secrets#5475)
- fix: make custom configuration available regardless of environment by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5713](external-secrets/external-secrets#5713)
- chore(chart): update bitwarden dependency to v0.5.2 by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5719](external-secrets/external-secrets#5719)
- docs(templating): update rbac for generic targets by [@&#8203;lostick](https://github.com/lostick) in [#&#8203;5736](external-secrets/external-secrets#5736)
- fix(testing): Breaking changes should not break ci by [@&#8203;evrardjp](https://github.com/evrardjp) in [#&#8203;5739](external-secrets/external-secrets#5739)
- fix(security): Get rid of getSecretKey by [@&#8203;evrardjp](https://github.com/evrardjp) in [#&#8203;5738](external-secrets/external-secrets#5738)
- fix(aws): parse resource policies into canonical JSON (sorted) before comparing by [@&#8203;cmoscofian](https://github.com/cmoscofian) in [#&#8203;5622](external-secrets/external-secrets#5622)
- docs: Fix example in GCP documentation by [@&#8203;headcr4sh](https://github.com/headcr4sh) in [#&#8203;5745](external-secrets/external-secrets#5745)
- chore(secretserver): update dependencies to accept new DelineaXPM/tss-sdk-go by [@&#8203;DelineaSahilWankhede](https://github.com/DelineaSahilWankhede) in [#&#8203;5742](external-secrets/external-secrets#5742)
- fix(gcpsm): Improve SecretExists method in GCP secret manager provider by [@&#8203;tosih](https://github.com/tosih) in [#&#8203;5610](external-secrets/external-secrets#5610)
- chore(docs): add clarification to helm values being disabled by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5746](external-secrets/external-secrets#5746)
- fix(release): apply [`64dc681`](external-secrets/external-secrets@64dc681) to release by [@&#8203;jakobmoellerdev](https://github.com/jakobmoellerdev) in [#&#8203;5749](external-secrets/external-secrets#5749)
- docs(release): 1.2 stability-support.md by [@&#8203;jakobmoellerdev](https://github.com/jakobmoellerdev) in [#&#8203;5750](external-secrets/external-secrets#5750)

##### Dependencies

- chore(deps): bump golang from 1.25.4 to 1.25.5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5693](external-secrets/external-secrets#5693)
- chore(deps): bump golang from 1.25.4-bookworm to 1.25.5-bookworm in /e2e by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5702](external-secrets/external-secrets#5702)
- chore(deps): bump ubi9/ubi from `dcd8128` to `75937d9` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5655](external-secrets/external-secrets#5655)
- chore(deps): bump peter-evans/slash-command-dispatch from 5.0.0 to 5.0.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5695](external-secrets/external-secrets#5695)
- chore(deps): bump github/codeql-action from 4.31.5 to 4.31.7 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5696](external-secrets/external-secrets#5696)
- chore(deps): bump actions/stale from 10.1.0 to 10.1.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5697](external-secrets/external-secrets#5697)
- chore(deps): bump actions/create-github-app-token from 2.2.0 to 2.2.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5700](external-secrets/external-secrets#5700)
- chore(deps): bump step-security/harden-runner from 2.13.2 to 2.13.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5698](external-secrets/external-secrets#5698)
- chore(deps): bump actions/checkout from 6.0.0 to 6.0.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5699](external-secrets/external-secrets#5699)
- chore(deps): bump platformdirs from 4.5.0 to 4.5.1 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5705](external-secrets/external-secrets#5705)
- chore(deps): bump distroless/static from `87bce11` to `4b2a093` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5692](external-secrets/external-secrets#5692)
- chore(deps): bump alpine from 3.22 to 3.23 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5703](external-secrets/external-secrets#5703)
- chore(deps): bump urllib3 from 2.5.0 to 2.6.0 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5704](external-secrets/external-secrets#5704)
- chore(deps): bump pymdown-extensions from 10.17.2 to 10.18 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5706](external-secrets/external-secrets#5706)
- chore(deps): bump alpine from 3.22.2 to 3.23.0 in /e2e by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5701](external-secrets/external-secrets#5701)
- chore(deps): bump golang from `2611181` to `2611181` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5721](external-secrets/external-secrets#5721)
- chore(deps): bump codecov/codecov-action from 5.5.1 to 5.5.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5725](external-secrets/external-secrets#5725)
- chore(deps): bump urllib3 from 2.6.0 to 2.6.2 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5730](external-secrets/external-secrets#5730)
- chore(deps): bump github/codeql-action from 4.31.7 to 4.31.8 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5726](external-secrets/external-secrets#5726)
- chore(deps): bump anchore/sbom-action from 0.20.10 to 0.20.11 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5724](external-secrets/external-secrets#5724)
- chore(deps): bump tornado from 6.5.2 to 6.5.3 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5732](external-secrets/external-secrets#5732)
- chore(deps): bump ubi9/ubi from `75937d9` to `d4feb57` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5722](external-secrets/external-secrets#5722)
- chore(deps): bump golang from `5117d68` to `09f53de` in /e2e by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5729](external-secrets/external-secrets#5729)
- chore(deps): bump alpine from `4b7ce07` to `51183f2` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5694](external-secrets/external-secrets#5694)
- chore(deps): bump hashicorp/setup-terraform from [`712b439`](external-secrets/external-secrets@712b439) to [`071811a`](external-secrets/external-secrets@071811a) by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5727](external-secrets/external-secrets#5727)
- chore(deps): bump pymdown-extensions from 10.18 to 10.19.1 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5731](external-secrets/external-secrets#5731)
- chore(deps): bump step-security/harden-runner from 2.13.3 to 2.14.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5728](external-secrets/external-secrets#5728)
- chore(deps): bump actions/cache from 4.3.0 to 5.0.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5723](external-secrets/external-secrets#5723)

#### New Contributors

- [@&#8203;iypetrov](https://github.com/iypetrov) made their first contribution in [#&#8203;5685](external-secrets/external-secrets#5685)
- [@&#8203;tokiwong](https://github.com/tokiwong) made their first contribution in [#&#8203;5708](external-secrets/external-secrets#5708)
- [@&#8203;Gabryel8818](https://github.com/Gabryel8818) made their first contribution in [#&#8203;5652](external-secrets/external-secrets#5652)
- [@&#8203;mikesellitto](https://github.com/mikesellitto) made their first contribution in [#&#8203;5475](external-secrets/external-secrets#5475)
- [@&#8203;lostick](https://github.com/lostick) made their first contribution in [#&#8203;5736](external-secrets/external-secrets#5736)
- [@&#8203;cmoscofian](https://github.com/cmoscofian) made their first contribution in [#&#8203;5622](external-secrets/external-secrets#5622)
- [@&#8203;headcr4sh](https://github.com/headcr4sh) made their first contribution in [#&#8203;5745](external-secrets/external-secrets#5745)
- [@&#8203;tosih](https://github.com/tosih) made their first contribution in [#&#8203;5610](external-secrets/external-secrets#5610)

**Full Changelog**: <external-secrets/external-secrets@v1.1.1...v1.2.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zOS4xIiwidXBkYXRlZEluVmVyIjoiNDIuMzkuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiY2hhcnQiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/2737
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
@tosih tosih deleted the improve-gcpsm-provider-secret-exists branch December 22, 2025 20:14
dsp0x4 pushed a commit to dsp0x4/external-secrets that referenced this pull request Mar 22, 2026
external-secrets#5610)

Co-authored-by: Gergely Brautigam <skarlso777@gmail.com>
Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/gcpsm area/provider kind/bug Categorizes issue or PR as related to a bug. size/m

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants