This is an automated email from the ASF dual-hosted git repository.
cdeppisch 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 d30f8862c6a CAMEL-21336: Allow Kamelets configured by EnvVars only
d30f8862c6a is described below
commit d30f8862c6ab7444177cf63762c32725cf76dca4
Author: Christoph Deppisch <[email protected]>
AuthorDate: Fri Oct 11 09:33:28 2024 +0200
CAMEL-21336: Allow Kamelets configured by EnvVars only
- Consider environment variables when validating Kamelet parameter
configuration
- Avoids errors due to missing required Kamelet parameter validation when
parameter is configured via environment variables
---
.../apache/camel/component/kamelet/Kamelet.java | 50 ++++++++++++++++++++++
.../camel/component/kamelet/KameletComponent.java | 9 ++++
.../org/apache/camel/RouteTemplateContext.java | 22 ++++++++++
.../java/org/apache/camel/impl/DefaultModel.java | 17 ++++----
.../camel/model/DefaultRouteTemplateContext.java | 27 ++++++++++++
.../main/java/org/apache/camel/util/IOHelper.java | 35 ++++++++-------
6 files changed, 134 insertions(+), 26 deletions(-)
diff --git
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
index 94d94e229cc..da2cdc792b0 100644
---
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
+++
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
@@ -17,6 +17,7 @@
package org.apache.camel.component.kamelet;
import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.function.Predicate;
@@ -30,6 +31,7 @@ import org.apache.camel.spi.PropertiesComponent;
import org.apache.camel.spi.UuidGenerator;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.SimpleUuidGenerator;
+import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
@@ -37,6 +39,7 @@ import static
org.apache.camel.model.ProcessorDefinitionHelper.filterTypeInOutpu
public final class Kamelet {
public static final String PROPERTIES_PREFIX = "camel.kamelet.";
+ public static final String ENV_VAR_PREFIX = "CAMEL_KAMELET_";
public static final String SCHEME = "kamelet";
public static final String SOURCE_ID = "source";
public static final String SINK_ID = "sink";
@@ -128,6 +131,53 @@ public final class Kamelet {
}
}
+ /**
+ * Looking for OS environment variables that match the properties of the
given Kamelet. At first lookup attempt is
+ * made without considering camelCase keys in the elements. The second
lookup is converting camelCase to
+ * underscores.
+ *
+ * For example given an ENV variable in either format: -
CAMEL_KAMELET_AWSS3SOURCE_BUCKETNAMEORARN=myArn -
+ * CAMEL_KAMELET_AWS_S3_SOURCE_BUCKET_NAME_OR_ARN=myArn
+ */
+ public static void extractKameletEnvironmentVariables(Map<String, Object>
properties, String... elements) {
+ StringBuilder prefixBuffer = new StringBuilder(Kamelet.ENV_VAR_PREFIX);
+
+ // Map contains parameter name as key and full environment variable as
value
+ Map<String, String> propertyMappings = new HashMap<>();
+ for (String element : elements) {
+ if (element == null) {
+ continue;
+ }
+
prefixBuffer.append(IOHelper.normalizeEnvironmentVariable(element)).append('_');
+
+ String prefix = prefixBuffer.toString();
+ System.getenv().keySet().stream()
+ .filter(Kamelet.startsWith(prefix))
+ .forEach(name ->
propertyMappings.put(name.substring(prefix.length()), name));
+ }
+
+ prefixBuffer = new StringBuilder(Kamelet.ENV_VAR_PREFIX);
+
+ for (String element : elements) {
+ if (element == null) {
+ continue;
+ }
+
prefixBuffer.append(IOHelper.normalizeEnvironmentVariable(StringHelper.camelCaseToDash(element))).append('_');
+
+ String prefix = prefixBuffer.toString();
+ System.getenv().keySet().stream()
+ .filter(Kamelet.startsWith(prefix))
+ .forEach(name ->
propertyMappings.put(name.substring(prefix.length()), name));
+ }
+
+ for (Map.Entry<String, String> mapping : propertyMappings.entrySet()) {
+ String value = System.getenv(mapping.getValue());
+ if (value != null) {
+ properties.put(mapping.getKey(), value);
+ }
+ }
+ }
+
public static RouteDefinition templateToRoute(RouteTemplateDefinition in,
Map<String, Object> parameters) {
final String rid = (String) parameters.get(PARAM_ROUTE_ID);
final boolean noErrorHandler = (boolean)
parameters.get(NO_ERROR_HANDLER);
diff --git
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
index ace60eda2fe..5e3eb6cc9da 100644
---
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
+++
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
@@ -218,6 +218,15 @@ public class KameletComponent extends DefaultComponent {
Kamelet.extractKameletProperties(getCamelContext(),
kameletProperties, templateId, routeId);
}
+ //
+ // Look for OS environment variables that match the Kamelet
properties
+ // Environment variables are loaded in the following order:
+ //
+ // CAMEL_KAMELET_" + templateId
+ // CAMEL_KAMELET_" + templateId + "_" routeId
+ //
+ Kamelet.extractKameletEnvironmentVariables(kameletProperties,
templateId, routeId);
+
//
// Uri params have the highest precedence
//
diff --git
a/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
b/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
index 35cba9e23c8..75d3c46ac28 100644
--- a/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
@@ -108,6 +108,17 @@ public interface RouteTemplateContext extends
HasCamelContext {
*/
Object getProperty(String name);
+ /**
+ * Gets the environment variable parameter that matches the given property
name. The match is performed by
+ * normalizing the given property name as an OS environment variable name.
The environment variable name may use
+ * pure uppercase or camelCase converted to underscore property names. As
an example bucketNameOrArn property
+ * matches BUCKETNAMEORARN and BUCKET_NAME_OR_ARN environment variables.
+ *
+ * @param name name of property
+ * @return the property value or <tt>null</tt> if no property exists
+ */
+ Object getEnvironmentVariable(String name);
+
/**
* Gets the property with the given name
*
@@ -139,6 +150,17 @@ public interface RouteTemplateContext extends
HasCamelContext {
*/
boolean hasParameter(String name);
+ /**
+ * Whether the route template has an environment variable parameter that
matches the given parameter name. The match
+ * is performed by normalizing the given name as an OS environment
variable name. The environment variable name may
+ * use pure uppercase or camelCase converted to underscore property names.
As an example bucketNameOrArn property
+ * matches BUCKETNAMEORARN and BUCKET_NAME_OR_ARN environment variables.
+ *
+ * @param name the parameter name
+ * @return true if exists
+ */
+ boolean hasEnvironmentVariable(String name);
+
/**
* Gets the local bean repository for the route template when creating the
new route
*/
diff --git
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 8651458a9d3..f167ce5b73a 100644
---
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -460,23 +460,24 @@ public class DefaultModel implements Model {
final Map<String, Object> propDefaultValues = new HashMap<>();
// include default values first from the template (and validate that
we have inputs for all required parameters)
if (target.getTemplateParameters() != null) {
- StringJoiner templatesBuilder = new StringJoiner(", ");
+ StringJoiner missingParameters = new StringJoiner(", ");
for (RouteTemplateParameterDefinition temp :
target.getTemplateParameters()) {
if (temp.getDefaultValue() != null) {
addProperty(prop, temp.getName(), temp.getDefaultValue());
addProperty(propDefaultValues, temp.getName(),
temp.getDefaultValue());
- } else {
- if (temp.isRequired() &&
!routeTemplateContext.hasParameter(temp.getName())) {
- // this is a required parameter which is missing
- templatesBuilder.add(temp.getName());
- }
+ } else if
(routeTemplateContext.hasEnvironmentVariable(temp.getName())) {
+ // property is configured via environment variables
+ addProperty(prop, temp.getName(),
routeTemplateContext.getEnvironmentVariable(temp.getName()));
+ } else if (temp.isRequired() &&
!routeTemplateContext.hasParameter(temp.getName())) {
+ // this is a required parameter which is missing
+ missingParameters.add(temp.getName());
}
}
- if (templatesBuilder.length() > 0) {
+ if (missingParameters.length() > 0) {
throw new IllegalArgumentException(
"Route template " + routeTemplateId + " the following
mandatory parameters must be provided: "
- + templatesBuilder);
+ + missingParameters);
}
}
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/DefaultRouteTemplateContext.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/DefaultRouteTemplateContext.java
index 68f85c0395c..e03c4fe54c5 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/DefaultRouteTemplateContext.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/DefaultRouteTemplateContext.java
@@ -27,6 +27,7 @@ import org.apache.camel.CamelContext;
import org.apache.camel.RouteTemplateContext;
import org.apache.camel.spi.BeanRepository;
import org.apache.camel.support.LocalBeanRegistry;
+import org.apache.camel.util.IOHelper;
import org.apache.camel.util.StringHelper;
/**
@@ -129,6 +130,32 @@ public final class DefaultRouteTemplateContext implements
RouteTemplateContext {
return false;
}
+ @Override
+ public boolean hasEnvironmentVariable(String name) {
+ String normalizedKey = IOHelper.normalizeEnvironmentVariable(name);
+ if (parameters.containsKey(normalizedKey)) {
+ return true;
+ }
+ normalizedKey =
IOHelper.normalizeEnvironmentVariable(StringHelper.camelCaseToDash(name));
+ if (parameters.containsKey(normalizedKey)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Object getEnvironmentVariable(String name) {
+ String normalizedKey = IOHelper.normalizeEnvironmentVariable(name);
+ if (parameters.containsKey(normalizedKey)) {
+ return parameters.get(normalizedKey);
+ }
+ normalizedKey =
IOHelper.normalizeEnvironmentVariable(StringHelper.camelCaseToDash(name));
+ if (parameters.containsKey(normalizedKey)) {
+ return parameters.get(normalizedKey);
+ }
+ return null;
+ }
+
@Override
public BeanRepository getLocalBeanRepository() {
return registry;
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
b/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
index 7cd201a1bbc..de347a45612 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/IOHelper.java
@@ -674,7 +674,7 @@ public final class IOHelper {
* For example given an ENV variable in either format: -
CAMEL_KAMELET_AWS_S3_SOURCE_BUCKETNAMEORARN=myArn -
* CAMEL_KAMELET_AWS_S3_SOURCE_BUCKET_NAME_OR_ARN=myArn
*
- * Then the following keys can lookup both ENV formats above: -
camel.kamelet.awsS3Source.bucketNameOrArn -
+ * Then the following keys can look up both ENV formats above: -
camel.kamelet.awsS3Source.bucketNameOrArn -
* camel.kamelet.aws-s3-source.bucketNameOrArn -
camel.kamelet.aws-s3-source.bucket-name-or-arn
*/
public static String lookupEnvironmentVariable(String key) {
@@ -683,31 +683,30 @@ public final class IOHelper {
String value = System.getenv(upperKey);
if (value == null) {
- // some OS do not support dashes in keys, so replace with
underscore
- String normalizedKey = upperKey.replace('-', '_');
-
- // and replace dots with underscores so keys like my.key are
- // translated to MY_KEY
- normalizedKey = normalizedKey.replace('.', '_');
-
- value = System.getenv(normalizedKey);
+ value = System.getenv(normalizeEnvironmentVariable(upperKey));
}
if (value == null) {
// camelCase keys should use underscore as separator
String caseKey = StringHelper.camelCaseToDash(key);
- caseKey = caseKey.toUpperCase();
- // some OS do not support dashes in keys, so replace with
underscore
- String normalizedKey = caseKey.replace('-', '_');
-
- // and replace dots with underscores so keys like my.key are
- // translated to MY_KEY
- normalizedKey = normalizedKey.replace('.', '_');
-
- value = System.getenv(normalizedKey);
+ value = System.getenv(normalizeEnvironmentVariable(caseKey));
}
return value;
}
+ /**
+ * Convert given key into an OS environment variable. Uses uppercase keys
and converts dashes and dots to
+ * underscores.
+ */
+ public static String normalizeEnvironmentVariable(String key) {
+ String upperKey = key.toUpperCase();
+ // some OS do not support dashes in keys, so replace with underscore
+ String normalizedKey = upperKey.replace('-', '_');
+
+ // and replace dots with underscores so keys like my.key are
+ // translated to MY_KEY
+ return normalizedKey.replace('.', '_');
+ }
+
/**
* Encoding-aware input stream.
*/