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&amp;location=us-central1&amp;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&amp;location=us-central1&amp;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;
+                });
+    }
+}

Reply via email to