spring-cloud / spring-cloud-config

External configuration (server and client) for Spring Cloud
Apache License 2.0
1.96k stars 1.29k forks source link

Config Server using GitHub AND Vault AND bootstrap = true not working #1485

Closed FWinkler79 closed 4 years ago

FWinkler79 commented 5 years ago

Hi there,

I have inspected the following (somewhat related) issues, but none of them solves the problem I am experiencing:

Some specs of my application:

I tried several setups, which are described below. I could not get either of them working, thus leaving me stuck for now. Help or advice is greatly appreciated.

Intended Setup:

My setup is as follows:

The composite configuration looks as follows:

spring:
  application.name: config-server

  profiles:
    active: composite

  # Config Server Configurations
  cloud:
    config:
      server:
        # Load configurations of config-server itself from Git repository as well. 
        # See: https://cloud.spring.io/spring-cloud-config/reference/html/#_embedding_the_config_server
        bootstrap: true
        composite:
          # Hashicorp Vault repository for storage and reference of secrets / credentials.
          - type: vault 
            host: localhost
            port: 8200
            kvVersion: 2
          # GitHub repository for bootstrapping and runtime configurations
          - type: git
            uri: https://github.com/FWinkler79/SpringCloudPlatform-Configs.git
            deleteUntrackedBranches: true

When I run config-server with this setup, I get the following error:

java.lang.IllegalStateException: No HttpServletRequest available
    at org.springframework.cloud.config.server.environment.HttpRequestConfigTokenProvider.getToken(HttpRequestConfigTokenProvider.java:41) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.getToken(VaultEnvironmentRepository.java:218) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.read(VaultEnvironmentRepository.java:209) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.findOne(VaultEnvironmentRepository.java:147) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.CompositeEnvironmentRepository.findOne(CompositeEnvironmentRepository.java:61) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.EnvironmentRepositoryPropertySourceLocator.locate(EnvironmentRepositoryPropertySourceLocator.java:55) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration.initialize(PropertySourceBootstrapConfiguration.java:97) ~[spring-cloud-context-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:623) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:367) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at com.equalities.cloud.config.server.ConfigServer.main(ConfigServer.java:17) ~[classes/:na]

So I tried what was suggested in #1446, i.e. to add spring.cloud.config.server.vault.token

Option 1:

spring:
  cloud:
    config:
      server:
        vault:
          token: root   # Option 1

       bootstrap: true
        composite:
          # Hashicorp Vault repository for storage and reference of secrets / credentials.
          - type: vault 
           ...
          # GitHub repository for bootstrapping and runtime configurations
          - type: git
            ...

As a result, I get the following error:

2019-09-30 15:35:37.714 DEBUG [config-server,,,] 69017 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : Application failed to start due to an exception

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'configTokenProvider' defined in class path resource [org/springframework/cloud/config/server/config/VaultConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.config.server.config.VaultConfiguration; factoryMethodName=configTokenProvider; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/config/server/config/VaultConfiguration.class]] for bean 'configTokenProvider': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration$DefaultConfigTokenProvider; factoryMethodName=configTokenProvider; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration$DefaultConfigTokenProvider.class]] bound.
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:893) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:274) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:141) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:117) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:327) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:705) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:531) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:140) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:203) ~[spring-cloud-context-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:114) ~[spring-cloud-context-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71) ~[spring-cloud-context-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:76) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:342) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:305) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at com.equalities.cloud.config.server.ConfigServer.main(ConfigServer.java:17) ~[classes/:na]

Option 2:

spring:
  cloud:
    config:
      server:
       bootstrap: true
        composite:
          # Hashicorp Vault repository for storage and reference of secrets / credentials.
          - type: vault 
            token: root   # Option 2
           ...
          # GitHub repository for bootstrapping and runtime configurations
          - type: git
            ...

As a result, I get the following error:

2019-09-30 15:40:23.455 DEBUG [config-server,,,] 69050 --- [           main] .c.l.ClasspathLoggingApplicationListener : Application failed to start with classpath: unknown
2019-09-30 15:40:23.460 DEBUG [config-server,,,] 69050 --- [           main] o.s.boot.diagnostics.FailureAnalyzers    : FailureAnalyzer org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer@7397c6 failed

