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

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus-examples.git

commit ecffa808ce4dd939858e1246f938c5e1d246b0d5
Author: Lukas Lowinger <[email protected]>
AuthorDate: Tue Jul 19 15:04:40 2022 +0200

    Add camel master example
---
 cluster-leader-election/README.adoc                | 150 ++++++++++
 cluster-leader-election/figure1.png                | Bin 0 -> 9673 bytes
 cluster-leader-election/pom.xml                    | 314 +++++++++++++++++++++
 .../src/main/java/org/acme/master/CamelRoute.java  |  41 +++
 .../java/org/acme/master/ClusterLockProducer.java  |  48 ++++
 .../src/main/kubernetes/service-account.yaml       |  56 ++++
 .../src/main/resources/application.properties      |  45 +++
 .../src/test/java/org/acme/master/MasterIT.java    |  23 ++
 .../src/test/java/org/acme/master/MasterTest.java  |  40 +++
 docs/modules/ROOT/attachments/examples.json        |   5 +
 10 files changed, 722 insertions(+)

diff --git a/cluster-leader-election/README.adoc 
b/cluster-leader-election/README.adoc
new file mode 100644
index 0000000..1fd74d1
--- /dev/null
+++ b/cluster-leader-election/README.adoc
@@ -0,0 +1,150 @@
+= Leader election in Kubernetes: A Camel Quarkus Master example
+:cq-example-description: An example that shows how to use Camel master 
component.
+
+{cq-description}
+
+Suppose an application connects out to an external service and receives 
information asynchronously via a TCP socket or websocket. As part of this 
process, the application receives data, transforms the structure, and publishes 
that data into an https://developers.redhat.com/topics/kafka-kubernetes[Apache 
Kafka] topic. In this case, only a single connection can be active at one time 
because of the possibility of publishing duplicate data (see diagram below).
+
+image::figure1.png[]
+
+The quick solution to this problem is actually a fundamental characteristic of 
Kubernetes. If the deployment is created with the replicas set to 1, then when 
the controller detects the pod is no longer running, it will attempt to create 
a new one. However, real-world situations can be more complicated. Some 
applications require a long startup time due to cache warming needs. When you 
combine slow startup times (minutes) for the pod with business requirements to 
minimize the loss of downt [...]
+
+This example shows you how to implement leader election in Kubernetes using 
Apache Camel.
+
+== Hot-warm with leader election
+To run an application as hot-warm means to have multiple instances of the 
application running and ready to serve requests, but only one instance actually 
doing the work. Within Kubernetes, this means having multiple pods ready at all 
times, but only one pod active for a particular process. In this scenario, the 
pods negotiate among themselves which one is active.
+
+Apache Camel has a component (called master) that is built exactly for this 
scenario. As the docs explain, the Camel-Master endpoint lets us ensure only a 
single consumer in a cluster consumes from a given endpoint, with automatic 
failover if that Java virtual machine (JVM) dies. To achieve this goal, the 
endpoint requires a shared resource and locking. The component has multiple 
implementations for the locking mechanism, including camel-kubernetes.
+
+Within the component's configuration, the developer provides a namespace to 
designate the shared resource. All processes that use the same namespace for 
the locking will ensure that only one process at a time obtains the lock. When 
a process has the lock, it is the leader, and the process will run. If it loses 
the lock for any reason, the component will stop the process, as well.
+
+== Start in the Development mode
+
+[source,shell]
+----
+$ mvn clean compile quarkus:dev
+----
+
+The above command compiles the project, starts the application and lets the 
Quarkus tooling watch for changes in your
+workspace. Any modifications in your project will automatically take effect in 
the running application.
+
+TIP: Please refer to the Development mode section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode[Camel
 Quarkus User guide] for more details.
+
+
+== Package the application for local playground
+
+Once you are done with developing you may want to package and run the 
application.
+
+TIP: Find more details about the JVM mode and Native mode in the Package and 
run section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_package_and_run_the_application[Camel
 Quarkus User guide]
+
+=== JVM mode
+
+[source,shell]
+----
+$ mvn clean package -Dquarkus.profile=local
+----
+
+=== Native mode
+
+IMPORTANT: Native mode requires having GraalVM and other tools installed. 
Please check the Prerequisites section
+of 
https://camel.apache.org/camel-quarkus/latest/first-steps.html#_prerequisites[Camel
 Quarkus User guide].
