Refactor Java Secret classes to align with Python SDK - #39806
Conversation
27d4b4e to
54130b9
Compare
54130b9 to
7ac81c0
Compare
Enhance Secret management interfaces and implementations in the Java SDK to match the capabilities of apache_beam/utils/secret.py: - Add RawSecret class to wrap raw byte arrays and string secrets directly. - Add getBytes(boolean) and getString(boolean) default methods to Secret interface for cached retrieval. - Add static fromJson factory method to Secret interface. - Add in-memory caching and map specification parsing (fromMap) to GcpSecret and GcpHsmGeneratedSecret. - Add GCP project ID fallback resolution in GcpSecret using environment variables (GOOGLE_CLOUD_PROJECT, GCP_PROJECT) and Application Default Credentials. - Implement equals and hashCode across all Secret implementations.
7ac81c0 to
5ee01d7
Compare
|
r: @damccorm |
|
Stopping reviewer notifications for this pull request: review requested by someone other than the bot, ceding control. If you'd like to restart, comment |
|
This PR does not change the location of However, For GCP secret manager, it is currently integrated in GBEK, but we may also move it to https://github.com/apache/beam/tree/master/sdks/java/extensions/google-cloud-platform-core/src/main/java/org/apache/beam/sdk/extensions/gcp in the future. WDYT? @damccorm |
damccorm
left a comment
There was a problem hiding this comment.
Thanks - this mostly LGTM, just had a couple minor comments
| "Invalid secret parameter " + String.join(", ", sortedInvalid)); | ||
| } | ||
| return new GcpHsmGeneratedSecret( | ||
| Preconditions.checkNotNull( |
There was a problem hiding this comment.
Do we need these precondition checks? Can these ever be null at this point?
There was a problem hiding this comment.
I slightly change the validation, PTAL.
But yes, GcpHsmGeneratedSecret requires nonnull parameters.
There was a problem hiding this comment.
Cool, it was more to do with the duplicated validation (which I think is no longer being done)
| if (Strings.isNullOrEmpty(projectId)) { | ||
| projectId = System.getenv("GOOGLE_CLOUD_PROJECT"); | ||
| } | ||
| if (Strings.isNullOrEmpty(projectId)) { | ||
| projectId = System.getenv("GCP_PROJECT"); | ||
| } | ||
| if (Strings.isNullOrEmpty(projectId)) { | ||
| try { | ||
| Class<?> clazz = Class.forName("com.google.cloud.ServiceOptions"); | ||
| java.lang.reflect.Method method = clazz.getMethod("getDefaultProjectId"); | ||
| @SuppressWarnings("nullness") | ||
| Object result = method.invoke(null); | ||
| if (result != null) { | ||
| projectId = result.toString(); | ||
| } | ||
| } catch (Throwable e) { | ||
| LOG.debug("Could not resolve GCP project via ServiceOptions reflection", e); | ||
| } | ||
| } |
There was a problem hiding this comment.
If we're allowing this here, should we do the same for GcpHsmGeneratedSecret?
There was a problem hiding this comment.
Done. I extracted that piece of code into a function and applied that in GcpHsmGeneratedSecret.
| * should be able to return a valid byte array representing the secret. | ||
| */ | ||
| public interface Secret extends Serializable { | ||
| public abstract class Secret implements Serializable { |
There was a problem hiding this comment.
Another way of doing this is to keep the Secret interface as not using cache, and then define a new abstract class CachedSecret.
This gives people flexibility to implement their Secret provider if they don't want cache in the first place, but it also adds another class layer of complexity.
It would be great to get a second opinion on this.
There was a problem hiding this comment.
I like your current approach - it mirrors what we're doing in Python and separates the secret provider from the preferred client side behavior which is nice.
I think breaking them out makes sense. We could probably move the tests that depend on We'd need to modify to allow classes to register secret managers (similar to how FileSystems work), but this seems doable |
…n GcpHsmGeneratedSecret.
Sure. I will do the class moving (without functional changes) in a separate PR. Agreed on the secret manager registration, but I think we can live without it in our first version. :) |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #39806 +/- ##
============================================
- Coverage 58.48% 58.14% -0.34%
+ Complexity 14825 13135 -1690
============================================
Files 2737 2524 -213
Lines 274981 265686 -9295
Branches 12057 10824 -1233
============================================
- Hits 160818 154493 -6325
+ Misses 107890 105358 -2532
+ Partials 6273 5835 -438
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
Aligns Java SDK Secret management classes with the Python SDK (
apache_beam.utils.secret).Follow-up to #39636
Changes
Secret: Converted to an abstract class; addedgetBytes(boolean cacheSecret)in-memory caching,getString()accessors, andSecret.fromJson(...)factory methods.RawSecret: Added implementation wrapping plaintext strings or byte arrays.GcpSecret&GcpHsmGeneratedSecret: AddedfromMapspecification parsing, GCP project ID fallback resolution inGcpSecret, andequals/hashCodeacross all Secret classes.SecretTest.javacovering JSON parsing, caching, option strings, and serialization.