Skip to content
This repository was archived by the owner on Jan 19, 2022. It is now read-only.

EC2 metadata resolution related exception thrown when running application locally #556

Closed
nanachimi opened this issue May 22, 2020 · 9 comments
Labels
component: core An issue related to core functionality - credentials, region resolution status: in-progress An issue that being worked on type: documentation A documentation update
Milestone

Comments

@nanachimi
Copy link

Describe the bug
I used spring-cloud-starter-aws (version 2.2.1.RELEASE) to send email using Amazon SES. I'm able to send email but I have the following error when starting the app. I'm not dealing with EC2 but it looks like the underline aws-java-sdk-ses (version 1.11.787) is trying to load EC2 resources. How can I get rid of this misconfiguration?

2020-05-22 17:41:09.647  WARN 47453 --- [           main] i.InstanceMetadataServiceResourceFetcher : Fail to retrieve token 

com.amazonaws.SdkClientException: Failed to connect to service endpoint: 
	at com.amazonaws.internal.EC2ResourceFetcher.doReadResource(EC2ResourceFetcher.java:100) ~[aws-java-sdk-core-1.11.787.jar:na]
	at com.amazonaws.internal.InstanceMetadataServiceResourceFetcher.getToken(InstanceMetadataServiceResourceFetcher.java:91) ~[aws-java-sdk-core-1.11.787.jar:na]
	at com.amazonaws.internal.InstanceMetadataServiceResourceFetcher.readResource(InstanceMetadataServiceResourceFetcher.java:69) ~[aws-java-sdk-core-1.11.787.jar:na]
	at com.amazonaws.internal.EC2ResourceFetcher.readResource(EC2ResourceFetcher.java:66) ~[aws-java-sdk-core-1.11.787.jar:na]
	at com.amazonaws.util.EC2MetadataUtils.getItems(EC2MetadataUtils.java:402) ~[aws-java-sdk-core-1.11.787.jar:na]
	at com.amazonaws.util.EC2MetadataUtils.getData(EC2MetadataUtils.java:371) ~[aws-java-sdk-core-1.11.787.jar:na]
	at org.springframework.cloud.aws.context.support.env.AwsCloudEnvironmentCheckUtils.isRunningOnCloudEnvironment(AwsCloudEnvironmentCheckUtils.java:38) ~[spring-cloud-aws-context-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.cloud.aws.context.annotation.OnAwsCloudEnvironmentCondition.matches(OnAwsCloudEnvironmentCondition.java:37) ~[spring-cloud-aws-context-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:225) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:599) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:110) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:811) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:808) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:779) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:319) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:62) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.0.RELEASE.jar:2.3.0.RELEASE]
	at com.packnmoov.webappserver.WebAppServerRunner.main(WebAppServerRunner.java:9) ~[main/:na]
Caused by: java.net.ConnectException: No route to host (connect failed)
	at java.base/java.net.PlainSocketImpl.socketConnect(Native Method) ~[na:na]
	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399) ~[na:na]
	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242) ~[na:na]
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224) ~[na:na]
	at java.base/java.net.Socket.connect(Socket.java:591) ~[na:na]
	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:242) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341) ~[na:na]
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1242) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1221) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1075) ~[na:na]
	at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1009) ~[na:na]
	at com.amazonaws.internal.ConnectionUtils.connectToEndpoint(ConnectionUtils.java:52) ~[aws-java-sdk-core-1.11.787.jar:na]
	at com.amazonaws.internal.EC2ResourceFetcher.doReadResource(EC2ResourceFetcher.java:80) ~[aws-java-sdk-core-1.11.787.jar:na]
	... 30 common frames omitted

Sample

@Configuration
public class AwsMailConfig {