+
+To prepare a native executable using GraalVM, run the following command:
+
+[source,shell]
+----
+$ mvn clean package -Pnative -Dquarkus.profile=local
+----
+
+== Local Playground
+
+The first example uses the traditional Camel route domain-specific language 
(DSL), where we incorporate the master component along with the timer component 
(see link:src/main/java/org/acme/master/CamelRoute.java[CamelRoute]).
+
+Although we will ultimately deploy this application out to a Kubernetes 
cluster, we want to demonstrate and test this functionality in our local 
environment. When doing so, we won't have access to the 
https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/lease-v1/[Kubernetes
 leases], so we will implement the locking using the camel-file component. 
Because of the environmental differences, we will leverage Quarkus profiles to 
produce the correct CamelClusterService impleme [...]
+
+If a new instance of the application is started locally, then the newly 
started application will not be able to obtain the locks and therefore will not 
run the timer component. If you kill the leader, the other application will 
check the lock, see that it's not locked, and subsequently obtain the lock and 
start processing. Additionally, we can apply settings to designate how often we 
want the service to check the locks and acquire the lock.
+
+Run the packaged application in separate terminals.
+
+JVM Mode:
+[source,shell]
+----
+$ java -jar target/quarkus-app/quarkus-run.jar
+----
+
+In case you built the application in Native mode:
+[source,shell]
+----
+./target/*-runner
+----
+
+Depending on which one will start faster, you should first see similar output:
+
+[source,shell]
+----
+INFO  [clustered] (Camel (camel-quarkus-examples-cluster-leader-election) 
thread #3 - timer://clustered) Clustered route (timer) 
e54cc6a7-7b5f-4aa3-a9f8-4c31536c3b75 ...
+----
+
+In other terminal you should not see `Clustered route` message.
+
+You can also close the application process with `CTRL+C` and watch the other 
terminal will take a lead and you should start seeing `Clustered route` 
messages.
+
+== Kubernetes
+In a local environment, we used the FileLockClusterService. Now that we are 
ready to deploy this application on Kubernetes, we will switch the 
implementation from using files to using 
https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/lease-v1/[Kubernetes
 leases]. To start, let’s take a look at the deployment manifest for the 
application.
+
+=== Setup service account
+
+In this deployment, we have two replicas, but we want only one pod to run the 
processes. The configuration contains two items of note:
+
+* Notice the serviceAccountName in the deployment config. For this deployment, 
we need to set up a service account that specifically has access to the 
https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/lease-v1/[Kubernetes
 leases].
+* We are getting the kubernetes namespace from environment variable which is 
used to set up the `KubernetesClusterService` to point the same namespace as 
our application. This will tell the application to attempt to acquire the lease 
objects in the same namespace as our application.
+
+Before we deploy, we need to set up the service account and give it 
permissions to read and write to the lease objects:
+
+[source,shell]
+----
+kubectl apply -f src/main/kubernetes/service-account.yaml
+----
+
+=== Playground
+
+==== Deploy
+[source,shell]
+----
+mvn clean package -DskipTests -Dquarkus.kubernetes.deploy=true -Dkubernetes
+----
+
+Once we deploy the application into Kubernetes, the application will use the 
`KubernetesClusterService` implementation of the `CamelClusterService` to 
perform the leadership elections. To do this, the service will periodically 
query the lease information and attempt to update the information if the last 
update has not been performed in the designated lease time. The configuration 
for the timing of the leader election activity is more detailed, which should 
be expected; we are no longer s [...]
+
+You should see running two pods:
+
+[source,shell]
+----
+camel-quarkus-examples-cluster-leader-election-5d46b7564c-jwbw2                
  1/1     Running             0             17m
+camel-quarkus-examples-cluster-leader-election-5d46b7564c-vhvxg                
  1/1     Running             0             16m
+----
+
+Only one of them is printing `Clustered route` message. You can try to kill 
the pod having the lead (in this case it is 
`camel-quarkus-examples-cluster-leader-election-5d46b7564c-jwbw2`).
+
+[source,shell]
+----
+kubectl delete pod 
camel-quarkus-examples-cluster-leader-election-5d46b7564c-jwbw2
+----
+
+Then new pod election will happen and you should see similar output:
+
+[source,shell]
+----
+kubectl logs camel-quarkus-examples-cluster-leader-election-5d46b7564c-vhvxg
+...
+INFO  [org.apa.cam.com.kub.clu.loc.KubernetesLeadershipController] (Camel 
(camel-quarkus-examples-cluster-leader-election) thread #1 - 
CamelKubernetesLeadershipController) 
Pod[camel-quarkus-examples-cluster-leader-election-5d46b7564c-vhvxg] Current 
pod is becoming the new leader now...
+...
+INFO  [clustered] (Camel (camel-quarkus-examples-cluster-leader-election) 
thread #4 - timer://clustered) Clustered route (timer) 
9389bae5-7677-4679-90f7-77ce6b7e5fda ...
+...
+----
+
+== Feedback
+
+Please report bugs and propose improvements via 
https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus] 
project.
diff --git a/cluster-leader-election/figure1.png 
b/cluster-leader-election/figure1.png
new file mode 100644
index 0000000..d15192b
Binary files /dev/null and b/cluster-leader-election/figure1.png differ
diff --git a/cluster-leader-election/pom.xml b/cluster-leader-election/pom.xml
new file mode 100644
index 0000000..0c6bf24
--- /dev/null
+++ b/cluster-leader-election/pom.xml
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-quarkus-examples-cluster-leader-election</artifactId>
+    <groupId>org.apache.camel.quarkus.examples</groupId>
+    <version>2.11.0-SNAPSHOT</version>
+
+    <name>Camel Quarkus :: Examples :: Cluster leader election</name>
+    <description>Camel Quarkus Example :: Cluster leader election</description>
+
+    <properties>
+        <quarkus.platform.version>2.11.1.Final</quarkus.platform.version>
+        
<camel-quarkus.platform.version>2.12.0-SNAPSHOT</camel-quarkus.platform.version>
+
+        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+        
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        
<camel-quarkus.platform.group-id>org.apache.camel.quarkus</camel-quarkus.platform.group-id>
+        
<camel-quarkus.platform.artifact-id>camel-quarkus-bom</camel-quarkus.platform.artifact-id>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.target>11</maven.compiler.target>
+        <maven.compiler.source>11</maven.compiler.source>
+        
<maven.compiler.testTarget>${maven.compiler.target}</maven.compiler.testTarget>
+        
<maven.compiler.testSource>${maven.compiler.source}</maven.compiler.testSource>
+
+        <formatter-maven-plugin.version>2.17.1</formatter-maven-plugin.version>
+        <impsort-maven-plugin.version>1.3.2</impsort-maven-plugin.version>
+        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+        <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
+        <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
+        <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
+        <mycila-license.version>3.0</mycila-license.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Import BOM -->
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>${camel-quarkus.platform.group-id}</groupId>
+                <artifactId>${camel-quarkus.platform.artifact-id}</artifactId>
+                <version>${camel-quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-log</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-timer</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-file</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-kubernetes</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-master</artifactId>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+
+                <plugin>
+                    <groupId>net.revelc.code.formatter</groupId>
+                    <artifactId>formatter-maven-plugin</artifactId>
+                    <version>${formatter-maven-plugin.version}</version>
+                    <configuration>
+                        
<configFile>${maven.multiModuleProjectDirectory}/eclipse-formatter-config.xml</configFile>
+                        <lineEnding>LF</lineEnding>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>net.revelc.code</groupId>
+                    <artifactId>impsort-maven-plugin</artifactId>
+                    <version>${impsort-maven-plugin.version}</version>
+                    <configuration>
+                        <groups>java.,javax.,org.w3c.,org.xml.,junit.</groups>
+                        <removeUnused>true</removeUnused>
+                        <staticAfter>true</staticAfter>
+                        
<staticGroups>java.,javax.,org.w3c.,org.xml.,junit.</staticGroups>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <showDeprecation>true</showDeprecation>
+                        <showWarnings>true</showWarnings>
+                        <compilerArgs>
+                            <arg>-Xlint:unchecked</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <configuration>
+                        <failIfNoTests>false</failIfNoTests>
+                        <systemProperties>
+                            
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        </systemProperties>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>${quarkus.platform.group-id}</groupId>
+                    <artifactId>quarkus-maven-plugin</artifactId>
+                    <version>${quarkus.platform.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>${maven-jar-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>com.mycila</groupId>
+                    <artifactId>license-maven-plugin</artifactId>
+                    <version>${mycila-license.version}</version>
+                    <configuration>
+                        <failIfUnknown>true</failIfUnknown>
+                        
<header>${maven.multiModuleProjectDirectory}/header.txt</header>
+                        <excludes>
+                            <exclude>**/*.adoc</exclude>
+                            <exclude>**/*.txt</exclude>
+                            <exclude>**/LICENSE.txt</exclude>
+                            <exclude>**/LICENSE</exclude>
+                            <exclude>**/NOTICE.txt</exclude>
+                            <exclude>**/NOTICE</exclude>
+                            <exclude>**/README</exclude>
+                            <exclude>**/pom.xml.versionsBackup</exclude>
+                        </excludes>
+                        <mapping>
+                            <java>SLASHSTAR_STYLE</java>
+                            <properties>CAMEL_PROPERTIES_STYLE</properties>
+                            <kt>SLASHSTAR_STYLE</kt>
+                        </mapping>
+                        <headerDefinitions>
+                            
<headerDefinition>${maven.multiModuleProjectDirectory}/license-properties-headerdefinition.xml</headerDefinition>
+                        </headerDefinitions>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build</id>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code</groupId>
+                <artifactId>impsort-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>sort-imports</id>
+                        <goals>
+                            <goal>sort</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.package.type>native</quarkus.package.type>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        
<quarkus.package.type>${quarkus.package.type}</quarkus.package.type>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>kubernetes</id>
+            <activation>
+                <property>
+                    <name>kubernetes</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-kubernetes</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>io.quarkus</groupId>
+                    <artifactId>quarkus-container-image-jib</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.camel.quarkus</groupId>
+                    <artifactId>camel-quarkus-microprofile-health</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+</project>
diff --git 
a/cluster-leader-election/src/main/java/org/acme/master/CamelRoute.java 
b/cluster-leader-election/src/main/java/org/acme/master/CamelRoute.java
new file mode 100644
index 0000000..dd6187c
--- /dev/null
+++ b/cluster-leader-election/src/main/java/org/acme/master/CamelRoute.java
@@ -0,0 +1,41 @@
+/*
+ * 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.acme.master;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+
+@ApplicationScoped
+public class CamelRoute extends EndpointRouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        // This route is configured to be local (see application.properties)
+        // so it will be started regardless of the leadership status of
+        // this node.
+        from("timer:heartbeat?period=10000")
+                .routeId("heartbeat")
+                .log("HeartBeating route (timer) {{node.id}} ...");
+
+        // This route is configured to be clustered so it will be started
+        // by the controller only when this node is leader
+        from("master:{{node.namespace}}:timer:clustered?period=5000")
+                .routeId("clustered")
+                .log("Clustered route (timer) {{node.id}} ...");
+    }
+}
diff --git 
a/cluster-leader-election/src/main/java/org/acme/master/ClusterLockProducer.java
 
b/cluster-leader-election/src/main/java/org/acme/master/ClusterLockProducer.java
new file mode 100644
index 0000000..a9782d5
--- /dev/null
+++ 
b/cluster-leader-election/src/main/java/org/acme/master/ClusterLockProducer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.acme.master;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.cluster.CamelClusterService;
+import org.apache.camel.component.file.cluster.FileLockClusterService;
+import org.apache.camel.component.kubernetes.cluster.KubernetesClusterService;
+
+@ApplicationScoped
+public class ClusterLockProducer {
+
+    @Produces
+    public CamelClusterService clusterService(CamelContext camelContext) {
+        String kubernetesNamespace = System.getenv("KUBERNETES_NAMESPACE");
+        if (kubernetesNamespace != null) {
+            KubernetesClusterService service = new KubernetesClusterService();
+            service.setKubernetesNamespace(kubernetesNamespace);
+            return service;
+        } else {
+            FileLockClusterService service = new FileLockClusterService();
+            service.setRoot("target/cluster");
+            service.setAcquireLockDelay(1, TimeUnit.SECONDS);
+            service.setAcquireLockInterval(1, TimeUnit.SECONDS);
+            return service;
+        }
+    }
+
+}
diff --git a/cluster-leader-election/src/main/kubernetes/service-account.yaml 
b/cluster-leader-election/src/main/kubernetes/service-account.yaml
new file mode 100644
index 0000000..1e829c4
--- /dev/null
+++ b/cluster-leader-election/src/main/kubernetes/service-account.yaml
@@ -0,0 +1,56 @@
+#
+# 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.
+#
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: camel-leader-election
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: camel-leader-election
+rules:
+  - apiGroups:
+      - ""
+      - "coordination.k8s.io"
+    resources:
+      - configmaps
+      - secrets
+      - pods
+      - leases
+    verbs:
+      - create
+      - delete
+      - deletecollection
+      - get
+      - list
+      - patch
+      - update
+      - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: camel-leader-election
+subjects:
+  - kind: ServiceAccount
+    name: camel-leader-election
+roleRef:
+  kind: Role
+  name: camel-leader-election
+  apiGroup: rbac.authorization.k8s.io
diff --git a/cluster-leader-election/src/main/resources/application.properties 
b/cluster-leader-election/src/main/resources/application.properties
new file mode 100644
index 0000000..624c81d
--- /dev/null
+++ b/cluster-leader-election/src/main/resources/application.properties
@@ -0,0 +1,45 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+#
+# Quarkus
+#
+quarkus.banner.enabled = false
+quarkus.log.file.enable = true
+
+#
+# Quarkus - Camel
+#
+node.namespace = camel-master
+node.id = ${quarkus.uuid}
+
+#
+# Camel
+#
+camel.context.name = camel-quarkus-examples-cluster-leader-election
+
+#
+# Kubernetes
+#
+quarkus.kubernetes.replicas=2
+quarkus.kubernetes-client.generate-rbac=false
+quarkus.kubernetes.service-account=camel-leader-election
+
+# Uncomment after https://github.com/apache/camel-quarkus/issues/3918 is solved
+#camel.cluster.file.enabled = true
+#camel.cluster.file.id = ${node.id}
+#camel.cluster.file.root = target/cluster wait for
+
diff --git 
a/cluster-leader-election/src/test/java/org/acme/master/MasterIT.java 
b/cluster-leader-election/src/test/java/org/acme/master/MasterIT.java
new file mode 100644
index 0000000..d36ce3a
--- /dev/null
+++ b/cluster-leader-election/src/test/java/org/acme/master/MasterIT.java
@@ -0,0 +1,23 @@
+/*
+ * 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.acme.master;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class MasterIT extends MasterTest {
+}
diff --git 
a/cluster-leader-election/src/test/java/org/acme/master/MasterTest.java 
b/cluster-leader-election/src/test/java/org/acme/master/MasterTest.java
new file mode 100644
index 0000000..ef2edfd
--- /dev/null
+++ b/cluster-leader-election/src/test/java/org/acme/master/MasterTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.acme.master;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+
+import static org.awaitility.Awaitility.await;
+
+@QuarkusTest
+public class MasterTest {
+
+    @Test
+    public void testLog() {
+        await().atMost(10L, TimeUnit.SECONDS).pollDelay(1, 
TimeUnit.SECONDS).until(() -> {
+            String log = new 
String(Files.readAllBytes(Paths.get("target/quarkus.log")), 
StandardCharsets.UTF_8);
+            return log.contains("Clustered route (timer)");
+        });
+    }
+
+}
diff --git a/docs/modules/ROOT/attachments/examples.json 
b/docs/modules/ROOT/attachments/examples.json
index 58fde5c..42964a2 100644
--- a/docs/modules/ROOT/attachments/examples.json
+++ b/docs/modules/ROOT/attachments/examples.json
@@ -49,6 +49,11 @@
     "description": "Shows how to define Camel routes using Kotlin programming 
language",
     "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/timer-log-kotlin";
   },
+  {
+    "title": "Leader election in Kubernetes: A Camel Quarkus Master example",
+    "description": "Shows how to use Camel master component.",
+    "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/cluster-leader-election";
+  },
   {
     "title": "Observability",
     "description": "Demonstrates how to add support for metrics, health checks 
and distributed tracing",

Reply via email to