java.lang.TypeNotPresentException: Type org.springframework.jdbc.CannotGetJdbcConnectionException not present
    at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117) ~[na:na]
    at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125) ~[na:na]
    at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:na]
    at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68) ~[na:na]
    at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138) ~[na:na]
    at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:na]
    at java.base/sun.reflect.generics.repository.ClassRepository.computeSuperclass(ClassRepository.java:104) ~[na:na]
    at java.base/sun.reflect.generics.repository.ClassRepository.getSuperclass(ClassRepository.java:86) ~[na:na]
    at java.base/java.lang.Class.getGenericSuperclass(Class.java:950) ~[na:na]
    at org.springframework.core.ResolvableType.getSuperType(ResolvableType.java:466) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.core.ResolvableType.as(ResolvableType.java:455) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.core.ResolvableType.forClass(ResolvableType.java:1037) ~[spring-core-5.1.9.RELEASE.jar:5.1.9.RELEASE]
    at org.springframework.boot.diagnostics.AbstractFailureAnalyzer.getCauseType(AbstractFailureAnalyzer.java:56) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.diagnostics.AbstractFailureAnalyzer.analyze(AbstractFailureAnalyzer.java:33) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.diagnostics.FailureAnalyzers.analyze(FailureAnalyzers.java:110) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.diagnostics.FailureAnalyzers.reportException(FailureAnalyzers.java:103) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.reportFailure(SpringApplication.java:813) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:798) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at com.equalities.cloud.config.server.ConfigServer.main(ConfigServer.java:17) ~[classes/:na]
Caused by: java.lang.ClassNotFoundException: org.springframework.jdbc.CannotGetJdbcConnectionException
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
    at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
    at java.base/java.lang.Class.forName(Class.java:398) ~[na:na]
    at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114) ~[na:na]
    ... 21 common frames omitted

2019-09-30 15:40:23.462 ERROR [config-server,,,] 69050 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: No HttpServletRequest available
    at org.springframework.cloud.config.server.environment.HttpRequestConfigTokenProvider.getToken(HttpRequestConfigTokenProvider.java:41) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.getToken(VaultEnvironmentRepository.java:218) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.read(VaultEnvironmentRepository.java:209) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.VaultEnvironmentRepository.findOne(VaultEnvironmentRepository.java:147) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.CompositeEnvironmentRepository.findOne(CompositeEnvironmentRepository.java:61) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.config.server.environment.EnvironmentRepositoryPropertySourceLocator.locate(EnvironmentRepositoryPropertySourceLocator.java:55) ~[spring-cloud-config-server-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration.initialize(PropertySourceBootstrapConfiguration.java:97) ~[spring-cloud-context-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:623) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:367) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204) ~[spring-boot-2.1.8.RELEASE.jar:2.1.8.RELEASE]
    at com.equalities.cloud.config.server.ConfigServer.main(ConfigServer.java:17) ~[classes/:na]

Alternative Setup:

I also tried without composite:

spring:
  application.name: config-server

  # Activate the config-server 'composite' profile
  # so that it can load its own configs from Git.
  profiles:
    active: vault, git

  # Config Server Configurations
  cloud:
    config:
      server:
        # Load configurations of config-server itself from Git repository as well. 
        # See: https://cloud.spring.io/spring-cloud-config/reference/html/#_embedding_the_config_server
        bootstrap: true

        # Hashicorp Vault repository for storage and reference of secrets / credentials.
        vault: 
          host: localhost
          port: 8200
          kvVersion: 2
          #token: root  # does neither work with nor without it.

        # GitHub repository for bootstrapping and runtime configurations
        git:
          uri: https://github.com/FWinkler79/SpringCloudPlatform-Configs.git
          deleteUntrackedBranches: true

In this setup I cannot get it to work no matter whether the spring.cloud.config.server.vault.token is set or not - getting the same kind of exceptions as above.

Sample

You can find a sample where this is shown here.

To reproduce:

deciojr commented 5 years ago

After 4-5 days trying to implement this, I found a workaround with the following configuration

Spring cloud version: Greenwich.SR3 Spring Cloud Config Server: 2.1.4.RELEASE Spring boot: 2.1.8

bootstrap.yml

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        bootstrap: true
        composite:
          - type: vault
            kv-version: 2
            token:  VAULT_TOKEN
        vault:
          token: VAULT_TOKEN
  profiles:
    active: composite
  main:
    allow-bean-definition-overriding: true

application.yml

server:
  port: 8888
spring:
   cloud:
    config:
      server:
        composite:
          - type: git
            uri: GIT_URI
            username: ${git.username}
            password: ${git.password}

The only problem is that, when i query the config server at its default profile, the vault configuration gets repeated

image

If anyone knows how to prevent the above or the right way to do this, it would still be very much appreciated

FWinkler79 commented 5 years ago

