This is an automated email from the ASF dual-hosted git repository. Croway pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit 26e95d53a691360e71fa225584c324e56b3483c6 Author: croway <[email protected]> AuthorDate: Fri Jul 10 18:29:50 2026 +0200 CAMEL-24000: camel-spring-boot - consolidate vault auto-configurations and guard against option drift Replace the eight hand-written per-field copy chains in the vault auto-configurations with BeanUtils.copyProperties (all properties mirror the Camel core vault configurations by name and type), and add a reflection test that fails when Camel core gains a vault option the starter properties class does not expose. The new test immediately found real drift: camel.vault.ibm.refresh-enabled and camel.vault.ibm.secrets existed in Camel core but could not be configured from Spring Boot. Add both properties and regenerate spring-boot.json. Co-Authored-By: Claude Fable 5 <[email protected]> --- .../src/main/docs/spring-boot.json | 13 +++ .../boot/vault/AwsVaultAutoConfiguration.java | 15 +--- .../boot/vault/AzureVaultAutoConfiguration.java | 14 +--- .../boot/vault/CyberArkVaultAutoConfiguration.java | 11 +-- .../boot/vault/GcpVaultAutoConfiguration.java | 9 +- .../vault/HashicorpVaultAutoConfiguration.java | 11 +-- .../boot/vault/IBMVaultAutoConfiguration.java | 10 +-- .../vault/IBMVaultConfigurationProperties.java | 26 ++++++ .../KubernetesConfigMapVaultAutoConfiguration.java | 4 +- .../vault/KubernetesVaultAutoConfiguration.java | 4 +- .../VaultConfigurationPropertiesMappingTest.java | 96 ++++++++++++++++++++++ 11 files changed, 151 insertions(+), 62 deletions(-) diff --git a/core/camel-spring-boot/src/main/docs/spring-boot.json b/core/camel-spring-boot/src/main/docs/spring-boot.json index 80b6541d6ca..8639a13ba8f 100644 --- a/core/camel-spring-boot/src/main/docs/spring-boot.json +++ b/core/camel-spring-boot/src/main/docs/spring-boot.json @@ -2036,6 +2036,19 @@ "description": "Specify the username to access IBM Event Stream", "sourceType": "org.apache.camel.spring.boot.vault.IBMVaultConfigurationProperties" }, + { + "name": "camel.vault.ibm.refresh-enabled", + "type": "java.lang.Boolean", + "description": "Whether to automatically refresh the secrets on update", + "sourceType": "org.apache.camel.spring.boot.vault.IBMVaultConfigurationProperties", + "defaultValue": false + }, + { + "name": "camel.vault.ibm.secrets", + "type": "java.lang.String", + "description": "Define the secrets to look at", + "sourceType": "org.apache.camel.spring.boot.vault.IBMVaultConfigurationProperties" + }, { "name": "camel.vault.ibm.service-url", "type": "java.lang.String", diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AwsVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AwsVaultAutoConfiguration.java index 843bd034003..88c8327bee2 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AwsVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AwsVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.AwsVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,19 +32,7 @@ public class AwsVaultAutoConfiguration { @Bean public AwsVaultConfiguration awsVaultConfiguration(AwsVaultConfigurationProperties config) { AwsVaultConfiguration answer = new AwsVaultConfiguration(); - answer.setAccessKey(config.getAccessKey()); - answer.setSecretKey(config.getSecretKey()); - answer.setRegion(config.getRegion()); - answer.setDefaultCredentialsProvider(config.isDefaultCredentialsProvider()); - answer.setProfileCredentialsProvider(config.isProfileCredentialsProvider()); - answer.setProfileName(config.getProfileName()); - answer.setRefreshEnabled(config.isRefreshEnabled()); - answer.setRefreshPeriod(config.getRefreshPeriod()); - answer.setSecrets(config.getSecrets()); - answer.setUseSqsNotification(config.isUseSqsNotification()); - answer.setSqsQueueUrl(config.getSqsQueueUrl()); - answer.setUriEndpointOverride(config.getUriEndpointOverride()); - answer.setOverrideEndpoint(config.isOverrideEndpoint()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AzureVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AzureVaultAutoConfiguration.java index 4e687470443..c7c859a8d80 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AzureVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/AzureVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.AzureVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,18 +32,7 @@ public class AzureVaultAutoConfiguration { @Bean public AzureVaultConfiguration azureVaultConfiguration(AzureVaultConfigurationProperties config) { AzureVaultConfiguration answer = new AzureVaultConfiguration(); - answer.setClientId(config.getClientId()); - answer.setClientSecret(config.getClientSecret()); - answer.setVaultName(config.getVaultName()); - answer.setTenantId(config.getTenantId()); - answer.setAzureIdentityEnabled(config.isAzureIdentityEnabled()); - answer.setRefreshEnabled(config.isRefreshEnabled()); - answer.setRefreshPeriod(config.getRefreshPeriod()); - answer.setSecrets(config.getSecrets()); - answer.setEventhubConnectionString(config.getEventhubConnectionString()); - answer.setBlobAccessKey(config.getBlobAccessKey()); - answer.setBlobAccountName(config.getBlobAccountName()); - answer.setBlobContainerName(config.getBlobContainerName()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/CyberArkVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/CyberArkVaultAutoConfiguration.java index 7055aad6da2..4d86ee4c236 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/CyberArkVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/CyberArkVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.CyberArkVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,15 +32,7 @@ public class CyberArkVaultAutoConfiguration { @Bean public CyberArkVaultConfiguration cyberarkVaultConfiguration(CyberArkVaultConfigurationProperties config) { CyberArkVaultConfiguration answer = new CyberArkVaultConfiguration(); - answer.setUrl(config.getUrl()); - answer.setAccount(config.getAccount()); - answer.setUsername(config.getUsername()); - answer.setPassword(config.getPassword()); - answer.setApiKey(config.getApiKey()); - answer.setAuthToken(config.getAuthToken()); - answer.setVerifySsl(config.isVerifySsl()); - answer.setCertificatePath(config.getCertificatePath()); - answer.setSecrets(config.getSecrets()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/GcpVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/GcpVaultAutoConfiguration.java index f13e654fb2f..260e2fcd074 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/GcpVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/GcpVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.GcpVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,13 +32,7 @@ public class GcpVaultAutoConfiguration { @Bean public GcpVaultConfiguration gcpVaultConfiguration(GcpVaultConfigurationProperties config) { GcpVaultConfiguration answer = new GcpVaultConfiguration(); - answer.setServiceAccountKey(config.getServiceAccountKey()); - answer.setProjectId(config.getProjectId()); - answer.setUseDefaultInstance(config.isUseDefaultInstance()); - answer.setRefreshEnabled(config.isRefreshEnabled()); - answer.setRefreshPeriod(config.getRefreshPeriod()); - answer.setSecrets(config.getSecrets()); - answer.setSubscriptionName(config.getSubscriptionName()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/HashicorpVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/HashicorpVaultAutoConfiguration.java index 1165a650510..603ac58d170 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/HashicorpVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/HashicorpVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.HashicorpVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,15 +32,7 @@ public class HashicorpVaultAutoConfiguration { @Bean public HashicorpVaultConfiguration hashicorpVaultConfiguration(HashicorpVaultConfigurationProperties config) { HashicorpVaultConfiguration answer = new HashicorpVaultConfiguration(); - answer.setToken(config.getToken()); - answer.setHost(config.getHost()); - answer.setPort(config.getPort()); - answer.setScheme(config.getScheme()); - answer.setCloud(config.isCloud()); - answer.setNamespace(config.getNamespace()); - answer.setRefreshEnabled(config.isRefreshEnabled()); - answer.setRefreshPeriod(config.getRefreshPeriod()); - answer.setSecrets(config.getSecrets()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultAutoConfiguration.java index 5ed5263b803..edca703c5d8 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.IBMSecretsManagerVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,14 +32,7 @@ public class IBMVaultAutoConfiguration { @Bean public IBMSecretsManagerVaultConfiguration ibmSecretsManagerVaultConfiguration(IBMVaultConfigurationProperties config) { IBMSecretsManagerVaultConfiguration answer = new IBMSecretsManagerVaultConfiguration(); - answer.setToken(config.getToken()); - answer.setServiceUrl(config.getServiceUrl()); - answer.setEventStreamTopic(config.getEventStreamTopic()); - answer.setEventStreamBootstrapServers(config.getEventStreamBootstrapServers()); - answer.setEventStreamUsername(config.getEventStreamUsername()); - answer.setEventStreamPassword(config.getEventStreamPassword()); - answer.setEventStreamGroupId(config.getEventStreamGroupId()); - answer.setEventStreamConsumerPollTimeout(config.getEventStreamConsumerPollTimeout()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultConfigurationProperties.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultConfigurationProperties.java index c637c5ed5a5..51cbfcb4434 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultConfigurationProperties.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/IBMVaultConfigurationProperties.java @@ -63,6 +63,32 @@ public class IBMVaultConfigurationProperties { */ private long eventStreamConsumerPollTimeout = 3000; + /** + * Whether to automatically refresh the secrets on update + */ + private boolean refreshEnabled; + + /** + * Define the secrets to look at + */ + private String secrets; + + public boolean isRefreshEnabled() { + return refreshEnabled; + } + + public void setRefreshEnabled(boolean refreshEnabled) { + this.refreshEnabled = refreshEnabled; + } + + public String getSecrets() { + return secrets; + } + + public void setSecrets(String secrets) { + this.secrets = secrets; + } + public String getToken() { return token; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesConfigMapVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesConfigMapVaultAutoConfiguration.java index 8f25ba71d90..44e8d1fcf9a 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesConfigMapVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesConfigMapVaultAutoConfiguration.java @@ -19,6 +19,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.KubernetesConfigMapVaultConfiguration; import org.apache.camel.vault.KubernetesVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -32,8 +33,7 @@ public class KubernetesConfigMapVaultAutoConfiguration { @Bean public KubernetesConfigMapVaultConfiguration kubernetesConfigMapVaultConfiguration(KubernetesConfigMapVaultConfigurationProperties config) { KubernetesConfigMapVaultConfiguration answer = new KubernetesConfigMapVaultConfiguration(); - answer.setRefreshEnabled(config.isRefreshEnabled()); - answer.setConfigmaps(config.getConfigmaps()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesVaultAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesVaultAutoConfiguration.java index e211914765a..fcb5d4faec7 100644 --- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesVaultAutoConfiguration.java +++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/vault/KubernetesVaultAutoConfiguration.java @@ -18,6 +18,7 @@ package org.apache.camel.spring.boot.vault; import org.apache.camel.spring.boot.CamelAutoConfiguration; import org.apache.camel.vault.KubernetesVaultConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,8 +32,7 @@ public class KubernetesVaultAutoConfiguration { @Bean public KubernetesVaultConfiguration kubernetesVaultConfiguration(KubernetesVaultConfigurationProperties config) { KubernetesVaultConfiguration answer = new KubernetesVaultConfiguration(); - answer.setRefreshEnabled(config.isRefreshEnabled()); - answer.setSecrets(config.getSecrets()); + BeanUtils.copyProperties(config, answer); return answer; } diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/vault/VaultConfigurationPropertiesMappingTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/vault/VaultConfigurationPropertiesMappingTest.java new file mode 100644 index 00000000000..c83ead470d9 --- /dev/null +++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/vault/VaultConfigurationPropertiesMappingTest.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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 org.apache.camel.spring.boot.vault; + +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.camel.vault.AwsVaultConfiguration; +import org.apache.camel.vault.AzureVaultConfiguration; +import org.apache.camel.vault.CyberArkVaultConfiguration; +import org.apache.camel.vault.GcpVaultConfiguration; +import org.apache.camel.vault.HashicorpVaultConfiguration; +import org.apache.camel.vault.IBMSecretsManagerVaultConfiguration; +import org.apache.camel.vault.KubernetesConfigMapVaultConfiguration; +import org.apache.camel.vault.KubernetesVaultConfiguration; +import org.apache.camel.vault.VaultConfiguration; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * Guards against drift between the Camel core vault configurations and the corresponding Spring Boot + * {@code camel.vault.*} configuration properties classes. + * + * The vault auto-configurations copy the Spring Boot properties onto the Camel configuration with + * {@link org.springframework.beans.BeanUtils#copyProperties(Object, Object)}, which matches properties by name and + * type. When a vault option is added to Camel core, this test fails until the matching property (same name and type) + * is added to the starter properties class, so the option stays configurable from Spring Boot. + */ +public class VaultConfigurationPropertiesMappingTest { + + static Stream<Arguments> vaultPairs() { + return Stream.of( + arguments(AwsVaultConfiguration.class, AwsVaultConfigurationProperties.class), + arguments(AzureVaultConfiguration.class, AzureVaultConfigurationProperties.class), + arguments(GcpVaultConfiguration.class, GcpVaultConfigurationProperties.class), + arguments(HashicorpVaultConfiguration.class, HashicorpVaultConfigurationProperties.class), + arguments(KubernetesVaultConfiguration.class, KubernetesVaultConfigurationProperties.class), + arguments(KubernetesConfigMapVaultConfiguration.class, + KubernetesConfigMapVaultConfigurationProperties.class), + arguments(IBMSecretsManagerVaultConfiguration.class, IBMVaultConfigurationProperties.class), + arguments(CyberArkVaultConfiguration.class, CyberArkVaultConfigurationProperties.class)); + } + + @ParameterizedTest + @MethodSource("vaultPairs") + void starterExposesAllCamelVaultOptions(Class<?> camelConfiguration, Class<?> starterProperties) throws Exception { + Map<String, PropertyDescriptor> starter = new HashMap<>(); + for (PropertyDescriptor pd : Introspector.getBeanInfo(starterProperties, Object.class) + .getPropertyDescriptors()) { + starter.put(pd.getName(), pd); + } + + List<String> missing = new ArrayList<>(); + // only the options declared on the concrete configuration class, not the + // per-provider sub-configurations inherited from VaultConfiguration + for (PropertyDescriptor pd : Introspector.getBeanInfo(camelConfiguration, VaultConfiguration.class) + .getPropertyDescriptors()) { + if (pd.getWriteMethod() == null || pd.getReadMethod() == null) { + continue; + } + PropertyDescriptor sp = starter.get(pd.getName()); + if (sp == null || sp.getReadMethod() == null || sp.getWriteMethod() == null + || !sp.getPropertyType().equals(pd.getPropertyType())) { + missing.add(pd.getName() + " (" + pd.getPropertyType().getSimpleName() + ")"); + } + } + + assertTrue(missing.isEmpty(), + () -> camelConfiguration.getSimpleName() + " options not configurable via " + + starterProperties.getSimpleName() + " (add the missing properties): " + missing); + } + +}
