-
Notifications
You must be signed in to change notification settings - Fork 693
Add initial secret manager property source implementation #2168
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2168 +/- ##
============================================
- Coverage 72.62% 72.35% -0.27%
- Complexity 1911 1954 +43
============================================
Files 245 251 +6
Lines 7038 7167 +129
Branches 725 735 +10
============================================
+ Hits 5111 5186 +75
- Misses 1589 1645 +56
+ Partials 338 336 -2
Continue to review full report at Codecov.
|
...a/org/springframework/cloud/gcp/autoconfigure/secretmanager/SecretManagerPropertySource.java
Show resolved
Hide resolved
.build(); | ||
|
||
AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName); | ||
return response.getPayload().getData().toStringUtf8(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably document it in SpringBoot that only "string" secrets are accepted. Secret Manager accepts a wide character set for payload values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public Object getProperty(String name)
So, I don't think we really need to convert all values to Strings. @dzou WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good point. I could just leave it as the SecretPayload which would be more flexible. Users would then be injecting/reading SecretPayload
from the environment properties (instead of us forcing strings) and can convert the values to string or their preferred payload.
Do you feel this is a reasonable approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I think there might be problems with mapping to @ConfigurationProperties
classes that have Strings
and other basic Java types. It's something that we should test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You wouldn't want SecretPayload
leaking into user code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spring does the conversion automatically. If there isn't a SecretPayload
encode/decoder it would fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh ok, this SecretPayload
object can really just be converted to two options: String
or byte[]
.
@spencergibb - How do I hook into this encode/decoder concept? (like which interfaces do I implement/register?) Is it possible for me to override getProperty(String propertyName, Class<T> targetType)
such that if targetType is String
I decode to string, and if the target type is byte[]
I decode to byte[]?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String and byte[] properties are now supported through registering custom SecretPayload converters. See: https://github.com/spring-cloud/spring-cloud-gcp/pull/2168/files#diff-04e47cc423dee80f0eb313f527e9f5b2R74-R90
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great start!
The obvious things that are missing, but could be in different PRs: docs, sample app.
...gframework/cloud/gcp/autoconfigure/secretmanager/GcpSecretManagerBootstrapConfiguration.java
Show resolved
Hide resolved
|
||
@Configuration | ||
@EnableConfigurationProperties(GcpSecretManagerProperties.class) | ||
@ConditionalOnProperty("spring.cloud.gcp.secretmanager.enabled") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
havingValue = "true"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be disabled by default like our config management; otherwise all applications would be making RPCs to secret manager to list the secrets even if none exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any required properties in GcpSecretManagerProperties
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no required properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added matchIfMIssing= true
to enable by default and then also added @ConditionalOnClass(SecretManagerServiceClient.class)
to make conditional on the addition of secret manager class to classpath.
...a/org/springframework/cloud/gcp/autoconfigure/secretmanager/SecretManagerPropertySource.java
Show resolved
Hide resolved
...a/org/springframework/cloud/gcp/autoconfigure/secretmanager/SecretManagerPropertySource.java
Outdated
Show resolved
Hide resolved
...pringframework/cloud/gcp/autoconfigure/secretmanager/SecretManagerPropertySourceLocator.java
Outdated
Show resolved
Hide resolved
|
||
super(propertySourceName, client); | ||
|
||
Map<String, Object> propertiesMap = initializePropertiesMap(client, projectIdProvider.getProjectId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're loading properties in the constructor. Would it make sense to allow users to reload the properties by providing a public "refresh" method to do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More generally, test behavior of a@RefreshScope
property in the sample. Ideally, triggering a refresh event should get those properties to expire.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the refresh functionality, can I verify this in a separate PR? This would be something to verify through the addition of the sample app and a Properties class.
.build(); | ||
|
||
AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName); | ||
return response.getPayload().getData().toStringUtf8(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public Object getProperty(String name)
So, I don't think we really need to convert all values to Strings. @dzou WDYT?
.../springframework/cloud/gcp/autoconfigure/secretmanager/it/SecretManagerIntegrationTests.java
Outdated
Show resolved
Hide resolved
/** | ||
* Creates the secret with the specified payload if the secret does not already exist. | ||
*/ | ||
private void createSecret(String secretId, String payload) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reminds me, do we want to create a SecretManagerTemplate
? You would also put the secret reading in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, and this would also go into the new module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make sure this property source supports injecting and refreshing @RefreshScope
annotated properties?
https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html#refresh-scope
import org.springframework.core.env.Environment; | ||
import org.springframework.core.env.PropertySource; | ||
|
||
public class SecretManagerPropertySourceLocator implements PropertySourceLocator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it makes sense to put this class into a new spring-cloud-gcp-secretmanager
module. Then we could give people a single-dependency starter instead of having them bring in the autoconfiguration module combined with explicitly setting a property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it just be conditional on class of the secret manager api rather than setting a property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, we can discuss moving this in the next PR with the addition of secret template which will necessitate creating a the new secretmanager module. For now I am keeping the door open if we decide not to add a template class then this will mirror how our spring config support is organized (in which all the relevant classes are kept in autoconfigure/config).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a tracking issue to add a template?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is tracked in #2176
|
||
super(propertySourceName, client); | ||
|
||
Map<String, Object> propertiesMap = initializePropertiesMap(client, projectIdProvider.getProjectId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More generally, test behavior of a@RefreshScope
property in the sample. Ideally, triggering a refresh event should get those properties to expire.
/** | ||
* Creates the secret with the specified payload if the secret does not already exist. | ||
*/ | ||
private void createSecret(String secretId, String payload) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, and this would also go into the new module.
HashMap<String, Object> secretsMap = new HashMap<>(); | ||
for (Secret secret : response.iterateAll()) { | ||
String secretId = extractSecretId(secret); | ||
ByteString secretPayload = getSecretPayload(client, projectId, secretId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking, but a potential future optimization would be to parallelize this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted, added to #2176 to track.
SecretVersionName secretVersionName = SecretVersionName.newBuilder() | ||
.setProject(projectId) | ||
.setSecret(secretId) | ||
.setSecretVersion(LATEST_VERSION_STRING) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think people will want to access specific secret versions. Defaulting to "latest" is not a production best practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good point, we'll definitely need an answer for this. I think we'll have to discuss a bit more in-depth with our team what is the best way to let users specify which secret versions they want to load into the environment; I guess we wouldn't want to load all versions of all secrets by default.
I added this point to #2176 to track; we will definitely implement this before releasing to users. I just want this PR to setup the initial scaffolding.
...gframework/cloud/gcp/autoconfigure/secretmanager/GcpSecretManagerBootstrapConfiguration.java
Outdated
Show resolved
Hide resolved
...gframework/cloud/gcp/autoconfigure/secretmanager/GcpSecretManagerBootstrapConfiguration.java
Outdated
Show resolved
Hide resolved
...a/org/springframework/cloud/gcp/autoconfigure/secretmanager/SecretManagerPropertySource.java
Outdated
Show resolved
Hide resolved
import org.springframework.core.env.Environment; | ||
import org.springframework.core.env.PropertySource; | ||
|
||
public class SecretManagerPropertySourceLocator implements PropertySourceLocator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a tracking issue to add a template?
.../springframework/cloud/gcp/autoconfigure/secretmanager/it/SecretManagerIntegrationTests.java
Outdated
Show resolved
Hide resolved
SonarCloud Quality Gate failed. 0 Bugs |
@elefeint @meltsufin - PTAL when you guys get a chance :) |
This is the initial scaffolding for setting up Secret Manager as a bootstrap property source.
In progress #2115.