Hi @deciojr,

great that you had a look at it, it's much appreciated. I am just not sure we should call it a "workaround", since having the vault configuration duplicated is not really clean and kind of unexpected. What do you think?

Cheers!

deciojr commented 5 years ago

I agree. English isn't my primary language, so I couldn't think of any other word at the time.

FWinkler79 commented 5 years ago

I am wondering if this is something that the Spring team would be willing to fix.

Generally, I see a benefit in a config-server being able to bootstrap its own config from the very GitHub repo it uses to serve other services' configs from. And it's also obvious that an integration with a password Vault is a great benefit.

However, this bug makes that combination impossible to use - when I would have thought this should be the preferred setup.

How likely is it that this is getting fixed? Just asking, since otherwise we will need to go for a different, less optimal solution.

spencergibb commented 4 years ago

There have been new options for authenticating vault. See #1475. Can this be used here?

FWinkler79 commented 4 years ago

Thanks for the hint. Unfortunately that does not help here. In the meantime I think I found the bug that causes this issue.

If evth. is configured right (i.e. as documented) I keep getting the error:

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'configTokenProvider', defined in class path resource [org/springframework/cloud/config/server/config/VaultConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration$DefaultConfigTokenProvider.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

2020-01-14 18:25:27.739 DEBUG [config-server,,,] 99242 --- [           main] .c.l.ClasspathLoggingApplicationListener : Application failed to start with classpath: unknown
2020-01-14 18:25:27.739 ERROR [config-server,,,] 99242 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'configTokenProvider' defined in class path resource [org/springframework/cloud/config/server/config/VaultConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.config.server.config.VaultConfiguration; factoryMethodName=configTokenProvider; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/config/server/config/VaultConfiguration.class]] for bean 'configTokenProvider': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration$DefaultConfigTokenProvider; factoryMethodName=configTokenProvider; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration$DefaultConfigTokenProvider.class]] bound.
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:927) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:287) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:144) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:337) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:242) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:706) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:140) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:206) ~[spring-cloud-context-2.2.1.RELEASE.jar:2.2.1.RELEASE]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:117) ~[spring-cloud-context-2.2.1.RELEASE.jar:2.2.1.RELEASE]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:74) ~[spring-cloud-context-2.2.1.RELEASE.jar:2.2.1.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:76) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:345) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    at com.equalities.cloud.config.server.ConfigServer.main(ConfigServer.java:17) ~[classes/:na]

So I checked why this is the case, and found that there are two classes involved, one of which is not correct:

  1. EnvironmentRepositoryConfiguration
  2. VaultConfiguration

EnvironmentRepositoryConfiguration imports VaultConfiguration, and expects that it defines a bean named configTokenProvider. In case that bean is not defined, EnvironmentRepositoryConfiguration will declare its default fallback like this:

@Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(ConfigTokenProvider.class)
    protected static class DefaultConfigTokenProvider {

        @Bean
        public ConfigTokenProvider configTokenProvider(
                ObjectProvider<HttpServletRequest> httpRequest) {
            return new HttpRequestConfigTokenProvider(httpRequest);
        }

However, since the fallback is declared inside a static inner class, Spring evaluates that first, thus not giving VaultConfiguration a chance to provide its (proper) configTokenProvider. This causes the error, and the default configTokenProvider simply does not work.

Note: this could even be a problem in other cases as well, as the list of imported configurations on EnvironmentRepositoryConfiguration is quite extensive.

A Possible Fix

A possible fix should be to change EnvironmentRepositoryConfiguration to not declare the fallback configTokenProvider from within an inner configuration class, but via a @Bean method like this:

// all @Imports here...
public class EnvironmentRepositoryConfiguration {
...
    @Bean
    @ConditionalOnMissingBean(ConfigTokenProvider.class)
    public ConfigTokenProvider configTokenProvider(ObjectProvider<HttpServletRequest> httpRequest) {
         return new HttpRequestConfigTokenProvider(httpRequest);
    }
...
}

I was able to reproduce the erroneous behaviour in a test rig given here.

Is there a chance this can be fixed?

spencergibb commented 4 years ago

mark yours as @Primary?

spencergibb commented 4 years ago

PRs welcome.

FWinkler79 commented 4 years ago

Since there is no bean of mine involved, but only framework beans that I cannot mark nor re-declare, I cannot mark anything as @Primary.

I created a PR that fixes this issue, so that the intended setup will work. If the PR is merged, I will add a final description here, that shows the setup.

I tested this in my local environment, using this project setup.