1

I am trying to use spring-cloud-starter-aws for one purpose - so that my ec2 instance can access its elasticache node using the logical name instead of the physical name. My issue is that I want to run this binary in a local context without AWS (not using the elasticache node) but I have not been able to disable. I get an exception: An IOException occured when connecting to service endpoint: http://169.254.169.254/latest/dynamic/instance-identity/document. The exception is:

Error creating bean with name 'org.springframework.cloud.aws.context.support.io.ResourceLoaderBeanPostProcessor#0': 
Cannot resolve reference to bean 'amazonS3'.

I think this is because the aws sdk is calling out to the instance endpoint which I don't have running in a local context. How can I disable this?

Aritz
  • 30,971
  • 16
  • 136
  • 217
user2298491
  • 117
  • 2
  • 5
  • Have you tried something like this? http://stackoverflow.com/a/37162383/1199132 – Aritz Aug 30 '16 at 17:10
  • I tried putting: aws: stack: auto: false in application.yml and it made no difference. I will look at the section in the docs on manual configuration. Thanks. – user2298491 Aug 31 '16 at 08:55

2 Answers2

1

Thanks to @Martian previous answer the application have been modified as so

First disable the autoconfiguration beans related o AWS inthe application class

@SpringBootApplication(exclude = {
org.springframework.cloud.aws.autoconfigure.context.ContextInstanceDataAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.context.ContextCredentialsAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.context.ContextRegionProviderAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.context.ContextResourceLoaderAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.context.ContextStackAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.mail.MailSenderAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.cache.ElastiCacheAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.messaging.MessagingAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.jdbc.AmazonRdsDatabaseAutoConfiguration.class,
org.springframework.cloud.aws.autoconfigure.metrics.CloudWatchExportAutoConfiguration.class
})

Then create an AWS dedicated Spring boot config with the appropriate coditional settings

    /**
     * Only enable AWS related auto config when we run in AWS Cloud
     */
    @ConditionalOnAwsCloudEnvironment
    @Import({
        org.springframework.cloud.aws.autoconfigure.context.ContextInstanceDataAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.context.ContextCredentialsAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.context.ContextRegionProviderAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.context.ContextResourceLoaderAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.context.ContextStackAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.mail.MailSenderAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.cache.ElastiCacheAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.messaging.MessagingAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.jdbc.AmazonRdsDatabaseAutoConfiguration.class,
        org.springframework.cloud.aws.autoconfigure.metrics.CloudWatchExportAutoConfiguration.class
    })
    public class AwsConfig {
    }
Sylvain
  • 876
  • 8
  • 11
0

spring-cloud-starter-aws uses spring-cloud-aws-autoconfigure which provides the following AWS related configurations:

org.springframework.cloud.aws.autoconfigure.context.ContextInstanceDataAutoConfiguration
org.springframework.cloud.aws.autoconfigure.context.ContextCredentialsAutoConfiguration
org.springframework.cloud.aws.autoconfigure.context.ContextRegionProviderAutoConfiguration
org.springframework.cloud.aws.autoconfigure.context.ContextResourceLoaderAutoConfiguration
org.springframework.cloud.aws.autoconfigure.context.ContextStackAutoConfiguration
org.springframework.cloud.aws.autoconfigure.mail.MailSenderAutoConfiguration
org.springframework.cloud.aws.autoconfigure.cache.ElastiCacheAutoConfiguration
org.springframework.cloud.aws.autoconfigure.messaging.MessagingAutoConfiguration
org.springframework.cloud.aws.autoconfigure.jdbc.AmazonRdsDatabaseAutoConfiguration
org.springframework.cloud.aws.autoconfigure.metrics.CloudWatchExportAutoConfiguration

All of them are triggered by EnableAutoConfiguration. If you can live without EnableAutoConfiguration your problem is solved. However, since you are using spring-cloud-starter-aws, I do not expect that this is the case.

Unfortunatelly, only ContextInstanceDataAutoConfiguration and ContextInstanceDataAutoConfiguration employs @ConditionalOnAwsCloudEnvironment, the remaining configurations start regardless of the environment. Most of them are conditioned only with something similar to the following (this example is from ContextCredentialsAutoConfiguration):

@ConditionalOnClass(name = {"com.amazonaws.auth.AWSCredentialsProvider"})

To not start such configuration you need to remove the com.amazonaws.auth.AWSCredentialsProvider class from classpath. Which can be accomplished with maven profiles or fancy modularisation, but I believe that it is usually a suboptimal solution as there are significant side efects of such project restructuration.

I believe that the simplest solution is to remove spring-cloud-aws-autoconfigure from project dependencies and write own versions of the configuration class. Here is a ContextCredentialsAutoConfiguration version that worked for me:

import mu.KLogging
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.cloud.aws.context.config.annotation.ContextDefaultConfigurationRegistrar
import org.springframework.cloud.aws.context.config.support.ContextConfigurationUtils
import org.springframework.context.EnvironmentAware
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar
import org.springframework.context.annotation.Profile
import org.springframework.core.env.Environment
import org.springframework.core.type.AnnotationMetadata

@Configuration
@Import(ContextDefaultConfigurationRegistrar::class, AwsContextCredentialsAutoConfiguration.Registrar::class)
@ConditionalOnClass(name = ["com.amazonaws.auth.AWSCredentialsProvider"])
@Profile("!it")
class AwsContextCredentialsAutoConfiguration {

    class Registrar : ImportBeanDefinitionRegistrar, EnvironmentAware {
        companion object: KLogging()
        private var environment: Environment? = null

        override fun setEnvironment(environment: Environment) {
            this.environment = environment
        }

        override fun registerBeanDefinitions(importingClassMetadata: AnnotationMetadata, registry: BeanDefinitionRegistry) {
            val useDefaultCredentialsChain = this.environment!!.getProperty("cloud.aws.credentials.useDefaultAwsCredentialsChain", Boolean::class.java, false)
            if (useDefaultCredentialsChain) {
                logger.debug("Using default AWS credentials provider")
                ContextConfigurationUtils.registerDefaultAWSCredentialsProvider(registry)
            } else {
                logger.debug("Using custom credentials provider (based on environment properties)")
                ContextConfigurationUtils.registerCredentialsProvider(
                        registry,
                        this.environment!!.getProperty("cloud.aws.credentials.accessKey"),
                        this.environment!!.getProperty("cloud.aws.credentials.secretKey"),
                        this.environment!!.getProperty("cloud.aws.credentials.instanceProfile", Boolean::class.java, true) as Boolean && !this.environment!!.containsProperty("cloud.aws.credentials.accessKey"),
                        this.environment!!.getProperty("cloud.aws.credentials.profileName", "default"),
                        this.environment!!.getProperty("cloud.aws.credentials.profilePath"))
            }

        }
    }
}

Please note the @Profile("!it") annotation, it is the profile enabled for integration tests. As a consequence, the configuration is ignored during the integration tests, but you can replace the it with the profile you are using for your local context.

Marian
  • 2,571
  • 2
  • 9
  • 8