This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 524473d66e78 [backport camel-4.22.x] CAMEL-24776: camel-jbang -
auto-download secret properties function deps on run, not only export (#26527)
524473d66e78 is described below
commit 524473d66e7839c77d370f4e9bdd1b06ae887349
Author: Guillaume Nodet - AI Bot <[email protected]>
AuthorDate: Thu Sep 17 07:46:11 2026 +0200
[backport camel-4.22.x] CAMEL-24776: camel-jbang - auto-download secret
properties function deps on run, not only export (#26527)
Co-authored-by: gnodet-bot <[email protected]>
---
...ndencyDownloaderPropertiesFunctionResolver.java | 11 +-
...cyDownloaderPropertiesFunctionResolverTest.java | 142 +++++++++++++++++++++
2 files changed, 149 insertions(+), 4 deletions(-)
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolver.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolver.java
index 2ea2dfa1e3c7..a2c76adfe842 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolver.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolver.java
@@ -42,9 +42,7 @@ public class DependencyDownloaderPropertiesFunctionResolver
extends DefaultPrope
public PropertiesFunction resolvePropertiesFunction(String name) {
PropertiesFunction answer = null;
- if (export) {
- downloadPropertiesFunctionExport(name);
- } else if (transform) {
+ if (transform) {
if ("boolean".equals(name)) {
// ensure boolean function can fallback and return a value as
we just want to transform
answer = new ExportBooleanFunction();
@@ -56,6 +54,11 @@ public class DependencyDownloaderPropertiesFunctionResolver
extends DefaultPrope
answer = new TransformDummyFunction(name);
addPropertiesFunction(answer);
}
+ } else {
+ // for both run and export we must auto-download the JAR that
backs the properties
+ // function (e.g. camel-kubernetes for secret/configmap) so the
function can be resolved;
+ // otherwise the placeholder is silently parsed as a key with a
default value (CAMEL-24776)
+ downloadPropertiesFunctionDependency(name);
}
if (answer == null) {
@@ -67,7 +70,7 @@ public class DependencyDownloaderPropertiesFunctionResolver
extends DefaultPrope
return answer;
}
- private void downloadPropertiesFunctionExport(String name) {
+ private void downloadPropertiesFunctionDependency(String name) {
DependencyDownloader downloader =
getCamelContext().hasService(DependencyDownloader.class);
if ("base64".equals(name)) {
diff --git
a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolverTest.java
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolverTest.java
new file mode 100644
index 000000000000..3ec98bd9517e
--- /dev/null
+++
b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderPropertiesFunctionResolverTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.main.download;
+
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.impl.engine.SimpleCamelContext;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Regression test for CAMEL-24776: a normal run (not only export) must
auto-download the JAR that backs a secret
+ * properties function - e.g. camel-kubernetes for {@code {{secret:...}}} and
{@code {{configmap:...}}} - so the
+ * function can be resolved. Otherwise the placeholder is silently parsed as a
key with a default value and resolves to
+ * the wrong literal.
+ */
+public class DependencyDownloaderPropertiesFunctionResolverTest {
+
+ private static final String KUBERNETES =
"org.apache.camel:camel-kubernetes";
+ private static final String BASE64 = "org.apache.camel:camel-base64";
+ private static final String AWS =
"org.apache.camel:camel-aws-secrets-manager";
+ private static final String AZURE =
"org.apache.camel:camel-azure-key-vault";
+ private static final String GCP =
"org.apache.camel:camel-google-secret-manager";
+ private static final String HASHICORP =
"org.apache.camel:camel-hashicorp-vault";
+
+ @Test
+ void runDownloadsKubernetesForSecretFunction() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"secret").contains(KUBERNETES),
+ "run must auto-download camel-kubernetes for the secret
function");
+ }
+
+ @Test
+ void runDownloadsKubernetesForConfigmapFunction() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"configmap").contains(KUBERNETES),
+ "run must auto-download camel-kubernetes for the configmap
function");
+ }
+
+ @Test
+ void runDownloadsCamelBase64ForBase64Function() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"base64").contains(BASE64),
+ "run must auto-download camel-base64 for the base64 function");
+ }
+
+ @Test
+ void runDownloadsAwsSecretsManagerForAwsFunction() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"aws").contains(AWS),
+ "run must auto-download camel-aws-secrets-manager for the aws
function");
+ }
+
+ @Test
+ void runDownloadsAzureKeyVaultForAzureFunction() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"azure").contains(AZURE),
+ "run must auto-download camel-azure-key-vault for the azure
function");
+ }
+
+ @Test
+ void runDownloadsGoogleSecretManagerForGcpFunction() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"gcp").contains(GCP),
+ "run must auto-download camel-google-secret-manager for the
gcp function");
+ }
+
+ @Test
+ void runDownloadsHashicorpVaultForHashicorpFunction() {
+ assertTrue(resolveAndRecordDownloads(false, false,
"hashicorp").contains(HASHICORP),
+ "run must auto-download camel-hashicorp-vault for the
hashicorp function");
+ }
+
+ @Test
+ void exportStillDownloadsKubernetesForSecretFunction() {
+ assertTrue(resolveAndRecordDownloads(true, false,
"secret").contains(KUBERNETES),
+ "export must keep auto-downloading camel-kubernetes for the
secret function");
+ }
+
+ @Test
+ void transformStubsWithoutDownloading() {
+ assertEquals(List.of(), resolveAndRecordDownloads(false, true,
"secret"),
+ "transform must stub the function without downloading any
dependency");
+ }
+
+ private static List<String> resolveAndRecordDownloads(boolean export,
boolean transform, String function) {
+ List<String> downloaded = new ArrayList<>();
+ try (SimpleCamelContext context = new SimpleCamelContext()) {
+ context.addService(recordingDownloader(context, downloaded));
+
+ DependencyDownloaderPropertiesFunctionResolver resolver
+ = new
DependencyDownloaderPropertiesFunctionResolver(context, export, transform);
+ // the backing dependency is not on the test classpath, so the
function itself will not
+ // resolve; we only assert whether the download was attempted
+ resolver.resolvePropertiesFunction(function);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return downloaded;
+ }
+
+ /**
+ * A {@link DependencyDownloader} test double that records {@code
downloadDependency(groupId,
+ * artifactId, version)} calls and reports nothing as already on the
classpath, so the download branch is exercised.
+ * Implemented as a dynamic proxy to avoid stubbing the whole downloader
SPI.
+ */
+ private static DependencyDownloader recordingDownloader(SimpleCamelContext
context, List<String> downloaded) {
+ return (DependencyDownloader) Proxy.newProxyInstance(
+
DependencyDownloaderPropertiesFunctionResolverTest.class.getClassLoader(),
+ new Class[] { DependencyDownloader.class },
+ (proxy, method, args) -> {
+ switch (method.getName()) {
+ case "downloadDependency" -> {
+ // every overload starts with (String groupId,
String artifactId, String version, ...)
+ downloaded.add(args[0] + ":" + args[1]);
+ return null;
+ }
+ case "alreadyOnClasspath" -> {
+ return false;
+ }
+ case "getCamelContext" -> {
+ return context;
+ }
+ default -> {
+ return method.getReturnType() == boolean.class ?
Boolean.FALSE : null;
+ }
+ }
+ });
+ }
+}