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 cdd6b90ed19f CAMEL-17368: document Google Functions ADC and GKE
authentication (#27284)
cdd6b90ed19f is described below
commit cdd6b90ed19fd35cdb5ae0fb72fe8eb0983b7951
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Oct 6 15:16:49 2026 +0200
CAMEL-17368: document Google Functions ADC and GKE authentication (#27284)
* CAMEL-17368: document Google Functions ADC and GKE authentication
Document the existing keyless authentication path and cover ADC selection,
explicit service-account key precedence, and ADC failure propagation.
Co-authored-by: Codex <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
* CAMEL-17368: show the ADC example in Java, XML and YAML, and note what is
not exposed
The keyless example now uses the page's Java/XML/YAML tabs and says it
differs
from the later examples only by leaving serviceAccountKey out. A NOTE,
matching the
google-pubsub page, says that explicit Workload Identity Federation
configuration and
service account impersonation are not exposed as endpoint options.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---------
Signed-off-by: Andrea Cosentino <[email protected]>
Co-authored-by: Codex <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../catalog/docs/google-functions-component.adoc | 68 ++++++++++++
.../src/main/docs/google-functions-component.adoc | 68 ++++++++++++
.../GoogleCloudFunctionsClientFactoryTest.java | 117 +++++++++++++++++++++
3 files changed, 253 insertions(+)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-functions-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-functions-component.adoc
index 6d774caeef1c..f10db47ee62e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-functions-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-functions-component.adoc
@@ -55,6 +55,74 @@ export
GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/my-key.json"
----
+=== Application Default Credentials and GKE
+
+When `serviceAccountKey` is not configured, the component uses Google's
+https://cloud.google.com/docs/authentication/application-default-credentials[Application
Default Credentials (ADC)].
+ADC discovers credentials from the environment, local application default
credentials,
+or the metadata server of the Google Cloud environment. An explicitly
configured
+`serviceAccountKey` takes precedence over ADC.
+
+On Google Kubernetes Engine (GKE), configure
+https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity[Workload
Identity Federation for GKE]
+for the cluster and the node pool where the Camel pod runs. Configure the pod
to use
+a Kubernetes ServiceAccount with the IAM permissions required for the Cloud
Functions
+operations it performs, either through direct IAM access or by linking it to
an IAM
+service account as described in the GKE documentation.
+
+The Google authentication library obtains credentials through the GKE metadata
server.
+No service account key file or Camel-specific Workload Identity option is
required.
+Leave `serviceAccountKey` unset and do not set
`GOOGLE_APPLICATION_CREDENTIALS` to a
+credential file when using the pod's workload identity, since ADC checks that
environment
+variable before the metadata server.
+
+For example, this route lists functions using the pod's identity. Unlike the
examples further down, it
+leaves `serviceAccountKey` out; there is nothing else to configure:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:listFunctions")
+
.to("google-functions://myCamelFunction?project=myProject&location=us-central1&operation=listFunctions");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:listFunctions"/>
+ <to
uri="google-functions://myCamelFunction?project=myProject&location=us-central1&operation=listFunctions"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:listFunctions
+ steps:
+ - to:
+ uri: google-functions://myCamelFunction
+ parameters:
+ project: myProject
+ location: us-central1
+ operation: listFunctions
+----
+====
+
+NOTE: Configuring
+https://cloud.google.com/iam/docs/workload-identity-federation[Workload
Identity Federation] for an
+external identity provider (an explicit WIF credential configuration file for
AWS, Azure or GitHub
+Actions), or service account impersonation, is not exposed as an endpoint
option by the Google
+components.
+
+
== URI Format
----
diff --git
a/components/camel-google/camel-google-functions/src/main/docs/google-functions-component.adoc
b/components/camel-google/camel-google-functions/src/main/docs/google-functions-component.adoc
index 6d774caeef1c..f10db47ee62e 100644
---
a/components/camel-google/camel-google-functions/src/main/docs/google-functions-component.adoc
+++
b/components/camel-google/camel-google-functions/src/main/docs/google-functions-component.adoc
@@ -55,6 +55,74 @@ export
GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/my-key.json"
----
+=== Application Default Credentials and GKE
+
+When `serviceAccountKey` is not configured, the component uses Google's
+https://cloud.google.com/docs/authentication/application-default-credentials[Application
Default Credentials (ADC)].
+ADC discovers credentials from the environment, local application default
credentials,
+or the metadata server of the Google Cloud environment. An explicitly
configured
+`serviceAccountKey` takes precedence over ADC.
+
+On Google Kubernetes Engine (GKE), configure
+https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity[Workload
Identity Federation for GKE]
+for the cluster and the node pool where the Camel pod runs. Configure the pod
to use
+a Kubernetes ServiceAccount with the IAM permissions required for the Cloud
Functions
+operations it performs, either through direct IAM access or by linking it to
an IAM
+service account as described in the GKE documentation.
+
+The Google authentication library obtains credentials through the GKE metadata
server.
+No service account key file or Camel-specific Workload Identity option is
required.
+Leave `serviceAccountKey` unset and do not set
`GOOGLE_APPLICATION_CREDENTIALS` to a
+credential file when using the pod's workload identity, since ADC checks that
environment
+variable before the metadata server.
+
+For example, this route lists functions using the pod's identity. Unlike the
examples further down, it
+leaves `serviceAccountKey` out; there is nothing else to configure:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:listFunctions")
+
.to("google-functions://myCamelFunction?project=myProject&location=us-central1&operation=listFunctions");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:listFunctions"/>
+ <to
uri="google-functions://myCamelFunction?project=myProject&location=us-central1&operation=listFunctions"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:listFunctions
+ steps:
+ - to:
+ uri: google-functions://myCamelFunction
+ parameters:
+ project: myProject
+ location: us-central1
+ operation: listFunctions
+----
+====
+
+NOTE: Configuring
+https://cloud.google.com/iam/docs/workload-identity-federation[Workload
Identity Federation] for an
+external identity provider (an explicit WIF credential configuration file for
AWS, Azure or GitHub
+Actions), or service account impersonation, is not exposed as an endpoint
option by the Google
+components.
+
+
== URI Format
----
diff --git
a/components/camel-google/camel-google-functions/src/test/java/org/apache/camel/component/google/functions/unit/GoogleCloudFunctionsClientFactoryTest.java
b/components/camel-google/camel-google-functions/src/test/java/org/apache/camel/component/google/functions/unit/GoogleCloudFunctionsClientFactoryTest.java
new file mode 100644
index 000000000000..58b27e92c4d3
--- /dev/null
+++
b/components/camel-google/camel-google-functions/src/test/java/org/apache/camel/component/google/functions/unit/GoogleCloudFunctionsClientFactoryTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.google.functions.unit;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import com.google.auth.Credentials;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.auth.oauth2.ServiceAccountCredentials;
+import com.google.cloud.functions.v1.CloudFunctionsServiceClient;
+import com.google.cloud.functions.v1.CloudFunctionsServiceSettings;
+import
org.apache.camel.component.google.functions.GoogleCloudFunctionsClientFactory;
+import
org.apache.camel.component.google.functions.GoogleCloudFunctionsConfiguration;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.MockedStatic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+
+class GoogleCloudFunctionsClientFactoryTest {
+
+ @TempDir
+ Path temporaryDirectory;
+
+ @Test
+ void usesApplicationDefaultCredentialsWhenServiceAccountKeyIsAbsent()
throws Exception {
+ GoogleCredentials credentials = mock(GoogleCredentials.class);
+ CloudFunctionsServiceClient client =
mock(CloudFunctionsServiceClient.class);
+ try (DefaultCamelContext context = new DefaultCamelContext();
+ MockedStatic<GoogleCredentials> adc =
mockStatic(GoogleCredentials.class);
+ MockedStatic<CloudFunctionsServiceClient> clients =
mockStatic(CloudFunctionsServiceClient.class)) {
+
adc.when(GoogleCredentials::getApplicationDefault).thenReturn(credentials);
+ stubClientCreation(clients, credentials, client);
+
+ assertSame(client,
GoogleCloudFunctionsClientFactory.create(context, new
GoogleCloudFunctionsConfiguration()));
+
+ adc.verify(GoogleCredentials::getApplicationDefault);
+ clients.verify(() ->
CloudFunctionsServiceClient.create(any(CloudFunctionsServiceSettings.class)));
+ }
+ }
+
+ @Test
+ void serviceAccountKeyTakesPrecedenceOverApplicationDefaultCredentials()
throws Exception {
+ Path keyFile = temporaryDirectory.resolve("service-account.json");
+ Files.writeString(keyFile, "service-account-key");
+ GoogleCloudFunctionsConfiguration configuration = new
GoogleCloudFunctionsConfiguration();
+ configuration.setServiceAccountKey(keyFile.toUri().toString());
+ ServiceAccountCredentials credentials =
mock(ServiceAccountCredentials.class);
+ CloudFunctionsServiceClient client =
mock(CloudFunctionsServiceClient.class);
+ try (DefaultCamelContext context = new DefaultCamelContext();
+ MockedStatic<GoogleCredentials> adc =
mockStatic(GoogleCredentials.class);
+ MockedStatic<ServiceAccountCredentials> keys =
mockStatic(ServiceAccountCredentials.class);
+ MockedStatic<CloudFunctionsServiceClient> clients =
mockStatic(CloudFunctionsServiceClient.class)) {
+ keys.when(() ->
ServiceAccountCredentials.fromStream(any(InputStream.class))).thenAnswer(invocation
-> {
+ InputStream stream = invocation.getArgument(0);
+ assertEquals("service-account-key", new
String(stream.readAllBytes(), StandardCharsets.UTF_8));
+ return credentials;
+ });
+ stubClientCreation(clients, credentials, client);
+
+ assertSame(client,
GoogleCloudFunctionsClientFactory.create(context, configuration));
+
+ keys.verify(() ->
ServiceAccountCredentials.fromStream(any(InputStream.class)));
+ adc.verifyNoInteractions();
+ clients.verify(() ->
CloudFunctionsServiceClient.create(any(CloudFunctionsServiceSettings.class)));
+ }
+ }
+
+ @Test
+ void propagatesApplicationDefaultCredentialFailureWithoutCreatingClient()
throws Exception {
+ IOException failure = new IOException("Application Default Credentials
unavailable");
+ try (DefaultCamelContext context = new DefaultCamelContext();
+ MockedStatic<GoogleCredentials> adc =
mockStatic(GoogleCredentials.class);
+ MockedStatic<CloudFunctionsServiceClient> clients =
mockStatic(CloudFunctionsServiceClient.class)) {
+
adc.when(GoogleCredentials::getApplicationDefault).thenThrow(failure);
+
+ assertSame(failure, assertThrows(IOException.class,
+ () -> GoogleCloudFunctionsClientFactory.create(context,
new GoogleCloudFunctionsConfiguration())));
+
+ clients.verifyNoInteractions();
+ }
+ }
+
+ private void stubClientCreation(
+ MockedStatic<CloudFunctionsServiceClient> clients, Credentials
credentials, CloudFunctionsServiceClient client) {
+ clients.when(() ->
CloudFunctionsServiceClient.create(any(CloudFunctionsServiceSettings.class)))
+ .thenAnswer(invocation -> {
+ CloudFunctionsServiceSettings settings =
invocation.getArgument(0);
+ assertSame(credentials,
settings.getCredentialsProvider().getCredentials());
+ return client;
+ });
+ }
+}