Skip to content

feat: add GCP Secret Manager OpenFeature provider#1772

Merged
toddbaert merged 32 commits into
open-feature:mainfrom
mahpatil:feat/gcp-secret-manager
May 28, 2026
Merged

feat: add GCP Secret Manager OpenFeature provider#1772
toddbaert merged 32 commits into
open-feature:mainfrom
mahpatil:feat/gcp-secret-manager

Conversation

@mahpatil

@mahpatil mahpatil commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a new OpenFeature provider for GCP Secret Manager that reads feature flags from GCP Secret Manager secrets
  • Includes flag caching, value conversion for all OpenFeature types (bool, string, int, double, object), and background polling for updates
  • Adds a sample application under samples/gcp-secret-manager-sample/ with setup/teardown scripts

Provider Details

  • Package: providers/gcp-secret-manager
  • Class: GcpSecretManagerProvider
  • Supports all OpenFeature flag types via structured or plain-text secret values
  • Configurable poll interval and GCP project settings

Test plan

  • Unit tests for FlagCache, FlagValueConverter, and GcpSecretManagerProvider
  • Integration test (GcpSecretManagerProviderIntegrationTest) requires a real GCP project — set GCP_PROJECT_ID env var to run
  • Sample app: follow samples/gcp-secret-manager-sample/README.md to run end-to-end

@mahpatil mahpatil requested a review from a team as a code owner April 9, 2026 05:18

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new OpenFeature provider for Google Cloud Secret Manager, enabling feature flags to be managed as GCP secrets. The changes include the core provider implementation with caching and value conversion, along with comprehensive unit and integration tests, and a sample application. Feedback suggests adding an initial entry to the new module's CHANGELOG.md, refining the spotbugs-exclusions.xml to only include relevant exclusions, and improving the testability of FlagCache by injecting a Clock instance instead of relying on Instant.now() and Thread.sleep().

Comment thread providers/gcp-secret-manager/CHANGELOG.md Outdated
Comment thread providers/gcp-secret-manager/spotbugs-exclusions.xml Outdated
@mahpatil mahpatil force-pushed the feat/gcp-secret-manager branch from 1c0fd8d to 0d10a01 Compare April 11, 2026 20:44
@aepfli

aepfli commented Apr 13, 2026

Copy link
Copy Markdown
Member

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new OpenFeature provider for Google Cloud Secret Manager, enabling feature flags to be stored and managed as secrets. The implementation includes a TTL-based in-memory cache to optimize API usage and a converter to handle various OpenFeature data types. Feedback identifies several improvement opportunities, including fixing duplicate entries in the changelog, addressing a race condition and a potential 'thundering herd' issue in the caching logic, and strengthening input validation for the secret version configuration.

Comment thread providers/gcp-secret-manager/CHANGELOG.md Outdated
@chrfwow

chrfwow commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

The build pipeline currently fails for two reasons:
The commits are not signed off. To fix this, you will need to sign off all your changes and force push them on this branch
The type check fails, this can be fixed automatically when you run the spotlessApply command

@mahpatil mahpatil force-pushed the feat/gcp-secret-manager branch from bcefaa7 to 6af66e7 Compare April 24, 2026 08:54
@mahpatil

Copy link
Copy Markdown
Contributor Author

The build pipeline currently fails for two reasons: The commits are not signed off. To fix this, you will need to sign off all your changes and force push them on this branch The type check fails, this can be fixed automatically when you run the spotlessApply command

Signed off commits to fix DCO

@mahpatil mahpatil force-pushed the feat/gcp-secret-manager branch from 74e41c0 to 1d65ba5 Compare April 24, 2026 22:18
@mahpatil

Copy link
Copy Markdown
Contributor Author

I hope all comments have been addressed now. Please let me know if there's anything outstanding.

@mahpatil

Copy link
Copy Markdown
Contributor Author

Good catch — removed the duplicate now, clock, and cache setup from concurrentExpiryAndInsertDoNotLoseNewEntry and switched to the T0/T1 constants and instance fields already initialised in @BeforeEach.

@mahpatil mahpatil force-pushed the feat/gcp-secret-manager branch from d01ff47 to ea481a2 Compare April 27, 2026 15:41
@toddbaert

Copy link
Copy Markdown
Member

Re: #1771 (cc @aepfli @chrfwow @mahpatil)

Looking at #1771 + #1772 side by side, I think there's been a small misunderstanding of @aepfli's earlier feedback. IIUC he asked for the PR to be split for review hygiene ("a pull request is small, and provides just one additional feature"), not necessarily for two separate Maven modules. The split-PRs ask has been honoured; the split-modules part was an additional interpretation.