    private final String awsAccessKey;
    private final String awsSecretKey;
...
    @Bean
    public AmazonSimpleEmailService amazonSimpleEmailService() {
        return AmazonSimpleEmailServiceClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider( new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                .withRegion(Regions.EU_CENTRAL_1)
                .build();
    }
...
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 22, 2020
@giova333
Copy link
Contributor

giova333 commented May 28, 2020

This is how OnAwsCloudEnvironmentCondition works. In order to understand if the application is executed on AWS environment it tries to get instance metadata https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/instancedata-data-retrieval.html The issue is more related to the exception handling/logging as I see.
@maciejwalkowiak I can check this

@maciejwalkowiak
Copy link
Contributor

maciejwalkowiak commented May 29, 2020

Thanks @giova333. Indeed it's just a log message triggered during @ConditionalOnAwsCloudEnvironment evaluation. It happens only with new version of AWS SDK v1 (checked with 1.11.791). It would be nice if we find away to detect AWS env without having this exception logged. A workaround for today is adding to application.properties:

logging.level.com.amazonaws.util.EC2MetadataUtils=error

@maciejwalkowiak maciejwalkowiak added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels May 29, 2020
@maciejwalkowiak maciejwalkowiak added this to the 2.3 milestone May 29, 2020
@maciejwalkowiak maciejwalkowiak added the component: core An issue related to core functionality - credentials, region resolution label May 29, 2020
@maciejwalkowiak maciejwalkowiak changed the title Spring Cloud AWS - Failed to connect to service endpoint when Sending Mail with AWS SES EC2 metadata resolution related exception thrown when running application locally May 29, 2020
@giova333
Copy link
Contributor

@maciejwalkowiak I don't think we will be able to manage that on our side.
EC2 metadata service is the recommended way to identify whether an application is running on AWS environment. Of course, we can call this service without using EC2MetadataUtils and this will solve the problem in this particular case but not everywhere.

Aws sdk uses DefaultAwsRegionProviderChain as default class for region discovery and
DefaultAWSCredentialsProviderChain for credentials lookup.

Both classes have a dependency on EC2MetadataUtils

DefaultAwsRegionProviderChain -> InstanceMetadataRegionProvider -> EC2MetadataUtils
DefaultAWSCredentialsProviderChain -> InstanceProfileCredentialsProvider -> EC2MetadataUtils

This means that whenever developers run our code outside ec2 instances and use some clients such as AmazonS3Client, AWSSecretsManagerClient ... without providing the region explicitly, sdk will try to load the region from env variables, system properties, aws config. If the region is not provided in any of the sources that I mentioned above, as the last resort, DefaultAwsRegionProviderChain
will try to load the region from ec2 metadata service using EC2MetadataUtils. And this is where the same issue with the logging will occur and the same flow for credentials lookup.
It is easy to reproduce, just call the perform s3 GetObjectRequest, catch the exception and you will see the same problem (checked with 1.11.791):

 try {
            AmazonS3Client.builder()
                    .build()
                    .getObject("bucketName", "key");
        } catch (Exception e) {  
        }

I would say that the issue has a global level and as for me the most appropriate way to manage it is to report it to aws sdk saying that the developers should be able to manage such cases themselves, handling the thrown exception, without having the original exception logged. That is what I am going to do.

@maciejwalkowiak maciejwalkowiak added type: documentation A documentation update and removed type: enhancement A general enhancement labels May 31, 2020
@maciejwalkowiak maciejwalkowiak modified the milestones: 2.3, 2.2.3 May 31, 2020
@maciejwalkowiak maciejwalkowiak added the status: in-progress An issue that being worked on label May 31, 2020
maciejwalkowiak added a commit that referenced this issue May 31, 2020
tmnuwan12 pushed a commit to tmnuwan12/spring-cloud-aws that referenced this issue Jun 7, 2020
@fransflippo
Copy link

For me the problem is not just the exception being logged, but the several seconds this adds to my startup time. Is there no way to set a property that will override this so this check doesn't need to be done? Just like cloud.aws.region.static=eu-west-1 specifies the region instead of it being autodetected.

@psrmx
Copy link

psrmx commented Oct 13, 2020

-> This means that whenever developers run our code outside ec2 instances and use some clients ... without providing the region explicitly...
Problem is that the warning/ exception still happens when providing the region explicitly. I have set cloud.aws.region.static=eu-central-1 and cloud.aws.region.auto=false, but I still get the autodetected warning.
Hiding a log warning feels more like tackling the symptoms instead of addressing the cause to me.

@maciejwalkowiak
Copy link
Contributor

@psrmx I understand the sentiment but the underlying issue is in AWS SDK. We will consider changing the way cloud environment is detected (as fetching the metadata is anyway very slow) in Spring Cloud AWS 3.0.

maciejwalkowiak added a commit to maciejwalkowiak/spring-cloud-aws that referenced this issue Oct 15, 2020
maciejwalkowiak added a commit to maciejwalkowiak/spring-cloud-aws that referenced this issue Oct 15, 2020
maciejwalkowiak added a commit to maciejwalkowiak/spring-cloud-aws that referenced this issue Oct 15, 2020
maciejwalkowiak added a commit to maciejwalkowiak/spring-cloud-aws that referenced this issue Oct 15, 2020
@raja-baz
Copy link

So this happens as part of the evaluation of @ConditionalOnAwsCloudEnvironment which feeds into two pieces of auto-configuration: ElastiCacheAutoConfiguration and ContextInstanceDataAutoConfiguration. If you don't care about those being autoconfigured, you can exclude them and this issue goes away.

Like so(in kotlin):

@SpringBootApplication(exclude = [
	ElastiCacheAutoConfiguration::class,
	ContextInstanceDataAutoConfiguration::class,
])

@edudar
Copy link

edudar commented Apr 2, 2021

Or even better, in properties like this:

spring:
  autoconfiure:
    exclude:
      - <full package path>.ContextInstanceDataAutoConfiguration
      - <full package path>.ElastiCacheAutoConfiguration

and have this exclusion only in dev or tests profile but keep them in prod or whatever that runs in AWS

However, there might be more uses for AwsCloudEnvironmentCheckUtils so I'd still like to vote for a new property like cloud.aws.metadata.enabled or spring.cloud.aws.metadata.enabled. If it's false that AwsCloudEnvironmentCheckUtils .isRunningOnCloudEnvironment() would return false preemptively without even asking AWS about it. This can obviously be implemented with a custom property but then one has to drag it around from project to project.

@maciejwalkowiak
Copy link
Contributor

In 2.3.0 we introduced a property cloud.aws.instance.data.enabled which is false by default.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
component: core An issue related to core functionality - credentials, region resolution status: in-progress An issue that being worked on type: documentation A documentation update
Development

No branches or pull requests

8 participants