This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new b40c2e3c5b33 CAMEL-26617: camel-azure-key-vault - run the integration 
tests on Floci AZ (#29237)
b40c2e3c5b33 is described below

commit b40c2e3c5b3356563f4edd6ea26df3f2a0864a15
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Nov 5 16:50:26 2026 +0100

    CAMEL-26617: camel-azure-key-vault - run the integration tests on Floci AZ 
(#29237)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../apache/camel/catalog/test-infra/metadata.json  |  11 ++
 .../camel-azure/camel-azure-key-vault/pom.xml      |   9 ++
 .../operations/KeyVaultOperationsIT.java           | 168 +++++++++++++++++++++
 .../operations/KeyVaultOperationsTest.java         | 110 --------------
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   8 -
 test-infra/camel-test-infra-all/pom.xml            |  11 ++
 .../src/generated/resources/META-INF/metadata.json |  11 ++
 .../camel-test-infra-azure-key-vault}/pom.xml      |  64 +++-----
 .../azure/key/vault/AzureKeyVaultConfigs.java      |  33 ++++
 .../vault/clients/AzureKeyVaultClientUtils.java    |  97 ++++++++++++
 .../vault/services/AzureKeyVaultInfraService.java  |  37 +++++
 .../AzureKeyVaultLocalContainerInfraService.java   |  46 ++++++
 .../services/AzureKeyVaultRemoteInfraService.java  |  51 +++++++
 .../key/vault/services/AzureKeyVaultService.java   |  27 ++++
 .../services/AzureKeyVaultServiceFactory.java      |  83 ++++++++++
 .../src/main/resources/META-INF/MANIFEST.MF        |   0
 test-infra/pom.xml                                 |   1 +
 17 files changed, 606 insertions(+), 161 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
index aced56b21e4f..e1f97d5309cd 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
@@ -284,6 +284,17 @@
   "version" : "4.24.0-SNAPSHOT",
   "serviceVersion" : "2026.5",
   "uiSupported" : false
+}, {
+  "service" : 
"org.apache.camel.test.infra.azure.key.vault.services.AzureKeyVaultInfraService",
+  "description" : "Local Azure Key Vault service with Floci AZ",
+  "implementation" : 
"org.apache.camel.test.infra.azure.key.vault.services.AzureKeyVaultLocalContainerInfraService",
+  "alias" : [ "azure" ],
+  "aliasImplementation" : [ "key-vault" ],
+  "groupId" : "org.apache.camel",
+  "artifactId" : "camel-test-infra-azure-key-vault",
+  "version" : "4.24.0-SNAPSHOT",
+  "serviceVersion" : "0.16.0",
+  "uiSupported" : false
 }, {
   "service" : 
"org.apache.camel.test.infra.openfga.services.OpenFgaInfraService",
   "description" : "OpenFGA, a relationship-based fine-grained authorization 
engine",
diff --git a/components/camel-azure/camel-azure-key-vault/pom.xml 
b/components/camel-azure/camel-azure-key-vault/pom.xml
index cfcc9d852c0e..75418fd39b16 100644
--- a/components/camel-azure/camel-azure-key-vault/pom.xml
+++ b/components/camel-azure/camel-azure-key-vault/pom.xml
@@ -33,6 +33,9 @@
     <description>Camel Azure Key Vault Component</description>
 
     <properties>
+        <!-- Floci AZ is not available on these platforms -->
+        <skipITs.ppc64le>true</skipITs.ppc64le>
+        <skipITs.s390x>true</skipITs.s390x>
     </properties>
 
     <dependencies>
@@ -80,5 +83,11 @@
             <artifactId>camel-test-junit6</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-azure-key-vault</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/components/camel-azure/camel-azure-key-vault/src/test/java/org/apache/camel/component/azure/key/vault/integration/operations/KeyVaultOperationsIT.java
 
b/components/camel-azure/camel-azure-key-vault/src/test/java/org/apache/camel/component/azure/key/vault/integration/operations/KeyVaultOperationsIT.java
new file mode 100644
index 000000000000..0cbaa03aa4b9
--- /dev/null
+++ 
b/components/camel-azure/camel-azure-key-vault/src/test/java/org/apache/camel/component/azure/key/vault/integration/operations/KeyVaultOperationsIT.java
@@ -0,0 +1,168 @@
+/*
+ * 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.component.azure.key.vault.integration.operations;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import com.azure.core.exception.HttpResponseException;
+import com.azure.security.keyvault.secrets.SecretClient;
+import com.azure.security.keyvault.secrets.models.DeletedSecret;
+import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
+import com.azure.security.keyvault.secrets.models.SecretProperties;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.azure.key.vault.KeyVaultConstants;
+import org.apache.camel.test.infra.azure.key.vault.AzureKeyVaultConfigs;
+import 
org.apache.camel.test.infra.azure.key.vault.clients.AzureKeyVaultClientUtils;
+import 
org.apache.camel.test.infra.azure.key.vault.services.AzureKeyVaultService;
+import 
org.apache.camel.test.infra.azure.key.vault.services.AzureKeyVaultServiceFactory;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * The operations of the producer against the Key Vault of a Floci AZ 
container, or against a real vault when its name
+ * and a service principal are given:
+ * {@code mvn verify -DvaultName=myvault -DclientId=... -DclientSecret=... 
-DtenantId=...}. The test purges the secret
+ * that it deletes: the real vault must not have purge protection, and the 
service principal must be allowed to purge.
+ */
+class KeyVaultOperationsIT extends CamelTestSupport {
+
+    @RegisterExtension
+    static AzureKeyVaultService service = createService();
+
+    // a name of its own: a real vault keeps a deleted secret for days, and 
refuses to create another one with its name
+    private final String secretName = "camel-it-" + UUID.randomUUID();
+
+    private SecretClient secretClient;
+
+    private static AzureKeyVaultService createService() {
+        if (isSet(AzureKeyVaultConfigs.VAULT_NAME) && 
isSet(AzureKeyVaultConfigs.CLIENT_ID)
+                && isSet(AzureKeyVaultConfigs.CLIENT_SECRET) && 
isSet(AzureKeyVaultConfigs.TENANT_ID)) {
+            System.setProperty("azure.instance.type", "remote");
+        }
+        return AzureKeyVaultServiceFactory.createService();
+    }
+
+    private static boolean isSet(String property) {
+        String value = System.getProperty(property);
+        return value != null && !value.isBlank();
+    }
+
+    private static boolean isRemote() {
+        return "remote".equals(System.getProperty("azure.instance.type"));
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        secretClient = AzureKeyVaultClientUtils.getClient();
+        // on a real vault the endpoints build their client from the service 
principal: a client in the registry would
+        // be autowired in its place
+        if (!isRemote()) {
+            context.getRegistry().bind("secretClient", secretClient);
+        }
+        return context;
+    }
+
+    @Test
+    void createGetUpdateDeleteAndPurgeSecret() {
+        Exchange created = template.send("direct:createSecret", exchange -> {
+            exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, 
secretName);
+            exchange.getMessage().setBody("TestValue");
+        });
+        assertNull(created.getException());
+        KeyVaultSecret secret = assertInstanceOf(KeyVaultSecret.class, 
created.getMessage().getBody());
+        assertEquals(secretName, secret.getName());
+        assertEquals("TestValue", secret.getValue());
+
+        Exchange read = template.send("direct:getSecret",
+                exchange -> 
exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, secretName));
+        assertNull(read.getException());
+        assertEquals("TestValue", read.getMessage().getBody());
+
+        SecretProperties properties = secret.getProperties()
+                .setContentType("text/plain")
+                .setTags(Map.of("owner", "camel"));
+        Exchange updated = template.send("direct:updateSecretProperties",
+                exchange -> 
exchange.getMessage().setHeader(KeyVaultConstants.SECRET_PROPERTIES, 
properties));
+        assertNull(updated.getException());
+        SecretProperties updatedProperties = 
assertInstanceOf(SecretProperties.class, updated.getMessage().getBody());
+        assertEquals(secretName, updatedProperties.getName());
+        assertEquals("text/plain", updatedProperties.getContentType());
+        assertEquals(Map.of("owner", "camel"), updatedProperties.getTags());
+
+        Exchange deleted = template.send("direct:deleteSecret",
+                exchange -> 
exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, secretName));
+        assertNull(deleted.getException());
+        DeletedSecret deletedSecret = assertInstanceOf(DeletedSecret.class, 
deleted.getMessage().getBody());
+        assertEquals(secretName, deletedSecret.getName());
+        assertNotNull(deletedSecret.getRecoveryId());
+        assertNotNull(deletedSecret.getDeletedOn());
+        assertNotNull(deletedSecret.getScheduledPurgeDate());
+
+        Exchange missing = template.send("direct:getSecret",
+                exchange -> 
exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, secretName));
+        assertEquals(404,
+                assertInstanceOf(HttpResponseException.class, 
missing.getException()).getResponse().getStatusCode());
+
+        Exchange purged = template.send("direct:purgeDeletedSecret",
+                exchange -> 
exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, secretName));
+        assertNull(purged.getException());
+        // a real vault purges the deleted secret in the background
+        await().atMost(1, TimeUnit.MINUTES)
+                .untilAsserted(() -> assertEquals(404,
+                        assertThrows(HttpResponseException.class, () -> 
secretClient.getDeletedSecret(secretName))
+                                .getResponse().getStatusCode()));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:createSecret").to(keyVault("createSecret"));
+                from("direct:getSecret").to(keyVault("getSecret"));
+                
from("direct:updateSecretProperties").to(keyVault("updateSecretProperties"));
+                from("direct:deleteSecret").to(keyVault("deleteSecret"));
+                
from("direct:purgeDeletedSecret").to(keyVault("purgeDeletedSecret"));
+            }
+        };
+    }
+
+    /*
+     * On Floci AZ the endpoints use the client of the test-infra, through the 
secretClient option: the client that the
+     * component builds reaches only https://<vaultName>.vault.azure.net. On a 
real vault they use the service
+     * principal, as before, so that the component builds its client.
+     */
+    private static String keyVault(String operation) {
+        String client = isRemote()
+                ? 
"clientId=RAW({{clientId}})&clientSecret=RAW({{clientSecret}})&tenantId=RAW({{tenantId}})"
+                : "secretClient=#secretClient";
+        return "azure-key-vault://" + service.vaultName() + "?" + client + 
"&operation=" + operation;
+    }
+}
diff --git 
a/components/camel-azure/camel-azure-key-vault/src/test/java/org/apache/camel/component/azure/key/vault/integration/operations/KeyVaultOperationsTest.java
 