@chrfwow already raised the same question on #1771. Posting here too since this is where active review is happening, and IMO it's much cheaper to settle before #1772 merges than after.

Let's keep 2 PRs but land them in one module.

  • The two PRs duplicate ~80% of code (FlagCache, FlagValueConverter, the provider scaffolding, tests). They're already drifting (Clock injection, STATIC vs CACHED, exception cause, log4j versions).
  • The GCP client deps are technically different artifacts, but their transitive trees overlap heavily (gax, gRPC, protobuf, auth, etc.); the incremental footprint of having both on the classpath is small.
  • One module = one CHANGELOG, one release, one set of internals to maintain. Two modules = ongoing duplication tax. A third gcp-common module is overkill for what's effectively one shared FlagCache.

@mahpatil - to keep things reviewable, suggest stacking the branches: rework this PR (#1772) to add a single providers/gcp module with pom.xml, shared internals (FlagCache, FlagValueConverter), and the Secret Manager provider. Then rebase #1771 on top, so it only adds the Parameter Manager provider on top of the shared scaffolding. SM-first makes sense since this PR is closer to approval. That way each PR stays small and focused, and the shared scaffolding only shows up in the diff once.

Suggested module name: providers/gcp (artifact gcp). Short, accurate, and leaves room if other GCP-backed providers ever land later. gcp-secret-and-parameter-manager works too but is mouthful-y. WDYT?

@aepfli - was your earlier ask only about the PR split, or did you also intend two artifacts? Happy to discuss if there's a packaging reason I'm missing.

@toddbaert toddbaert requested a review from aepfli April 30, 2026 15:02
@mahpatil

mahpatil commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

Re: #1771 (cc @aepfli @chrfwow @mahpatil)

Looking at #1771 + #1772 side by side, I think there's been a small misunderstanding of @aepfli's earlier feedback. IIUC he asked for the PR to be split for review hygiene ("a pull request is small, and provides just one additional feature"), not necessarily for two separate Maven modules. The split-PRs ask has been honoured; the split-modules part was an additional interpretation.

@chrfwow already raised the same question on #1771. Posting here too since this is where active review is happening, and IMO it's much cheaper to settle before #1772 merges than after.

Let's keep 2 PRs but land them in one module.

  • The two PRs duplicate ~80% of code (FlagCache, FlagValueConverter, the provider scaffolding, tests). They're already drifting (Clock injection, STATIC vs CACHED, exception cause, log4j versions).
  • The GCP client deps are technically different artifacts, but their transitive trees overlap heavily (gax, gRPC, protobuf, auth, etc.); the incremental footprint of having both on the classpath is small.
  • One module = one CHANGELOG, one release, one set of internals to maintain. Two modules = ongoing duplication tax. A third gcp-common module is overkill for what's effectively one shared FlagCache.

@mahpatil - to keep things reviewable, suggest stacking the branches: rework this PR (#1772) to add a single providers/gcp module with pom.xml, shared internals (FlagCache, FlagValueConverter), and the Secret Manager provider. Then rebase #1771 on top, so it only adds the Parameter Manager provider on top of the shared scaffolding. SM-first makes sense since this PR is closer to approval. That way each PR stays small and focused, and the shared scaffolding only shows up in the diff once.

Suggested module name: providers/gcp (artifact gcp). Short, accurate, and leaves room if other GCP-backed providers ever land later. gcp-secret-and-parameter-manager works too but is mouthful-y. WDYT?

@aepfli - was your earlier ask only about the PR split, or did you also intend two artifacts? Happy to discuss if there's a packaging reason I'm missing.

This is a bit frustrating as I have been receiving feedback on the PR and now requested to refactor again. @aepfli please finalize with @chrfwow what you would want, I don't want to be keep coming back and incorporating comments, wasting everyones time. I feel the process very slow even though we are using gemini and im using claude, this PR has been going back and forth for a while.
Please provide concrete steps that I can incorporate quickly so we can merge this PR.

@aepfli

aepfli commented May 1, 2026

Copy link
Copy Markdown
Member

@mahpatil first of all, I want to apologise. Reading back through the thread, I can absolutely see how
my original comment landed the way it did, and the frustration you are voicing is completely fair. You
have been responsive, you have been thorough, and the last thing I want is for a contributor putting in
this kind of effort to feel that the goalposts keep moving. That is on me for not being clearer the
first time, and I am sorry for the churn it has caused.

Let me try to clear up the misunderstanding, because I think @toddbaert read my intent correctly. When I
asked you to split the work, I was talking about splitting the pull request for review hygiene —
keeping each PR small and focused on one feature so that we as maintainers can evaluate it properly. I
was not asking for two separate Maven modules or two separate artifacts. That part was an interpretation
on top of my ask, and I should have spelled it out at the time. The duplication that has surfaced
between #1771 and #1772 is exactly the kind of thing the "one module" approach avoids.

The technique that makes "split the PR" and "share the code" coexist is what is usually called stacked
diffs (or stacked PRs). The idea is simple: instead of two independent branches off main, you stack
them. PR A branches off main and contains the shared scaffolding plus the first provider. PR B then
branches off PR A's branch (not main) and adds only the second provider on top. Each PR stays small and
reviewable on its own, the shared code only shows up in the diff once, and once PR A merges, PR B's diff
naturally collapses to just its own delta. It is the same review benefit I was originally asking for,
without paying the duplication tax.

Concretely, for your case I think Todd's suggestion is exactly right: rework #1772 to add a single
providers/gcp module containing the shared internals (FlagCache, FlagValueConverter, scaffolding) and
the Secret Manager provider, then rebase #1771 on top of that branch so it only adds the Parameter
Manager provider. SM-first makes sense since this PR is closer to approval anyway.

Again, I am sorry for the back-and-forth — your contribution is genuinely appreciated and we want to
land it. If it would help, I am happy to jump on a quick call or pair on the rebase so we can get this
over the line without another round of comments.

Ayushman-Gaur and others added 7 commits May 12, 2026 17:38
Signed-off-by: Ayushman Gaur <ayushmangaur2017@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
…pen-feature#1780)

Signed-off-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
- TTL-based in-memory FlagCache with Clock injection for deterministic testing
- Thread-safe cache: synchronized get/remove block eliminates check-then-act race
- Double-checked locking in fetchWithCache prevents thundering herd on GCP API
- FlagValueConverter uses Boolean.parseBoolean for boolean flag values
- secretVersion validation added to GcpSecretManagerProviderOptions
- VmLens concurrent cache test covering concurrent get/put/expiry scenarios
- Tests for negative and exponential number formats in FlagValueConverterTest
- Integration test null guard for provider

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
- Move all setup (clock, cache, expired entry) inside the VmLens loop
  so each interleaving starts with a clean, deterministic state
- Replace the conditional assertion with an unconditional check:
  assertThat(cache.get("key")).isPresent().hasValue("new-value")
  After Thread A's put() completes, the new value must always be present;
  the previous if-present guard would silently pass a correctness bug
- Remove unused outer cache/controllableClock/now variables and the
  redundant sharedCache/freshClock inside the loop

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
… test"

This reverts commit 58cf2a6.

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
The previous implementation had several structural problems:

- clock, cache (and an unused outer cache) were recreated inside the
  VmLens while-loop. VmLens needs stable object references across
  iterations to correctly track which shared memory locations are
  concurrently accessed; recreating objects each iteration means VmLens
  sees different heap addresses and cannot build a reliable access model.

- now.set(now.get().plusSeconds(31)) inside the loop kept advancing the
  clock monotonically, so state was never cleanly reset between
  interleavings and could allow "new-value" entries to expire.

- The assertion was too weak: if (result.isPresent()) { ... } would
  silently pass even when the cache is empty, hiding the exact bug the
  test is meant to catch.

Fixed by:
- Creating clock and cache once outside the loop (stable references)
- Resetting state inside the loop to fixed instants (t0/t1) via
  cache.clear() + now.set(t0) before each interleaving
- Using an unconditional assertion:
  assertThat(cache.get("key")).isPresent().hasValue("new-value")
  After Runner.runParallel returns, Thread A's put() has completed so
  "new-value" must always survive Thread B's expiry-removal.

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Adds getOnTimedOutEntryWhileConcurrentInsertNeverReturnsStaleValue following
the FlagdProviderCTest pattern: shared state prepared once before the
interleaving loop in @beforeeach, only Runner.runParallel inside the loop,
and assertions embedded in the parallel lambdas.

Verifies that get() on a timed-out entry concurrent with a put() of the
same key never returns the stale value — result must be empty or the
freshly inserted value.

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
@mahpatil

Copy link
Copy Markdown
Contributor Author

@mahpatil first of all, I want to apologise. Reading back through the thread, I can absolutely see how my original comment landed the way it did, and the frustration you are voicing is completely fair. You have been responsive, you have been thorough, and the last thing I want is for a contributor putting in this kind of effort to feel that the goalposts keep moving. That is on me for not being clearer the first time, and I am sorry for the churn it has caused.

Let me try to clear up the misunderstanding, because I think @toddbaert read my intent correctly. When I asked you to split the work, I was talking about splitting the pull request for review hygiene — keeping each PR small and focused on one feature so that we as maintainers can evaluate it properly. I was not asking for two separate Maven modules or two separate artifacts. That part was an interpretation on top of my ask, and I should have spelled it out at the time. The duplication that has surfaced between #1771 and #1772 is exactly the kind of thing the "one module" approach avoids.

The technique that makes "split the PR" and "share the code" coexist is what is usually called stacked diffs (or stacked PRs). The idea is simple: instead of two independent branches off main, you stack them. PR A branches off main and contains the shared scaffolding plus the first provider. PR B then branches off PR A's branch (not main) and adds only the second provider on top. Each PR stays small and reviewable on its own, the shared code only shows up in the diff once, and once PR A merges, PR B's diff naturally collapses to just its own delta. It is the same review benefit I was originally asking for, without paying the duplication tax.

Concretely, for your case I think Todd's suggestion is exactly right: rework #1772 to add a single providers/gcp module containing the shared internals (FlagCache, FlagValueConverter, scaffolding) and the Secret Manager provider, then rebase #1771 on top of that branch so it only adds the Parameter Manager provider. SM-first makes sense since this PR is closer to approval anyway.

Again, I am sorry for the back-and-forth — your contribution is genuinely appreciated and we want to land it. If it would help, I am happy to jump on a quick call or pair on the rebase so we can get this over the line without another round of comments.

okay thanks for clarifying. I have now consolidated this into single provider for GCP. Do we also want to have single factory and tests to instantiate & test both the providers? Happy to incorporate that here or when merging Parameter manager.

@mahpatil

Copy link
Copy Markdown
Contributor Author

@chrfwow @aepfli just checking next steps on this? I dont see any responses to my comments or updated PR. Are you still keen to include this contribution?

@chrfwow

chrfwow commented May 27, 2026

Copy link
Copy Markdown
Contributor

The CI fails because there are some formatting issue. You can fix them by running spotless:apply

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
@mahpatil

Copy link
Copy Markdown
Contributor Author

Fixed formatting issues.

Comment thread providers/gcp/README.md Outdated
Comment thread providers/gcp/README.md Outdated
Comment thread samples/gcp/setup.sh Outdated
…onfig

Restore root pom.xml and release-please-config.json to their state on main,
keeping only the additions required for providers/gcp:
- <module>providers/gcp</module> in pom.xml
- new providers/gcp block in release-please-config.json

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Comment thread providers/gcp/pom.xml
@toddbaert toddbaert self-requested a review May 27, 2026 18:35

@toddbaert toddbaert left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved. I have some recommendations that I think are mostly around doc, and one about potentially improving the deps with a BOM ref, but I will leave that up to you. If I don't hear anything in the next 24 hrs, I will merge; otherwise feel free to make those minor fixes or tell me to hold off.

Thanks again for your contribution and patience.

PS: I made a small change to revert whitespace noise in the parent POM.

mahpatil and others added 4 commits May 28, 2026 09:29
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Mahesh Patil <17205424+mahpatil@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Mahesh Patil <17205424+mahpatil@users.noreply.github.com>
…gcp/GcpProviderOptions.java

Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Mahesh Patil <17205424+mahpatil@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Mahesh Patil <17205424+mahpatil@users.noreply.github.com>
@mahpatil

Copy link
Copy Markdown
Contributor Author

Approved. I have some recommendations that I think are mostly around doc, and one about potentially improving the deps with a BOM ref, but I will leave that up to you. If I don't hear anything in the next 24 hrs, I will merge; otherwise feel free to make those minor fixes or tell me to hold off.

Thanks again for your contribution and patience.

PS: I made a small change to revert whitespace noise in the parent POM.

Thank you @toddbaert, only one outstanding option on use of BOM, will incorporate it as part of parameter mgr, which is coming next. If you can now merge I can start on Parameter mgr which is really the more common usecase for us.

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
@toddbaert

Copy link
Copy Markdown
Member

FYI I added you as a "component_owner" for this, which will add you as assignee/reviewer to PRs in this folder.

@toddbaert toddbaert merged commit 17de2b7 into open-feature:main May 28, 2026
5 checks passed
@mahpatil mahpatil deleted the feat/gcp-secret-manager branch May 29, 2026 15:56
mahpatil added a commit to mahpatil/java-sdk-contrib that referenced this pull request Jun 3, 2026
Signed-off-by: Ayushman Gaur <ayushmangaur2017@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Signed-off-by: Mahesh Patil <17205424+mahpatil@users.noreply.github.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Ayushman-Gaur <120575155+Ayushman-Gaur@users.noreply.github.com>
Co-authored-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Co-authored-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants