diff --git a/docs/modules/ROOT/pages/client.adoc b/docs/modules/ROOT/pages/client.adoc index 543a7708..f639805a 100644 --- a/docs/modules/ROOT/pages/client.adoc +++ b/docs/modules/ROOT/pages/client.adoc @@ -93,6 +93,7 @@ First, you need to set `spring.cloud.config.fail-fast=true`. Then you need to add `spring-retry` and `spring-boot-starter-aop` to your classpath. The default behavior is to retry six times with an initial backoff interval of 1000ms and an exponential multiplier of 1.1 for subsequent backoffs. You can configure these properties (and others) by setting the `spring.cloud.config.retry.*` configuration properties. +To use a random exponential backoff policy set `spring.cloud.config.retry.useRandomPolicy` to `true`. TIP: To take full control of the retry behavior and are using legacy bootstrap, add a `@Bean` of type `RetryOperationsInterceptor` with an ID of `configServerRetryInterceptor`. Spring Retry has a `RetryInterceptorBuilder` that supports creating one. diff --git a/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager-backend.adoc b/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager-backend.adoc index a0ea21e6..5155a8d1 100644 --- a/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager-backend.adoc +++ b/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager-backend.adoc @@ -37,5 +37,6 @@ AWS Secrets Manager API credentials are determined using link:https://docs.aws.a [NOTE] ==== - When no application is specified `application` is the default, and when no profile is specified `default` is used. +- Both `label` and `defaultLabel` properties are ignored, when `ignoreLabel` is set to `true`. ==== diff --git a/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager.adoc b/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager.adoc index c414f478..1b0c995f 100644 --- a/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager.adoc +++ b/docs/modules/ROOT/pages/server/environment-repository/aws-secrets-manager.adoc @@ -78,3 +78,17 @@ Note that if the default label is not set and a request does not define a label, Note that if the staging label contains a slash (`/`), then the label in the HTTP URL should instead be specified with the special string `(\{special-string})` (to avoid ambiguity with other URL paths) the same way <<_git_backend,Git backend's section>> describes it. +Use `spring.cloud.config.server.aws-secretsmanager.ignore-label` property to ignore the `{label}` parameter of the HTTP resource as well as `spring.cloud.config.server.aws-secretsmanager.default-label` property. The repository will use secrets as if labelled version support is disabled. + +[source,yaml] +---- +spring: + profiles: + active: aws-secretsmanager + cloud: + config: + server: + aws-secretsmanager: + region: us-east-1 + ignore-label: true +---- diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index e3090ac5..fd9f93cd 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -22,6 +22,7 @@ |spring.cloud.config.retry.max-attempts | `+++6+++` | Maximum number of attempts. |spring.cloud.config.retry.max-interval | `+++2000+++` | Maximum interval for backoff. |spring.cloud.config.retry.multiplier | `+++1.1+++` | Multiplier for next interval. +|spring.cloud.config.retry.use-random-policy | `+++false+++` | Use a random exponential backoff policy. |spring.cloud.config.send-state | `+++true+++` | Flag to indicate whether to send state. Default true. |spring.cloud.config.tls | | TLS properties. |spring.cloud.config.token | | Security Token passed thru to underlying environment repository. diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoader.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoader.java index ced0f6ef..7a4eda8a 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoader.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoader.java @@ -265,8 +265,23 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader 1) { + String[] uris; + boolean discoveryEnabled = properties.getDiscovery().isEnabled(); + ConfigClientProperties bootstrapConfigClientProperties = context.getBootstrapContext() + .get(ConfigClientProperties.class); + // In the case where discovery is enabled we need to extract the config server + // uris, username, and password + // from the properties from the context. These are set in + // ConfigServerInstanceMonitor.refresh which will only + // be called the first time we fetch configuration. + if (discoveryEnabled) { + uris = bootstrapConfigClientProperties.getUri(); + } + else { + uris = properties.getUri(); + } + int noOfUrls = uris.length; + if (uris.length > 1) { logger.info("Multiple Config Server Urls found listed."); } @@ -284,10 +299,19 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader combineProfiles(ConfigClientProperties properties, org.springframework.core.env.Environment environment) { List combinedProfiles = new ArrayList<>(); - if (!ObjectUtils.isEmpty(properties.getProfile())) { - combinedProfiles = Stream.of(properties.getProfile().split(",")).map(String::trim).filter(s -> !s.isEmpty()) - .collect(Collectors.toList()); - } if (environment.getActiveProfiles().length > 0) { List finalCombinedProfiles = combinedProfiles; List filteredActiveProfiles = Stream.of(environment.getActiveProfiles()) @@ -110,7 +104,9 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator @Retryable(interceptor = "configServerRetryInterceptor") public org.springframework.core.env.PropertySource locate(org.springframework.core.env.Environment environment) { ConfigClientProperties properties = this.defaultProperties.override(environment); - properties.setProfile(String.join(",", combineProfiles(properties, environment))); + if (!StringUtils.hasText(properties.getProfile())) { + properties.setProfile(String.join(",", combineProfiles(properties, environment))); + } if (StringUtils.startsWithIgnoreCase(properties.getName(), "application-")) { InvalidApplicationNameException exception = new InvalidApplicationNameException(properties.getName()); diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryProperties.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryProperties.java index 903f0c8b..7eb88e77 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryProperties.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryProperties.java @@ -50,6 +50,11 @@ public class RetryProperties { */ int maxAttempts = 6; + /** + * Use a random exponential backoff policy. + */ + boolean useRandomPolicy = false; + public long getInitialInterval() { return this.initialInterval; } @@ -82,4 +87,12 @@ public class RetryProperties { this.maxAttempts = maxAttempts; } + public boolean isUseRandomPolicy() { + return this.useRandomPolicy; + } + + public void setUseRandomPolicy(boolean useRandomPolicy) { + this.useRandomPolicy = useRandomPolicy; + } + } diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryTemplateFactory.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryTemplateFactory.java index 5aa2abbc..068ea985 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryTemplateFactory.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/RetryTemplateFactory.java @@ -39,9 +39,9 @@ public final class RetryTemplateFactory { } public static RetryTemplate create(RetryProperties properties, Log log) { - RetryTemplate retryTemplate = RetryTemplate.builder().maxAttempts(properties.getMaxAttempts()) - .exponentialBackoff(properties.getInitialInterval(), properties.getMultiplier(), - properties.getMaxInterval()) + RetryTemplate retryTemplate = RetryTemplate + .builder().maxAttempts(properties.getMaxAttempts()).exponentialBackoff(properties.getInitialInterval(), + properties.getMultiplier(), properties.getMaxInterval(), properties.isUseRandomPolicy()) .build(); try { field.set(retryTemplate, log); diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoaderTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoaderTests.java index a80483d3..9fad257d 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoaderTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLoaderTests.java @@ -412,6 +412,28 @@ public class ConfigServerConfigDataLoaderTests { } + @Test + public void useDiscoveryUriIfEnabled() throws Exception { + String[] uris = new String[] { "http://uritest:8888" }; + properties.setUri(uris); + ConfigClientProperties.Discovery discovery = new ConfigClientProperties.Discovery(); + discovery.setEnabled(true); + discovery.setServiceId("configservice"); + properties.setDiscovery(discovery); + this.loader = new ConfigServerConfigDataLoader(destination -> logger); + ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class); + RestTemplate restTemplate = new RestTemplate(requestFactory); + when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate); + ConfigClientProperties bootstrapConfigClientProperties = new ConfigClientProperties(); + bootstrapConfigClientProperties.setDiscovery(discovery); + bootstrapConfigClientProperties.setUri(new String[] { "http://configservice:8888" }); + when(bootstrapContext.get(ConfigClientProperties.class)).thenReturn(bootstrapConfigClientProperties); + + mockRequestResponse(requestFactory, "http://configservice:8888", HttpStatus.OK); + + assertThat(this.loader.load(context, resource)).isNotNull(); + } + @Disabled @Test // TODO Enable once we have diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolverTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolverTests.java index 504050a8..f2f3c822 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolverTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolverTests.java @@ -138,6 +138,7 @@ public class ConfigServerConfigDataLocationResolverTests { assertThat(resource.getRetryProperties().getMaxInterval()).isEqualTo(defaultRetry.getMaxInterval()); assertThat(resource.getRetryProperties().getInitialInterval()).isEqualTo(defaultRetry.getInitialInterval()); assertThat(resource.getRetryProperties().getMultiplier()).isEqualTo(defaultRetry.getMultiplier()); + assertThat(resource.getRetryProperties().isUseRandomPolicy()).isEqualTo(defaultRetry.isUseRandomPolicy()); } @Test @@ -158,6 +159,7 @@ public class ConfigServerConfigDataLocationResolverTests { assertThat(resource.getRetryProperties().getMaxInterval()).isEqualTo(1500); assertThat(resource.getRetryProperties().getInitialInterval()).isEqualTo(1100); assertThat(resource.getRetryProperties().getMultiplier()).isEqualTo(1.2); + assertThat(resource.getRetryProperties().isUseRandomPolicy()).isEqualTo(false); } @Test diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocatorTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocatorTests.java index 9515bc6c..60731cba 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocatorTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocatorTests.java @@ -118,6 +118,17 @@ public class ConfigServicePropertySourceLocatorTests { assertThat(this.locator.locateCollection(this.environment)).isNotNull(); } + @Test + public void overrideProfile() { + Environment body = new Environment("app", "override-profile"); + body.add(new PropertySource("p1", new HashMap<>())); + mockRequestResponseWithProfile(new ResponseEntity<>(body, HttpStatus.OK), "override-profile"); + this.locator.setRestTemplate(this.restTemplate); + TestPropertyValues.of("spring.cloud.config.profile:override-profile", "spring.profiles.active: foo") + .applyTo(this.environment); + assertThat(this.locator.locateCollection(this.environment).size()).isEqualTo(2); + } + @Test public void sunnyDayWithLabelThatContainsASlash() { Environment body = new Environment("app", "master"); @@ -571,6 +582,12 @@ public class ConfigServicePropertySourceLocatorTests { ArgumentMatchers.eq(label))).thenReturn(response); } + private void mockRequestResponseWithProfile(ResponseEntity response, String profiles) { + Mockito.when(this.restTemplate.exchange(Mockito.any(String.class), Mockito.any(HttpMethod.class), + Mockito.any(HttpEntity.class), Mockito.any(Class.class), anyString(), ArgumentMatchers.eq(profiles))) + .thenReturn(response); + } + @SuppressWarnings("unchecked") private void mockRequestResponseWithoutLabel(ResponseEntity response) { Mockito.when(this.restTemplate.exchange(Mockito.any(String.class), Mockito.any(HttpMethod.class), diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentProperties.java index 2895a1da..f9decfa9 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentProperties.java @@ -54,6 +54,11 @@ public class AwsSecretsManagerEnvironmentProperties implements EnvironmentReposi */ private String defaultLabel; + /** + * Do not set staging label when fetching the secret values. + */ + private boolean ignoreLabel; + /** * The order of the environment repository. */ @@ -105,6 +110,14 @@ public class AwsSecretsManagerEnvironmentProperties implements EnvironmentReposi this.defaultLabel = defaultLabel; } + public boolean isIgnoreLabel() { + return this.ignoreLabel; + } + + public void setIgnoreLabel(boolean ignoreLabel) { + this.ignoreLabel = ignoreLabel; + } + public int getOrder() { return order; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepository.java index 0443b5e4..bdb4a89b 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepository.java @@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; +import software.amazon.awssdk.services.secretsmanager.model.InvalidRequestException; import software.amazon.awssdk.services.secretsmanager.model.ResourceNotFoundException; import org.springframework.cloud.config.environment.Environment; @@ -72,6 +73,7 @@ public class AwsSecretsManagerEnvironmentRepository implements EnvironmentReposi final String defaultApplication = configServerProperties.getDefaultApplicationName(); final String defaultProfile = configServerProperties.getDefaultProfile(); final String defaultLabel = environmentProperties.getDefaultLabel(); + final boolean ignoreLabel = environmentProperties.isIgnoreLabel(); if (ObjectUtils.isEmpty(application)) { application = defaultApplication; @@ -81,7 +83,10 @@ public class AwsSecretsManagerEnvironmentRepository implements EnvironmentReposi profileList = defaultProfile; } - if (StringUtils.isEmpty(label)) { + if (ignoreLabel) { + label = null; + } + else if (StringUtils.isEmpty(label)) { label = defaultLabel; } @@ -155,7 +160,7 @@ public class AwsSecretsManagerEnvironmentRepository implements EnvironmentReposi } } } - catch (ResourceNotFoundException | IOException e) { + catch (InvalidRequestException | ResourceNotFoundException | IOException e) { log.debug(String.format( "Skip adding propertySource. Unable to load secrets from AWS Secrets Manager for secretId=%s", path), e); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepositoryTests.java index 600aa08a..3b8bfe65 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/AwsSecretsManagerEnvironmentRepositoryTests.java @@ -42,6 +42,7 @@ import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest; import software.amazon.awssdk.services.secretsmanager.model.CreateSecretResponse; import software.amazon.awssdk.services.secretsmanager.model.DeleteSecretRequest; +import software.amazon.awssdk.services.secretsmanager.model.RestoreSecretRequest; import software.amazon.awssdk.services.secretsmanager.model.UpdateSecretVersionStageRequest; import org.springframework.cloud.config.environment.Environment; @@ -87,10 +88,19 @@ public class AwsSecretsManagerEnvironmentRepositoryTests { private final AwsSecretsManagerEnvironmentRepository labeledRepository = new AwsSecretsManagerEnvironmentRepository( smClient, configServerProperties, labeledEnvironmentProperties); + private final AwsSecretsManagerEnvironmentProperties ignoreLabelEnvironmentProperties = new AwsSecretsManagerEnvironmentProperties() {{ + setIgnoreLabel(true); + }}; + + private final AwsSecretsManagerEnvironmentRepository ignoreLabelRepository = new AwsSecretsManagerEnvironmentRepository( + smClient, configServerProperties, ignoreLabelEnvironmentProperties); + private final ObjectMapper objectMapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true); private final List toBeRemoved = new ArrayList<>(); + private final List markedForDeletion = new ArrayList<>(); + private static Map getFooProperties() { return new HashMap() { { @@ -237,6 +247,10 @@ public class AwsSecretsManagerEnvironmentRepositoryTests { @AfterEach public void cleanUp() { + markedForDeletion + .forEach(value -> smClient.restoreSecret(RestoreSecretRequest.builder().secretId(value).build())); + markedForDeletion.clear(); + toBeRemoved.forEach(value -> smClient .deleteSecret(DeleteSecretRequest.builder().secretId(value).forceDeleteWithoutRecovery(true).build())); toBeRemoved.clear(); @@ -1820,6 +1834,47 @@ public class AwsSecretsManagerEnvironmentRepositoryTests { assertThat(resultEnv).usingRecursiveComparison().withStrictTypeChecking().isEqualTo(expectedEnv); } + @Test + public void testFindOneWithExistingApplicationAndExistingProfileAndExistingLabelWhenIgnoreLabelIsSet() { + String application = "foo"; + String profile = "prod"; + String label = "release"; + String[] profiles = StringUtils.commaDelimitedListToStringArray(profile); + + String fooProdPropertiesName = "aws:secrets:/secret/foo-prod/"; + PropertySource fooProdProperties = new PropertySource(fooProdPropertiesName, getFooProdReleaseProperties()); + + String fooPropertiesName = "aws:secrets:/secret/foo/"; + PropertySource fooProperties = new PropertySource(fooPropertiesName, getFooReleaseProperties()); + + String fooDefaultPropertiesName = "aws:secrets:/secret/foo-default/"; + PropertySource fooDefaultProperties = new PropertySource(fooDefaultPropertiesName, + getFooDefaultReleaseProperties()); + + String applicationProdPropertiesName = "aws:secrets:/secret/application-prod/"; + PropertySource applicationProdProperties = new PropertySource(applicationProdPropertiesName, + getApplicationProdReleaseProperties()); + + String applicationDefaultPropertiesName = "aws:secrets:/secret/application-default/"; + PropertySource applicationDefaultProperties = new PropertySource(applicationDefaultPropertiesName, + getApplicationDefaultReleaseProperties()); + + String applicationPropertiesName = "aws:secrets:/secret/application/"; + PropertySource applicationProperties = new PropertySource(applicationPropertiesName, + getApplicationReleaseProperties()); + + Environment expectedEnv = new Environment(application, profiles, null, null, null); + expectedEnv.addAll(Arrays.asList( + fooProdProperties, applicationProdProperties, fooDefaultProperties, + applicationDefaultProperties, fooProperties, applicationProperties)); + + putSecrets(expectedEnv); + + Environment resultEnv = ignoreLabelRepository.findOne(application, profile, label); + + assertThat(resultEnv).usingRecursiveComparison().withStrictTypeChecking().isEqualTo(expectedEnv); + } + @Test public void testFindOneWithNullApplicationAndNullProfile() { String application = null; @@ -2502,6 +2557,36 @@ public class AwsSecretsManagerEnvironmentRepositoryTests { assertThat(resultEnv).usingRecursiveComparison().withStrictTypeChecking().isEqualTo(environment); } + @Test + public void testFindOneWithExistingApplicationAndNonExistingProfileAndNoDefaultProfileForFooMarkedForDeletion() { + String application = "foo"; + String profile = randomAlphabetic(RandomUtils.nextInt(2, 25)); + String[] profiles = StringUtils.commaDelimitedListToStringArray(profile); + + String fooPropertiesName = "aws:secrets:/secret/foo/"; + PropertySource fooProperties = new PropertySource(fooPropertiesName, getFooProperties()); + + String applicationDefaultPropertiesName = "aws:secrets:/secret/application-default/"; + PropertySource applicationDefaultProperties = new PropertySource(applicationDefaultPropertiesName, + getApplicationDefaultProperties()); + + String applicationPropertiesName = "aws:secrets:/secret/application/"; + PropertySource applicationProperties = new PropertySource(applicationPropertiesName, + getApplicationProperties()); + + Environment environment = new Environment(application, profiles, null, null, null); + environment.addAll(Arrays.asList(applicationDefaultProperties, fooProperties, applicationProperties)); + + putSecrets(environment); + deleteSecrets(environment); + + Environment emptyEnvironment = new Environment(application, profiles, null, null, null); + + Environment resultEnv = repository.findOne(application, profile, null); + + assertThat(resultEnv).usingRecursiveComparison().withStrictTypeChecking().isEqualTo(emptyEnvironment); + } + @Test public void factoryCustomizableWithRegion() { AwsSecretsManagerEnvironmentRepositoryFactory factory = new AwsSecretsManagerEnvironmentRepositoryFactory( @@ -2539,6 +2624,14 @@ public class AwsSecretsManagerEnvironmentRepositoryTests { } } + private void deleteSecrets(Environment environment) { + for (PropertySource ps : environment.getPropertySources()) { + String path = StringUtils.delete(ps.getName(), environmentProperties.getOrigin()); + smClient.deleteSecret(DeleteSecretRequest.builder().secretId(path).recoveryWindowInDays(30L).build()); + markedForDeletion.add(path); + } + } + private String getSecrets(PropertySource ps) { Map map = (Map) ps.getSource(); try {