Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2013-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.awspring.cloud.autoconfigure.config.s3;

public class AwsS3PropertySourceNotFoundException extends RuntimeException {

AwsS3PropertySourceNotFoundException(Exception source) {
super(source);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataLoader;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.MapPropertySource;
import org.springframework.lang.Nullable;
Expand All @@ -44,29 +43,23 @@ public S3ConfigDataLoader(DeferredLogFactory logFactory) {
@Override
@Nullable
public ConfigData load(ConfigDataLoaderContext context, S3ConfigDataResource resource) {
try {
// resource is disabled if s3 integration is disabled via
// spring.cloud.aws.s3.config.enabled=false
if (resource.isEnabled()) {
S3Client s3Client = context.getBootstrapContext().get(S3Client.class);
S3PropertySource propertySource = resource.getPropertySources()
.createPropertySource(resource.getContext(), resource.isOptional(), s3Client);
if (propertySource != null) {
return new ConfigData(Collections.singletonList(propertySource));
}
else {
return null;
}
// resource is disabled if s3 integration is disabled via
// spring.cloud.aws.s3.config.enabled=false
if (resource.isEnabled()) {
S3Client s3Client = context.getBootstrapContext().get(S3Client.class);
S3PropertySource propertySource = resource.getPropertySources().createPropertySource(resource.getContext(),
resource.isOptional(), s3Client);
if (propertySource != null) {
return new ConfigData(Collections.singletonList(propertySource));
}
else {
// create dummy empty config data
return new ConfigData(Collections.singletonList(new MapPropertySource("aws-s3:" + context, Map.of())));
return null;
}
}
catch (Exception e) {
throw new ConfigDataResourceNotFoundException(resource, e);
else {
// create dummy empty config data
return new ConfigData(Collections.singletonList(new MapPropertySource("aws-s3:" + context, Map.of())));
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2013-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.awspring.cloud.autoconfigure.config.s3;

import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;

public class S3ExceptionHappenedAnalyzer extends AbstractFailureAnalyzer<AwsS3PropertySourceNotFoundException> {

@Override
protected FailureAnalysis analyze(Throwable rootFailure, AwsS3PropertySourceNotFoundException cause) {
return new FailureAnalysis(
"Could not import properties from AWS S3. Exception happened while trying to load the keys: "
+ cause.getMessage(),
"Depending on error message determine action course", cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,4 @@ public S3PropertySource createPropertySource(String context, boolean optional, S
return null;
}

static class AwsS3PropertySourceNotFoundException extends RuntimeException {

AwsS3PropertySourceNotFoundException(Exception source) {
super(source);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ io.awspring.cloud.autoconfigure.config.s3.S3ConfigDataLoader
org.springframework.boot.diagnostics.FailureAnalyzer=\
io.awspring.cloud.autoconfigure.config.parameterstore.ParameterStoreMissingKeysFailureAnalyzer, \
io.awspring.cloud.autoconfigure.config.parameterstore.ParameterStoreExceptionHappenedAnalyzer, \
io.awspring.cloud.autoconfigure.config.s3.S3ExceptionHappenedAnalyzer, \
io.awspring.cloud.autoconfigure.config.secretsmanager.SecretsManagerMissingKeysFailureAnalyzer,\
io.awspring.cloud.autoconfigure.config.s3.S3MissingKeysFailureAnalyzer
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.awspring.cloud.autoconfigure.config.s3;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.*;
import static org.testcontainers.shaded.org.awaitility.Awaitility.await;

Expand All @@ -27,12 +28,15 @@
import java.util.Map;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.bootstrap.BootstrapRegistry;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.ConfigurableApplicationContext;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
Expand All @@ -58,12 +62,15 @@
*/

@Testcontainers
@ExtendWith(OutputCaptureExtension.class)
public class S3ConfigDataLoaderIntegrationTests {
private static final String YAML_TYPE = "application/x-yaml";
private static final String YAML_TYPE_ALTERNATIVE = "text/yaml";
private static final String TEXT_TYPE = "text/plain";
private static final String JSON_TYPE = "application/json";
private static String BUCKET = "test-bucket";

private static final String NEW_LINE_CHAR = System.lineSeparator();
@Container
static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:4.4.0")).withReuse(true);
Expand Down Expand Up @@ -150,6 +157,26 @@ void clientIsConfiguredWithCustomizerProvidedToBootstrapRegistry() throws JsonPr
}
}

@Test
void failOnKeysMissing(CapturedOutput output) {
SpringApplication application = new SpringApplication(App.class);
application.setWebApplicationType(WebApplicationType.NONE);

try (ConfigurableApplicationContext context = runApplication(application,
"aws-s3:test-bucket/tst.properties")) {
fail("Context without keys should fail to start");
}
catch (Exception e) {
assertThat(e).isInstanceOf(AwsS3PropertySourceNotFoundException.class);
// ensure that failure analyzer catches the exception and provides meaningful
// error message
// Ensure that new line character should be platform independent
String errorMessage = "Description:%1$s%1$sCould not import properties from AWS S3. Exception happened while trying to load the keys:"
.formatted(NEW_LINE_CHAR);
assertThat(output.getOut()).contains(errorMessage);
}
}

@Test
void reloadPropertiesFromS3() {
SpringApplication application = new SpringApplication(S3ConfigDataLoaderIntegrationTests.App.class);
Expand Down