b/components/camel-azure/camel-azure-key-vault/src/test/java/org/apache/camel/component/azure/key/vault/integration/operations/KeyVaultOperationsTest.java
deleted file mode 100644
index e185592247ff..000000000000
--- 
a/components/camel-azure/camel-azure-key-vault/src/test/java/org/apache/camel/component/azure/key/vault/integration/operations/KeyVaultOperationsTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.component.azure.key.vault.integration.operations;
-
-import org.apache.camel.EndpointInject;
-import org.apache.camel.Exchange;
-import org.apache.camel.ExchangePattern;
-import org.apache.camel.Processor;
-import org.apache.camel.ProducerTemplate;
-import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.azure.key.vault.KeyVaultConstants;
-import org.apache.camel.component.mock.MockEndpoint;
-import org.apache.camel.test.junit6.CamelTestSupport;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
-
-@EnabledIfSystemProperty(named = "vaultName", matches = ".*",
-                         disabledReason = "Make sure to supply azure key vault 
Vault Name, e.g:  mvn verify -DvaultName=string")
-@EnabledIfSystemProperty(named = "clientId", matches = ".*",
-                         disabledReason = "Make sure to supply azure key vault 
Client Id, e.g:  mvn verify -DclientId=string")
-@EnabledIfSystemProperty(named = "clientSecret", matches = ".*",
-                         disabledReason = "Make sure to supply azure key vault 
Client Secret, e.g:  mvn verify -DclientSecret=string")
-@EnabledIfSystemProperty(named = "tenantId", matches = ".*",
-                         disabledReason = "Make sure to supply azure key vault 
Tenant Id, e.g:  mvn verify -DtenantId=string")
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class KeyVaultOperationsTest extends CamelTestSupport {
-
-    @EndpointInject("direct:createSecret")
-    private ProducerTemplate template;
-
-    @EndpointInject("direct:getSecret")
-    private ProducerTemplate getTemplate;
-
-    @EndpointInject("direct:deleteSecret")
-    private ProducerTemplate deleteTemplate;
-
-    @EndpointInject("mock:createSecret")
-    private MockEndpoint createResult;
-
-    @EndpointInject("mock:getSecret")
-    private MockEndpoint getResult;
-
-    @EndpointInject("mock:deleteSecret")
-    private MockEndpoint deleteResult;
-
-    @Test
-    public void sendInOnly() throws Exception {
-        createResult.expectedMessageCount(1);
-        getResult.expectedMessageCount(1);
-        getResult.expectedBodiesReceived("TestValue");
-        deleteResult.expectedMessageCount(1);
-
-        template.send("direct:createSecret", ExchangePattern.InOnly, new 
Processor() {
-            public void process(Exchange exchange) {
-                exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, 
"Test2");
-                exchange.getIn().setBody("TestValue");
-            }
-        });
-
-        template.send("direct:getSecret", ExchangePattern.InOnly, new 
Processor() {
-            public void process(Exchange exchange) {
-                exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, 
"Test2");
-            }
-        });
-
-        template.send("direct:deleteSecret", ExchangePattern.InOnly, new 
Processor() {
-            public void process(Exchange exchange) {
-                exchange.getMessage().setHeader(KeyVaultConstants.SECRET_NAME, 
"Test2");
-            }
-        });
-
-        MockEndpoint.assertIsSatisfied(context);
-    }
-
-    @Override
-    protected RouteBuilder createRouteBuilder() {
-
-        return new RouteBuilder() {
-            @Override
-            public void configure() {
-                from("direct:createSecret")
-                        
.to("azure-key-vault://{{vaultName}}?clientId=RAW({{clientId}})&clientSecret=RAW({{clientSecret}})&tenantId=RAW({{tenantId}})")
-                        .to("mock:createSecret");
-
-                from("direct:getSecret")
-                        
.to("azure-key-vault://{{vaultName}}?clientId=RAW({{clientId}})&clientSecret=RAW({{clientSecret}})&tenantId=RAW({{tenantId}})&operation=getSecret")
-                        .to("mock:getSecret");
-
-                from("direct:deleteSecret")
-                        
.to("azure-key-vault://{{vaultName}}?clientId=RAW({{clientId}})&clientSecret=RAW({{clientSecret}})&tenantId=RAW({{tenantId}})&operation=deleteSecret")
-                        .to("mock:deleteSecret");
-            }
-        };
-    }
-}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index eb8f5d08a5f7..f4f06187c8a7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1379,14 +1379,6 @@ the initial state (`camel.health.initial-state`, DOWN by 
default) while the firs
 the change feed processor cannot start, with the failure of the last attempt; 
before, it was UP. To leave
 the check out, as before, set 
`camel.health.exclude-pattern=consumer:<routeId>`.
 
-=== camel-test-infra-azure - the azure.container property holds the Floci AZ 
image
-
-The Azure test-infra runs its new local Data Lake Storage Gen2 service (`camel 
infra run azure storage-datalake`) on
-https://github.com/floci-io/floci-az[Floci AZ], and the `azure.container` 
property of `camel-test-infra-azure-common`
-now holds the Floci AZ image. The Blob and Queue services still run on 
Azurite, whose image is now set with the
-`azure.storage.blob.container` and `azure.storage.queue.container` properties: 
to run them on another Azurite image,
-set these instead of `azure.container`.
-
 === camel-vertx-websocket - consumers on the same host and port must use the 
same SSLContextParameters
 
 All `vertx-websocket` consumers on the same host and port are served by one 
HTTP server, which is created from the
diff --git a/test-infra/camel-test-infra-all/pom.xml 
b/test-infra/camel-test-infra-all/pom.xml
index 548404b3fb89..950bfdf0a4ef 100644
--- a/test-infra/camel-test-infra-all/pom.xml
+++ b/test-infra/camel-test-infra-all/pom.xml
@@ -71,6 +71,11 @@
             <artifactId>camel-test-infra-rabbitmq</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-azure-key-vault</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-test-infra-azure-storage-blob</artifactId>
@@ -418,6 +423,12 @@
                                         
<artifactId>camel-test-infra-rabbitmq</artifactId>
                                     </dependency>
                                 </fileSet>
+                                <fileSet>
+                                    <dependency>
+                                        <groupId>org.apache.camel</groupId>
+                                        
<artifactId>camel-test-infra-azure-key-vault</artifactId>
+                                    </dependency>
+                                </fileSet>
                                 <fileSet>
                                     <dependency>
                                         <groupId>org.apache.camel</groupId>
diff --git 
a/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
 
b/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
index aced56b21e4f..e1f97d5309cd 100644
--- 
a/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
+++ 
b/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
@@ -284,6 +284,17 @@
   "version" : "4.24.0-SNAPSHOT",
   "serviceVersion" : "2026.5",
   "uiSupported" : false
+}, {
+  "service" : 
"org.apache.camel.test.infra.azure.key.vault.services.AzureKeyVaultInfraService",
+  "description" : "Local Azure Key Vault service with Floci AZ",
+  "implementation" : 
"org.apache.camel.test.infra.azure.key.vault.services.AzureKeyVaultLocalContainerInfraService",
+  "alias" : [ "azure" ],
+  "aliasImplementation" : [ "key-vault" ],
+  "groupId" : "org.apache.camel",
+  "artifactId" : "camel-test-infra-azure-key-vault",
+  "version" : "4.24.0-SNAPSHOT",
+  "serviceVersion" : "0.16.0",
+  "uiSupported" : false
 }, {
   "service" : 
"org.apache.camel.test.infra.openfga.services.OpenFgaInfraService",
   "description" : "OpenFGA, a relationship-based fine-grained authorization 
engine",
diff --git a/components/camel-azure/camel-azure-key-vault/pom.xml 
b/test-infra/camel-test-infra-azure-key-vault/pom.xml
similarity index 52%
copy from components/camel-azure/camel-azure-key-vault/pom.xml
copy to test-infra/camel-test-infra-azure-key-vault/pom.xml
index cfcc9d852c0e..910a4c81cbbb 100644
--- a/components/camel-azure/camel-azure-key-vault/pom.xml
+++ b/test-infra/camel-test-infra-azure-key-vault/pom.xml
@@ -17,36 +17,35 @@
     limitations under the License.
 
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
-    <modelVersion>4.0.0</modelVersion>
-
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <parent>
+        <artifactId>camel-test-infra-parent</artifactId>
         <groupId>org.apache.camel</groupId>
-        <artifactId>camel-azure-parent</artifactId>
+        <relativePath>../camel-test-infra-parent/pom.xml</relativePath>
         <version>4.24.0-SNAPSHOT</version>
     </parent>
+    <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>camel-azure-key-vault</artifactId>
-    <packaging>jar</packaging>
-
-    <name>Camel :: Azure :: Key Vault</name>
-    <description>Camel Azure Key Vault Component</description>
-
-    <properties>
-    </properties>
+    <name>Camel :: Test Infra :: Azure Key Vault</name>
+    <artifactId>camel-test-infra-azure-key-vault</artifactId>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.azure</groupId>
+                <artifactId>azure-sdk-bom</artifactId>
+                <version>${azure-sdk-bom-version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.camel</groupId>
-            <artifactId>camel-azure-common</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>camel-support</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>camel-util-json</artifactId>
+            <artifactId>camel-test-infra-azure-common</artifactId>
+            <version>${project.version}</version>
         </dependency>
 
         <!-- azure sdk -->
@@ -54,31 +53,10 @@
             <groupId>com.azure</groupId>
             <artifactId>azure-security-keyvault-secrets</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.azure</groupId>
-            <artifactId>azure-messaging-eventhubs</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.azure</groupId>
-            
<artifactId>azure-messaging-eventhubs-checkpointstore-blob</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.azure</groupId>
             <artifactId>azure-identity</artifactId>
         </dependency>
-
-        <!-- extras -->
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>${commons-io-version}</version>
-        </dependency>
-
-        <!-- for testing -->
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>camel-test-junit6</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
+
 </project>
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/AzureKeyVaultConfigs.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/AzureKeyVaultConfigs.java
new file mode 100644
index 000000000000..5c77d79f1f00
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/AzureKeyVaultConfigs.java
@@ -0,0 +1,33 @@
+/*
+ * 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.test.infra.azure.key.vault;
+
+/**
+ * The system properties that give a real vault and the service principal that 
reaches it: the ones that the integration
+ * tests of camel-azure-key-vault have always taken.
+ */
+public final class AzureKeyVaultConfigs {
+    public static final String VAULT_NAME = "vaultName";
+    public static final String CLIENT_ID = "clientId";
+    public static final String CLIENT_SECRET = "clientSecret";
+    public static final String TENANT_ID = "tenantId";
+
+    private AzureKeyVaultConfigs() {
+
+    }
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/clients/AzureKeyVaultClientUtils.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/clients/AzureKeyVaultClientUtils.java
new file mode 100644
index 000000000000..b4c6144f9d53
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/clients/AzureKeyVaultClientUtils.java
@@ -0,0 +1,97 @@
+/*
+ * 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.test.infra.azure.key.vault.clients;
+
+import java.net.URL;
+import java.time.OffsetDateTime;
+
+import com.azure.core.credential.AccessToken;
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.http.HttpPipelineCallContext;
+import com.azure.core.http.HttpRequest;
+import com.azure.core.http.policy.HttpPipelineSyncPolicy;
+import com.azure.identity.ClientSecretCredentialBuilder;
+import com.azure.security.keyvault.secrets.SecretClient;
+import com.azure.security.keyvault.secrets.SecretClientBuilder;
+import org.apache.camel.test.infra.azure.common.AzureConfigs;
+import org.apache.camel.test.infra.azure.key.vault.AzureKeyVaultConfigs;
+import reactor.core.publisher.Mono;
+
+public final class AzureKeyVaultClientUtils {
+
+    // Floci AZ accepts any bearer token
+    private static final TokenCredential FLOCI_AZ_CREDENTIAL
+            = request -> Mono.just(new AccessToken("floci-az", 
OffsetDateTime.now().plusHours(1)));
+
+    private AzureKeyVaultClientUtils() {
+
+    }
+
+    /**
+     * A client for the Key Vault of Floci AZ, or with {@code 
-Dazure.instance.type=remote} for the vault and the
+     * service principal of the {@link AzureKeyVaultConfigs} system properties
+     */
+    public static SecretClient getClient() {
+        String instanceType = System.getProperty("azure.instance.type");
+
+        if (instanceType == null || 
instanceType.equals("local-azure-container")) {
+            return getLocalClient(String.format("https://%s:%s/%s-keyvault";, 
System.getProperty(AzureConfigs.HOST),
+                    System.getProperty(AzureConfigs.PORT), 
System.getProperty(AzureConfigs.ACCOUNT_NAME)));
+        }
+
+        return new SecretClientBuilder()
+                .vaultUrl(String.format("https://%s.vault.azure.net";, 
System.getProperty(AzureKeyVaultConfigs.VAULT_NAME)))
+                .credential(new ClientSecretCredentialBuilder()
+                        
.tenantId(System.getProperty(AzureKeyVaultConfigs.TENANT_ID))
+                        
.clientId(System.getProperty(AzureKeyVaultConfigs.CLIENT_ID))
+                        
.clientSecret(System.getProperty(AzureKeyVaultConfigs.CLIENT_SECRET))
+                        .build())
+                .buildClient();
+    }
+
+    /**
+     * The Key Vault SDK sends a token only to an https address, and only for 
the resource of the 401 challenge of Key
+     * Vault that matches the host of the vault. Floci AZ serves http, and its 
challenge names the resource
+     * {@code https://vault.azure.net}: the client is built for the https 
address of the vault, with a token that Floci
+     * AZ accepts and without the check of the resource, and a policy sends 
its requests over http.
+     */
+    private static SecretClient getLocalClient(String vaultUrl) {
+        return new SecretClientBuilder()
+                .vaultUrl(vaultUrl)
+                .credential(FLOCI_AZ_CREDENTIAL)
+                .addPolicy(new HttpSchemePolicy())
+                .disableChallengeResourceVerification()
+                .buildClient();
+    }
+
+    /**
+     * Sends a request over http. A policy added to the builder comes after 
the credential policy, which then still sees
+     * the https address it requires.
+     */
+    private static final class HttpSchemePolicy extends HttpPipelineSyncPolicy 
{
+        @Override
+        protected void beforeSendingRequest(HttpPipelineCallContext context) {
+            HttpRequest request = context.getHttpRequest();
+
+            URL url = request.getUrl();
+            if ("https".equals(url.getProtocol())) {
+                request.setUrl("http" + 
url.toString().substring("https".length()));
+            }
+        }
+    }
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultInfraService.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultInfraService.java
new file mode 100644
index 000000000000..e114a2832737
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultInfraService.java
@@ -0,0 +1,37 @@
+/*
+ * 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.test.infra.azure.key.vault.services;
+
+import org.apache.camel.test.infra.common.services.InfrastructureService;
+
+/**
+ * An Azure Key Vault: the one of Floci AZ, or a real vault. {@code 
AzureKeyVaultClientUtils} builds a client for it.
+ */
+public interface AzureKeyVaultInfraService extends InfrastructureService {
+
+    /**
+     * The name of the vault
+     */
+    String vaultName();
+
+    /**
+     * The address of the vault, for example {@code 
http://localhost:32768/devstoreaccount1-keyvault} for Floci AZ or
+     * {@code https://myvault.vault.azure.net} for a real vault
+     */
+    String vaultUrl();
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultLocalContainerInfraService.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultLocalContainerInfraService.java
new file mode 100644
index 000000000000..0ceed9840e12
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultLocalContainerInfraService.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test.infra.azure.key.vault.services;
+
+import org.apache.camel.spi.annotations.InfraService;
+import org.apache.camel.test.infra.azure.common.services.FlociAzInfraService;
+
+/**
+ * A local Key Vault: Floci AZ serves the Key Vault of the storage account 
under {@code /<account>-keyvault}, over http.
+ * It accepts any bearer token, and answers a request without one with the 401 
challenge of Key Vault:
+ * {@code AzureKeyVaultClientUtils} builds a client that gets through it.
+ */
+@InfraService(service = AzureKeyVaultInfraService.class,
+              description = "Local Azure Key Vault service with Floci AZ",
+              serviceAlias = "azure", serviceImplementationAlias = "key-vault")
+public class AzureKeyVaultLocalContainerInfraService extends 
FlociAzInfraService implements AzureKeyVaultInfraService {
+
+    /**
+     * The name of the vault, the account: Floci AZ gives the secrets the 
identifiers of
+     * {@code https://<account>.vault.azure.net}
+     */
+    @Override
+    public String vaultName() {
+        return accountName();
+    }
+
+    @Override
+    public String vaultUrl() {
+        return String.format("%s/%s-keyvault", 
getContainer().serviceAddress(), accountName());
+    }
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultRemoteInfraService.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultRemoteInfraService.java
new file mode 100644
index 000000000000..877890a935b3
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultRemoteInfraService.java
@@ -0,0 +1,51 @@
+/*
+ * 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.test.infra.azure.key.vault.services;
+
+import org.apache.camel.test.infra.azure.key.vault.AzureKeyVaultConfigs;
+
+/**
+ * A real vault, named by the {@code vaultName} system property
+ */
+public class AzureKeyVaultRemoteInfraService implements 
AzureKeyVaultInfraService {
+
+    @Override
+    public void registerProperties() {
+
+    }
+
+    @Override
+    public void initialize() {
+        registerProperties();
+    }
+
+    @Override
+    public void shutdown() {
+
+    }
+
+    @Override
+    public String vaultName() {
+        return System.getProperty(AzureKeyVaultConfigs.VAULT_NAME);
+    }
+
+    @Override
+    public String vaultUrl() {
+        return String.format("https://%s.vault.azure.net";, vaultName());
+    }
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultService.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultService.java
new file mode 100644
index 000000000000..00f199aba3ed
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultService.java
@@ -0,0 +1,27 @@
+/*
+ * 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.test.infra.azure.key.vault.services;
+
+import org.apache.camel.test.infra.common.services.ContainerTestService;
+import org.apache.camel.test.infra.common.services.TestService;
+
+/**
+ * Test infra service for Azure Key Vault
+ */
+public interface AzureKeyVaultService extends AzureKeyVaultInfraService, 
TestService, ContainerTestService {
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultServiceFactory.java
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultServiceFactory.java
new file mode 100644
index 000000000000..d6b8c730173c
--- /dev/null
+++ 
b/test-infra/camel-test-infra-azure-key-vault/src/main/java/org/apache/camel/test/infra/azure/key/vault/services/AzureKeyVaultServiceFactory.java
@@ -0,0 +1,83 @@
+/*
+ * 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.test.infra.azure.key.vault.services;
+
+import org.apache.camel.test.infra.common.services.SimpleTestServiceBuilder;
+import org.apache.camel.test.infra.common.services.SingletonService;
+
+/**
+ * Creates the Key Vault of Floci AZ, or with {@code 
-Dazure.instance.type=remote} the vault of the
+ * {@code AzureKeyVaultConfigs} system properties.
+ */
+public final class AzureKeyVaultServiceFactory {
+
+    private static class SingletonAzureKeyVaultService extends 
SingletonService<AzureKeyVaultService>
+            implements AzureKeyVaultService {
+        public SingletonAzureKeyVaultService(AzureKeyVaultService service, 
String name) {
+            super(service, name);
+        }
+
+        @Override
+        public String vaultName() {
+            return getService().vaultName();
+        }
+
+        @Override
+        public String vaultUrl() {
+            return getService().vaultUrl();
+        }
+    }
+
+    private AzureKeyVaultServiceFactory() {
+
+    }
+
+    public static SimpleTestServiceBuilder<AzureKeyVaultService> builder() {
+        return new SimpleTestServiceBuilder<>("azure");
+    }
+
+    public static AzureKeyVaultService createService() {
+        return builder()
+                .addLocalMapping(AzureKeyVaultLocalContainerService::new)
+                .addRemoteMapping(AzureKeyVaultRemoteService::new)
+                .build();
+    }
+
+    public static AzureKeyVaultService createSingletonService() {
+        return SingletonServiceHolder.INSTANCE;
+    }
+
+    private static class SingletonServiceHolder {
+        static final AzureKeyVaultService INSTANCE;
+        static {
+            SimpleTestServiceBuilder<AzureKeyVaultService> instance = 
builder();
+            // a name of its own: the singleton of another Azure service of 
the same JVM must not stand for this one
+            instance.addLocalMapping(
+                    () -> new SingletonAzureKeyVaultService(new 
AzureKeyVaultLocalContainerService(), "azure-key-vault"))
+                    .addRemoteMapping(AzureKeyVaultRemoteService::new);
+            INSTANCE = instance.build();
+        }
+    }
+
+    static class AzureKeyVaultLocalContainerService extends 
AzureKeyVaultLocalContainerInfraService
+            implements AzureKeyVaultService {
+    }
+
+    static class AzureKeyVaultRemoteService extends 
AzureKeyVaultRemoteInfraService implements AzureKeyVaultService {
+    }
+}
diff --git 
a/test-infra/camel-test-infra-azure-key-vault/src/main/resources/META-INF/MANIFEST.MF
 
b/test-infra/camel-test-infra-azure-key-vault/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/test-infra/pom.xml b/test-infra/pom.xml
index 2ffc2452d5e6..d2f2f875c104 100644
--- a/test-infra/pom.xml
+++ b/test-infra/pom.xml
@@ -51,6 +51,7 @@
         <module>camel-test-infra-mongodb</module>
         <module>camel-test-infra-rabbitmq</module>
         <module>camel-test-infra-azure-common</module>
+        <module>camel-test-infra-azure-key-vault</module>
         <module>camel-test-infra-azure-storage-blob</module>
         <module>camel-test-infra-azure-storage-datalake</module>
         <module>camel-test-infra-azure-storage-queue</module>

Reply via email to