You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/10/13 17:18:50 UTC
[camel-karavan] 02/02: First operator that works
This is an automated email from the ASF dual-hosted git repository.
marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit eeca4b631a54bbb90d377d13045a042020e83696
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Thu Oct 13 13:18:36 2022 -0400
First operator that works
---
change_version.sh | 3 +
karavan-cloud/README.md | 4 +-
karavan-cloud/karavan-namespace.yaml | 4 +
karavan-cloud/minikube/karavan-acl.yaml | 2 +-
karavan-cloud/openshift/karavan-acl.yaml | 2 +-
karavan-operator/Makefile | 47 +++++++++
karavan-operator/PROJECT | 9 +-
karavan-operator/pom.xml | 28 +++++-
karavan-operator/resources/deployments-role.yaml | 29 +++---
karavan-operator/resources/karavan.yaml | 2 +
.../apache/camel/karavan/AbstractResources.java | 54 ----------
.../java/org/apache/camel/karavan/Constants.java | 21 ++--
.../java/org/apache/camel/karavan/Karavan.java | 13 ++-
.../apache/camel/karavan/KaravanController.java | 38 -------
.../apache/camel/karavan/KaravanDeployment.java | 101 +++++++++++++++++++
.../org/apache/camel/karavan/KaravanPvcData.java | 53 ++++++++++
.../org/apache/camel/karavan/KaravanPvcJbang.java | 51 ++++++++++
.../apache/camel/karavan/KaravanPvcM2Cache.java | 52 ++++++++++
.../apache/camel/karavan/KaravanReconciler.java | 77 +++++++++++++++
.../org/apache/camel/karavan/KaravanResources.java | 109 ---------------------
.../java/org/apache/camel/karavan/KaravanRole.java | 53 ++++++++++
.../apache/camel/karavan/KaravanRoleBinding.java | 48 +++++++++
.../camel/karavan/KaravanRoleBindingView.java | 48 +++++++++
.../org/apache/camel/karavan/KaravanService.java | 44 +++++++++
.../camel/karavan/KaravanServiceAccount.java | 44 +++++++++
.../java/org/apache/camel/karavan/KaravanSpec.java | 19 +++-
.../org/apache/camel/karavan/KaravanStatus.java | 13 +++
.../camel/karavan/KaravanTektonPipeline.java | 75 ++++++++++++++
.../apache/camel/karavan/KaravanTektonTask.java | 95 ++++++++++++++++++
.../src/main/resources/application.properties | 10 +-
.../resources/karavan-quarkus-builder-script.sh | 50 ++++++++++
31 files changed, 945 insertions(+), 253 deletions(-)
diff --git a/change_version.sh b/change_version.sh
index 08d4ef9..43562c3 100755
--- a/change_version.sh
+++ b/change_version.sh
@@ -6,6 +6,9 @@ mvn versions:set -DnewVersion=$1 -f karavan-generator
# echo "Set Application pom.xml version: $1";
mvn versions:set -DnewVersion=$1 -f karavan-app
+# echo "Set Operator pom.xml version: $1";
+mvn versions:set -DnewVersion=$1 -f karavan-operator
+
# echo "Set Core package.json extension version: $1";
yarn version --new-version $1 --no-commit --no-git-tag-version --cwd karavan-core
diff --git a/karavan-cloud/README.md b/karavan-cloud/README.md
index b70b285..7b6d0e3 100644
--- a/karavan-cloud/README.md
+++ b/karavan-cloud/README.md
@@ -15,7 +15,7 @@
```
2. Create namespace
```
- oc apply -f openshift/karavan-namespace.yaml
+ oc apply -f karavan-namespace.yaml
oc project karavan
```
@@ -47,7 +47,7 @@
```
2. Create namespace
```
- kubectl apply -f base/karavan-namespace.yaml
+ kubectl apply -f karavan-namespace.yaml
```
3. Enable Registry
```
diff --git a/karavan-cloud/karavan-namespace.yaml b/karavan-cloud/karavan-namespace.yaml
new file mode 100644
index 0000000..768a8b5
--- /dev/null
+++ b/karavan-cloud/karavan-namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: karavan
\ No newline at end of file
diff --git a/karavan-cloud/minikube/karavan-acl.yaml b/karavan-cloud/minikube/karavan-acl.yaml
index a102715..e060c80 100644
--- a/karavan-cloud/minikube/karavan-acl.yaml
+++ b/karavan-cloud/minikube/karavan-acl.yaml
@@ -78,7 +78,7 @@ subjects:
name: karavan
namespace: karavan
---
-# Pipeline shoulf have access to create rolebindings
+# Pipeline should have access to create rolebindings
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
diff --git a/karavan-cloud/openshift/karavan-acl.yaml b/karavan-cloud/openshift/karavan-acl.yaml
index a102715..e060c80 100644
--- a/karavan-cloud/openshift/karavan-acl.yaml
+++ b/karavan-cloud/openshift/karavan-acl.yaml
@@ -78,7 +78,7 @@ subjects:
name: karavan
namespace: karavan
---
-# Pipeline shoulf have access to create rolebindings
+# Pipeline should have access to create rolebindings
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
diff --git a/karavan-operator/Makefile b/karavan-operator/Makefile
index 584aa45..ebcc4ff 100644
--- a/karavan-operator/Makefile
+++ b/karavan-operator/Makefile
@@ -1,4 +1,36 @@
+VERSION ?= 3.18.5
+
+# CHANNELS define the bundle channels used in the bundle.
+# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
+# To re-generate a bundle for other specific channels without changing the standard setup, you can:
+# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)
+# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable")
+ifneq ($(origin CHANNELS), undefined)
+BUNDLE_CHANNELS := --channels=$(CHANNELS)
+endif
+
+# DEFAULT_CHANNEL defines the default channel used in the bundle.
+# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable")
+# To re-generate a bundle for any other default channel without changing the default setup, you can:
+# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)
+# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable")
+ifneq ($(origin DEFAULT_CHANNEL), undefined)
+BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
+endif
+BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
+
+# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.
+# This variable is used to construct full image tags for bundle and catalog images.
+#
+# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
+# karavan.camel.apache.org/operator-test-bundle:$VERSION and karavan.camel.apache.org/operator-test-catalog:$VERSION.
+IMAGE_TAG_BASE ?= ghcr.io/apache/camel-karavan-operator
+
+# BUNDLE_IMG defines the image:tag used for the bundle.
+# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
+BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
+
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
@@ -41,3 +73,18 @@ deploy: ## Deploy controller to the K8s cluster specified in ~/.kube/config.
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
kubectl delete -f target/kubernetes/kubernetes.yml
+
+##@Bundle
+.PHONY: bundle
+bundle: ## Generate bundle manifests and metadata, then validate generated files.
+## marker
+ cat target/kubernetes/karavans.app.karavan.camel.apache.org-v1.yml target/kubernetes/kubernetes.yml | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
+ operator-sdk bundle validate ./bundle
+
+.PHONY: bundle-build
+bundle-build: ## Build the bundle image.
+ docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
+
+.PHONY: bundle-push
+bundle-push: ## Push the bundle image.
+ docker push $(BUNDLE_IMG)
diff --git a/karavan-operator/PROJECT b/karavan-operator/PROJECT
index 0a79c8b..e6aa751 100644
--- a/karavan-operator/PROJECT
+++ b/karavan-operator/PROJECT
@@ -1,13 +1,10 @@
-domain: karavan.camel.apache.org
+domain: apache.org
layout:
- quarkus.javaoperatorsdk.io/v1-alpha
-projectName: karavan-operator
+projectName: camel-karavan-operator
resources:
- api:
- crdVersion: v1
- namespaced: true
- domain: karavan.camel.apache.org
- group: app
+ group: camel
kind: Karavan
version: v1
version: "3"
diff --git a/karavan-operator/pom.xml b/karavan-operator/pom.xml
index 86cdf7e..5feb7ae 100644
--- a/karavan-operator/pom.xml
+++ b/karavan-operator/pom.xml
@@ -6,7 +6,7 @@
<groupId>org.apache.camel.karavan</groupId>
<artifactId>karavan-operator</artifactId>
<name>karavan-operator</name>
- <version>0.0.1-SNAPSHOT</version>
+ <version>3.18.5</version>
<packaging>jar</packaging>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
@@ -15,8 +15,8 @@
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <quarkus-sdk.version>2.0.1</quarkus-sdk.version>
- <quarkus.version>2.7.0.Final</quarkus.version>
+ <quarkus-sdk.version>4.0.3</quarkus-sdk.version>
+ <quarkus.version>2.13.0.Final</quarkus.version>
</properties>
<dependencyManagement>
@@ -28,6 +28,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
+ <dependency>
+ <groupId>io.quarkiverse.operatorsdk</groupId>
+ <artifactId>quarkus-operator-sdk-bom</artifactId>
+ <version>${quarkus-sdk.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
<dependencies>
@@ -35,10 +42,23 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>tekton-client</artifactId>
+ </dependency>
<dependency>
<groupId>io.quarkiverse.operatorsdk</groupId>
<artifactId>quarkus-operator-sdk</artifactId>
- <version>${quarkus-sdk.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkiverse.operatorsdk</groupId>
+ <artifactId>quarkus-operator-sdk-bundle-generator</artifactId>
+ </dependency>
+ <!-- This dependency is needed only to ensure proper building order so that this module is build after the bundle generator extension -->
+ <dependency>
+ <groupId>io.quarkiverse.operatorsdk</groupId>
+ <artifactId>quarkus-operator-sdk-bundle-generator-deployment</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
diff --git a/karavan-operator/resources/deployments-role.yaml b/karavan-operator/resources/deployments-role.yaml
index af8e7e8..2ae9851 100644
--- a/karavan-operator/resources/deployments-role.yaml
+++ b/karavan-operator/resources/deployments-role.yaml
@@ -1,33 +1,28 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
- name: karavancontroller-deployment-role
+ name: karavan-operator-role
rules:
- - apiGroups:
- - apps
- - extensions
- resources:
- - deployments
- verbs:
- - get
- - list
- - watch
- - create
- - delete
- - patch
- - update
+ - apiGroups: [ "apps", "extensions" ]
+ resources: [ "serviceaccounts", "deployments", "services", "routes", "persistentvolumes", "persistentvolumeclaims"]
+ verbs: [ "*" ]
+ - apiGroups: [ "tekton.dev" ]
+ resources: [ "pipelinetasks", "pipelines" ]
+ verbs: [ "*" ]
+ - apiGroups: [ "rbac.authorization.k8s.io" ]
+ resources: [ "roles", "rolebindings" ]
+ verbs: [ "*" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app.kubernetes.io/name: karavan-operator
- app.kubernetes.io/version: 0.0.11
- name: karavan-operator-deployment-controller
+ name: karavan-operator
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
- name: karavancontroller-deployment-role
+ name: karavan-operator-role
subjects:
- kind: ServiceAccount
name: karavan-operator
\ No newline at end of file
diff --git a/karavan-operator/resources/karavan.yaml b/karavan-operator/resources/karavan.yaml
index 678ac1d..93abfbf 100644
--- a/karavan-operator/resources/karavan.yaml
+++ b/karavan-operator/resources/karavan.yaml
@@ -2,6 +2,7 @@ apiVersion: camel.apache.org/v1
kind: Karavan
metadata:
name: karavan
+ namespace: karavan
annotations:
camel.apache.org/support.level: Preview
camel.apache.org/version: 3.18.5
@@ -9,3 +10,4 @@ metadata:
spec:
instances: 1
auth: public
+ nodePort: 30666
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/AbstractResources.java b/karavan-operator/src/main/java/org/apache/camel/karavan/AbstractResources.java
deleted file mode 100644
index d67a696..0000000
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/AbstractResources.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.apache.camel.karavan;
-
-import java.util.Optional;
-
-import javax.inject.Inject;
-
-import io.fabric8.kubernetes.api.model.OwnerReference;
-import io.fabric8.kubernetes.api.model.OwnerReferenceBuilder;
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.client.KubernetesClient;
-
-public abstract class AbstractResources {
-
- private Karavan karavan;
-
- @Inject
- KubernetesClient client;
-
- protected Optional<Deployment> checkDeploymentExists(String name){
- return Optional.ofNullable(client.apps().deployments().inNamespace(client.getNamespace()).withName(name).get());
- }
-
- protected Optional<Service> checkServiceExists(String name) {
- return Optional.ofNullable(client.services().inNamespace(client.getNamespace()).withName(name).get());
- }
-
- protected void deleteDeployment(String name){
- client.apps().deployments().inNamespace(client.getNamespace()).withName(name).delete();
- }
-
- protected void deleteService(String name){
- client.services().inNamespace(client.getNamespace()).withName(name).delete();
- }
-
- protected OwnerReference createOwnerReference(Karavan resource) {
- final var metadata = resource.getMetadata();
- return new OwnerReferenceBuilder()
- .withUid(metadata.getUid())
- .withApiVersion(resource.getApiVersion())
- .withName(metadata.getName())
- .withKind(resource.getKind())
- .build();
- }
-
- public Karavan getKaravan() {
- return karavan;
- }
-
- public void setKaravan(Karavan karavan) {
- this.karavan = karavan;
- }
-
-}
\ No newline at end of file
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/Constants.java b/karavan-operator/src/main/java/org/apache/camel/karavan/Constants.java
index a709ebe..fef10c5 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/Constants.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/Constants.java
@@ -1,23 +1,20 @@
package org.apache.camel.karavan;
-import java.util.Map;
-
public final class Constants {
public static final String CRD_GROUP = "camel.apache.org";
public static final String CRD_VERSION = "v1";
public static final String SHORT_NAME = "karavan";
public static final String NAME = "karavan";
public static final String PLURAL_NAME = "karavans";
- public static final String MANAGED_BY_LABEL = "app.kubernetes.io/managed-by";
- public static final String MANAGED_BY_VALUE = "karavan-operator";
-
- public static final String KARAVAN_MODE = "KARAVAN_MODE";
- public static final Map<String, String> DEFAULT_LABELS = Map.of(
- "app.kubernetes.io/name", NAME,
- "app.kubernetes.io/version", "latest",
- "app.kubernetes.io/part-of", NAME
- );
+ public static final String SERVICEACCOUNT_KARAVAN = "karavan";
+ public static final String ROLE_KARAVAN = "karavan";
+ public static final String ROLEBINDING_KARAVAN = "karavan";
+ public static final String ROLEBINDING_KARAVAN_VIEW = "karavan-view";
+ public static final String PVC_DATA = "karavan-data";
+ public static final String PVC_M2_CACHE = "karavan-m2-cache";
+ public static final String PVC_JBANG = "karavan-jbang";
- public static final String KARAVAN_IMAGE = "ghcr.io/apache/camel-karavan:latest";
+ public static final String PIPELINE_BUILD_QUARKUS = "karavan-pipeline-build-quarkus";
+ public static final String TASK_BUILD_QUARKUS = "karavan-task-build-quarkus";
}
\ No newline at end of file
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/Karavan.java b/karavan-operator/src/main/java/org/apache/camel/karavan/Karavan.java
index 70df643..ac95084 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/Karavan.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/Karavan.java
@@ -8,5 +8,16 @@ import io.fabric8.kubernetes.model.annotation.*;
@Version(Constants.CRD_VERSION)
@ShortNames(Constants.SHORT_NAME)
@Plural(Constants.PLURAL_NAME)
-public class Karavan extends CustomResource<KaravanSpec, KaravanStatus> implements Namespaced {}
+public class Karavan extends CustomResource<KaravanSpec, KaravanStatus> implements Namespaced {
+
+ @Override
+ protected KaravanSpec initSpec() {
+ return new KaravanSpec();
+ }
+
+ @Override
+ protected KaravanStatus initStatus() {
+ return new KaravanStatus();
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanController.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanController.java
deleted file mode 100644
index d9f337f..0000000
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanController.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.apache.camel.karavan;
-
-import javax.inject.Inject;
-
-import io.fabric8.kubernetes.client.KubernetesClient;
-import io.javaoperatorsdk.operator.api.Context;
-import io.javaoperatorsdk.operator.api.Controller;
-import io.javaoperatorsdk.operator.api.ResourceController;
-import io.javaoperatorsdk.operator.api.UpdateControl;
-import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
-
-@Controller(namespaces = Controller.WATCH_CURRENT_NAMESPACE)
-public class KaravanController implements ResourceController<Karavan> {
-
- @Inject
- KaravanResources karavanResources;
-
- @Inject
- KubernetesClient client;
-
- public KaravanController(KubernetesClient client) {
- this.client = client;
- }
-
- @Override
- public void init(EventSourceManager eventSourceManager) {
- }
-
- @Override
- public UpdateControl<Karavan> createOrUpdateResource(Karavan resource, Context<Karavan> context) {
- karavanResources.createResources(resource);
-
- resource.setStatus(new KaravanStatus());
-
- return UpdateControl.updateStatusSubResource(resource);
- }
-}
-
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanDeployment.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanDeployment.java
new file mode 100644
index 0000000..979775d
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanDeployment.java
@@ -0,0 +1,101 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder;
+import io.fabric8.kubernetes.api.model.ObjectFieldSelectorBuilder;
+import io.fabric8.kubernetes.api.model.OwnerReference;
+import io.fabric8.kubernetes.api.model.OwnerReferenceBuilder;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
+import io.fabric8.kubernetes.api.model.VolumeBuilder;
+import io.fabric8.kubernetes.api.model.VolumeMountBuilder;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+public class KaravanDeployment extends CRUDKubernetesDependentResource<Deployment, Karavan> {
+
+ @ConfigProperty(name = "karavan.version")
+ String version;
+
+ @ConfigProperty(name = "karavan.image")
+ String image;
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanDeployment() {
+ super(Deployment.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Deployment desired(Karavan karavan, Context<Karavan> context) {
+ return new DeploymentBuilder()
+ .withNewMetadata()
+ .withName(Constants.NAME)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.NAME, Map.of("app.kubernetes.io/runtime", "quarkus")))
+ .withOwnerReferences(this.createOwnerReference(karavan))
+ .endMetadata()
+
+ .withNewSpec()
+ .withReplicas(karavan.getSpec().getInstances())
+ .withNewSelector()
+ .addToMatchLabels(Map.of("app", Constants.NAME))
+ .endSelector()
+
+ .withNewTemplate()
+ .withNewMetadata()
+ .addToLabels(Map.of("app", Constants.NAME))
+ .endMetadata()
+
+ .withNewSpec()
+ .addNewContainer()
+ .withName(Constants.NAME)
+// .withImage(getImageName(karavan))
+ .withImage("ghcr.io/apache/camel-karavan:3.18.4") // TODO: set correct version after
+ .withImagePullPolicy("Always")
+ .addNewEnv()
+ .withName("KUBERNETES_NAMESPACE")
+ .withValueFrom(new EnvVarSourceBuilder().withFieldRef(new ObjectFieldSelectorBuilder().withFieldPath("metadata.namespace").build()).build())
+ .endEnv()
+ .addNewPort()
+ .withContainerPort(8080)
+ .withName(Constants.NAME)
+ .endPort()
+ .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("memory", new Quantity("2048Mi"))).build())
+ .withVolumeMounts(new VolumeMountBuilder().withName("karavan-data").withMountPath("/deployments/karavan-data").build())
+ .endContainer()
+ .withServiceAccount(Constants.NAME)
+ .withVolumes(new VolumeBuilder().withName("karavan-data").withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSource("karavan-data", false)).build())
+ .endSpec()
+ .endTemplate()
+ .endSpec()
+ .build();
+ }
+
+ private String getImageName(Karavan karavan) {
+ String auth = karavan.getSpec().getAuth();
+ switch (auth){
+ case "oidc": return image + "-oidc:" + version;
+ case "basic": return image + "-basic:" + version;
+ default: return image + ":" + version;
+ }
+ }
+
+ private OwnerReference createOwnerReference(Karavan resource) {
+ final var metadata = resource.getMetadata();
+ return new OwnerReferenceBuilder()
+ .withUid(metadata.getUid())
+ .withApiVersion(resource.getApiVersion())
+ .withName(metadata.getName())
+ .withKind(resource.getKind())
+ .build();
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcData.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcData.java
new file mode 100644
index 0000000..c66c56b
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcData.java
@@ -0,0 +1,53 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
+import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+
+public class KaravanPvcData extends CRUDKubernetesDependentResource<PersistentVolumeClaim, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanPvcData() {
+ super(PersistentVolumeClaim.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public PersistentVolumeClaim desired(Karavan karavan, Context<Karavan> context) {
+ return new PersistentVolumeClaimBuilder()
+ .withNewMetadata()
+ .withName(Constants.PVC_DATA)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.PVC_DATA, Map.of()))
+ .endMetadata()
+ .withNewSpec()
+ .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("10Gi"))).build())
+ .withVolumeMode("Filesystem")
+ .withAccessModes("ReadWriteOnce")
+ .endSpec()
+ .build();
+ }
+
+ @Override
+ public ReconcileResult<PersistentVolumeClaim> reconcile(Karavan karavan, Context<Karavan> context) {
+ PersistentVolumeClaim pvc = getKubernetesClient().persistentVolumeClaims().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.PVC_DATA).get();
+ if (pvc == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(pvc);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcJbang.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcJbang.java
new file mode 100644
index 0000000..16d79dd
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcJbang.java
@@ -0,0 +1,51 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+
+public class KaravanPvcJbang extends CRUDKubernetesDependentResource<PersistentVolumeClaim, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanPvcJbang() {
+ super(PersistentVolumeClaim.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public PersistentVolumeClaim desired(Karavan karavan, Context<Karavan> context) {
+ return new PersistentVolumeClaimBuilder()
+ .withNewMetadata()
+ .withName(Constants.PVC_JBANG)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.PVC_JBANG, Map.of()))
+ .endMetadata()
+ .withNewSpec()
+ .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("2Gi"))).build())
+ .withVolumeMode("Filesystem")
+ .withAccessModes("ReadWriteOnce")
+ .endSpec()
+ .build();
+ }
+
+ public ReconcileResult<PersistentVolumeClaim> reconcile(Karavan karavan, Context<Karavan> context) {
+ PersistentVolumeClaim pvc = getKubernetesClient().persistentVolumeClaims().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.PVC_JBANG).get();
+ if (pvc == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(pvc);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcM2Cache.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcM2Cache.java
new file mode 100644
index 0000000..c2d6c08
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanPvcM2Cache.java
@@ -0,0 +1,52 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
+import io.fabric8.kubernetes.api.model.Quantity;
+import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+
+public class KaravanPvcM2Cache extends CRUDKubernetesDependentResource<PersistentVolumeClaim, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanPvcM2Cache() {
+ super(PersistentVolumeClaim.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public PersistentVolumeClaim desired(Karavan karavan, Context<Karavan> context) {
+ return new PersistentVolumeClaimBuilder()
+ .withNewMetadata()
+ .withName(Constants.PVC_M2_CACHE)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.PVC_M2_CACHE, Map.of()))
+ .endMetadata()
+ .withNewSpec()
+ .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("10Gi"))).build())
+ .withVolumeMode("Filesystem")
+ .withAccessModes("ReadWriteOnce")
+ .endSpec()
+ .build();
+ }
+
+ @Override
+ public ReconcileResult<PersistentVolumeClaim> reconcile(Karavan karavan, Context<Karavan> context) {
+ PersistentVolumeClaim pvc = getKubernetesClient().persistentVolumeClaims().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.PVC_M2_CACHE).get();
+ if (pvc == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(pvc);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanReconciler.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanReconciler.java
new file mode 100644
index 0000000..50e85c0
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanReconciler.java
@@ -0,0 +1,77 @@
+package org.apache.camel.karavan;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ContextInitializer;
+import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
+import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
+import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_ALL_NAMESPACES;
+
+@ControllerConfiguration(namespaces = WATCH_ALL_NAMESPACES, name = "karavan", dependents = {
+ @Dependent(type = KaravanServiceAccount.class),
+ @Dependent(type = KaravanRole.class),
+ @Dependent(type = KaravanRoleBinding.class),
+ @Dependent(type = KaravanRoleBindingView.class),
+ @Dependent(type = KaravanPvcData.class, name = Constants.PVC_DATA),
+ @Dependent(type = KaravanPvcM2Cache.class, name = Constants.PVC_M2_CACHE),
+ @Dependent(type = KaravanPvcJbang.class, name = Constants.PVC_JBANG),
+ @Dependent(type = KaravanTektonTask.class),
+ @Dependent(type = KaravanTektonPipeline.class),
+ @Dependent(type = KaravanDeployment.class),
+ @Dependent(name = "service", type = KaravanService.class),
+// @Dependent(type = IngressDependent.class, dependsOn = "service", readyPostcondition = IngressDependent.class)
+})
+public class KaravanReconciler implements Reconciler<Karavan>, ContextInitializer<Karavan> {
+
+ static final Logger log = LoggerFactory.getLogger(KaravanReconciler.class);
+
+ @ConfigProperty(name = "karavan.version")
+ String version;
+
+ @Override
+ public void initContext(Karavan karavan, Context<Karavan> context) {
+
+ }
+
+ @Override
+ public UpdateControl<Karavan> reconcile(Karavan karavan, Context<Karavan> context) throws Exception {
+ final var name = karavan.getMetadata().getName();
+ final var namespace = karavan.getMetadata().getNamespace();
+ // retrieve the workflow reconciliation result and re-schedule if we have dependents that are not yet ready
+// return context.managedDependentResourceContext().getWorkflowReconcileResult()
+// .map(wrs -> {
+// if (wrs.allDependentResourcesReady()) {
+//// final var url = IngressDependent.getExposedURL(
+//// context.getSecondaryResource(Ingress.class).orElseThrow());
+// log.info("App {} is exposed and ready to be used at {}", name, namespace);
+ karavan.setStatus(new KaravanStatus(KaravanStatus.State.READY));
+ return UpdateControl.updateStatus(karavan);
+// } else {
+// final var duration = Duration.ofSeconds(5);
+// log.info("App {} is not ready yet, rescheduling reconciliation after {}s", name, duration.toSeconds());
+// return UpdateControl.<Karavan> noUpdate().rescheduleAfter(duration);
+// }
+// }).orElseThrow();
+ }
+
+ protected Map<String, String> getLabels(String name, Map<String, String> labels) {
+ Map<String, String> result = new HashMap<>(Map.of(
+ "app", name,
+ "app.kubernetes.io/name", name,
+ "app.kubernetes.io/version", version,
+ "app.kubernetes.io/part-of", Constants.NAME
+ ));
+ result.putAll(labels);
+ return result;
+ }
+}
+
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanResources.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanResources.java
deleted file mode 100644
index f0ae627..0000000
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanResources.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package org.apache.camel.karavan;
-
-import java.util.Map;
-import java.util.Optional;
-
-import javax.enterprise.context.ApplicationScoped;
-
-import io.fabric8.kubernetes.api.model.IntOrString;
-import io.fabric8.kubernetes.api.model.Service;
-import io.fabric8.kubernetes.api.model.ServiceBuilder;
-import io.fabric8.kubernetes.api.model.apps.Deployment;
-import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
-
-import static org.apache.camel.karavan.Constants.KARAVAN_IMAGE;
-
-@ApplicationScoped
-public class KaravanResources extends AbstractResources {
-
-
- public void createResources(Karavan karavan) {
- this.setKaravan(karavan);
- createFrontendDeployment(karavan);
- createFrontendService();
- }
-
- private void createFrontendDeployment(Karavan karavan) {
- Optional<Deployment> potentialDeployment = checkDeploymentExists(Constants.NAME);
-
- if (potentialDeployment.isEmpty()) {
- Deployment deployment1 = new DeploymentBuilder()
- .withNewMetadata()
- .withName(Constants.NAME)
- .withLabels(Constants.DEFAULT_LABELS)
- .withOwnerReferences(this.createOwnerReference(this.getKaravan()))
- .endMetadata()
-
- .withNewSpec()
- .withReplicas(karavan.getSpec().getInstances())
- .withNewSelector()
- .addToMatchLabels(Constants.DEFAULT_LABELS)
- .endSelector()
-
- .withNewTemplate()
- .withNewMetadata()
- .addToLabels(Constants.DEFAULT_LABELS)
- .endMetadata()
-
- .withNewSpec()
- .addNewContainer()
- .withName(Constants.NAME)
- .withImage(KARAVAN_IMAGE)
- .withImagePullPolicy("Always")
- .addNewEnv()
- .withName(Constants.KARAVAN_MODE)
- .withValue(karavan.getSpec().getMode())
- .endEnv()
- .addNewPort()
- .withContainerPort(8080)
- .withName(Constants.NAME)
- .endPort()
- .endContainer()
-// .withServiceAccount(Constants.NAME)
- .endSpec()
- .endTemplate()
- .endSpec()
- .build();
- client.apps().deployments().inNamespace(client.getNamespace()).create(deployment1);
- } else { //We are maybe dealing with an update
- //EnvVar envar = potentialDeployment.get().getSpec().getTemplate().getSpec().getContainers().get(0).
- client.apps().deployments().inNamespace(client.getNamespace())
- .withName(Constants.NAME).edit(d -> new DeploymentBuilder(d)
- .editSpec()
- .editTemplate().editSpec()
- .editFirstContainer()
- .editFirstEnv()
- .withValue(karavan.getSpec().getMode())
- .endEnv()
- .endContainer()
- .endSpec()
- .endTemplate()
- .endSpec()
- .build());
- }
- }
-
-
- private void createFrontendService() {
- if (checkServiceExists(Constants.NAME).isEmpty()) {
- Service service = new ServiceBuilder()
- .withNewMetadata()
- .withName(Constants.NAME)
- .withLabels(Constants.DEFAULT_LABELS)
- .endMetadata()
- .withNewSpec()
- .withType("NodePort")
- .addNewPort()
- .withName(Constants.NAME)
- .withPort(80)
- .withTargetPort(new IntOrString(8080))
- .withNodePort(31171)
- .withProtocol("TCP")
- .endPort()
- .withSelector(Constants.DEFAULT_LABELS)
- .endSpec()
- .build();
- client.services().inNamespace(client.getNamespace()).create(service);
- }
- }
-}
\ No newline at end of file
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRole.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRole.java
new file mode 100644
index 0000000..7280052
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRole.java
@@ -0,0 +1,53 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.rbac.PolicyRuleBuilder;
+import io.fabric8.kubernetes.api.model.rbac.Role;
+import io.fabric8.kubernetes.api.model.rbac.RoleBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.List;
+import java.util.Map;
+
+
+public class KaravanRole extends CRUDKubernetesDependentResource<Role, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanRole() {
+ super(Role.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Role desired(Karavan karavan, Context<Karavan> context) {
+ return new RoleBuilder()
+ .withNewMetadata()
+ .withName(Constants.ROLE_KARAVAN)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.ROLE_KARAVAN, Map.of()))
+ .endMetadata()
+ .withRules(
+ new PolicyRuleBuilder().withApiGroups("").withResources("secrets", "configmaps").withVerbs("get", "list").build(),
+ new PolicyRuleBuilder().withApiGroups("").withResources("persistentvolumes", "persistentvolumeclaims").withVerbs("get", "list", "watch").build(),
+ new PolicyRuleBuilder().withApiGroups("tekton.dev").withResources("pipelineruns").withVerbs("*").build(),
+ new PolicyRuleBuilder().withApiGroups("", "apps").withResources("deployments", "services", "routes", "replicationcontrollers").withVerbs("*").build()
+ )
+ .build();
+ }
+
+ @Override
+ public ReconcileResult<Role> reconcile(Karavan karavan, Context<Karavan> context) {
+ Role role = getKubernetesClient().rbac().roles().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.ROLE_KARAVAN).get();
+ if (role == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(role);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRoleBinding.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRoleBinding.java
new file mode 100644
index 0000000..b7bcafa
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRoleBinding.java
@@ -0,0 +1,48 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
+import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
+import io.fabric8.kubernetes.api.model.rbac.Subject;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+
+public class KaravanRoleBinding extends CRUDKubernetesDependentResource<RoleBinding, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanRoleBinding() {
+ super(RoleBinding.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public RoleBinding desired(Karavan karavan, Context<Karavan> context) {
+ return new RoleBindingBuilder()
+ .withNewMetadata()
+ .withName(Constants.ROLEBINDING_KARAVAN)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.ROLEBINDING_KARAVAN, Map.of()))
+ .endMetadata()
+ .withNewRoleRef("rbac.authorization.k8s.io", "Role", Constants.ROLE_KARAVAN)
+ .withSubjects(new Subject("", "ServiceAccount", Constants.SERVICEACCOUNT_KARAVAN, karavan.getMetadata().getNamespace()))
+ .build();
+ }
+
+ @Override
+ public ReconcileResult<RoleBinding> reconcile(Karavan karavan, Context<Karavan> context) {
+ RoleBinding role = getKubernetesClient().rbac().roleBindings().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.ROLEBINDING_KARAVAN).get();
+ if (role == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(role);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRoleBindingView.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRoleBindingView.java
new file mode 100644
index 0000000..e4147f5
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanRoleBindingView.java
@@ -0,0 +1,48 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
+import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
+import io.fabric8.kubernetes.api.model.rbac.Subject;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+
+public class KaravanRoleBindingView extends CRUDKubernetesDependentResource<RoleBinding, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanRoleBindingView() {
+ super(RoleBinding.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public RoleBinding desired(Karavan karavan, Context<Karavan> context) {
+ return new RoleBindingBuilder()
+ .withNewMetadata()
+ .withName(Constants.ROLEBINDING_KARAVAN_VIEW)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.ROLEBINDING_KARAVAN_VIEW, Map.of()))
+ .endMetadata()
+ .withNewRoleRef("rbac.authorization.k8s.io", "ClusterRole", "view")
+ .withSubjects(new Subject("", "ServiceAccount", Constants.SERVICEACCOUNT_KARAVAN, karavan.getMetadata().getNamespace()))
+ .build();
+ }
+
+ @Override
+ public ReconcileResult<RoleBinding> reconcile(Karavan karavan, Context<Karavan> context) {
+ RoleBinding role = getKubernetesClient().rbac().roleBindings().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.ROLEBINDING_KARAVAN_VIEW).get();
+ if (role == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(role);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanService.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanService.java
new file mode 100644
index 0000000..0214a8e
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanService.java
@@ -0,0 +1,44 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.IntOrString;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServiceBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+
+public class KaravanService extends CRUDKubernetesDependentResource<Service, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanService() {
+ super(Service.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Service desired(Karavan karavan, Context<Karavan> context) {
+ return new ServiceBuilder()
+ .withNewMetadata()
+ .withName(Constants.NAME)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.NAME, Map.of()))
+ .endMetadata()
+ .withNewSpec()
+ .withType("NodePort")
+ .addNewPort()
+ .withName(Constants.NAME)
+ .withPort(80)
+ .withTargetPort(new IntOrString(8080))
+ .withNodePort(karavan.getSpec().getNodePort())
+ .withProtocol("TCP")
+ .endPort()
+ .withSelector(Map.of("app", Constants.NAME))
+ .endSpec()
+ .build();
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanServiceAccount.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanServiceAccount.java
new file mode 100644
index 0000000..a5ed361
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanServiceAccount.java
@@ -0,0 +1,44 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.ServiceAccount;
+import io.fabric8.kubernetes.api.model.ServiceAccountBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+
+import javax.inject.Inject;
+import java.util.Map;
+
+public class KaravanServiceAccount extends CRUDKubernetesDependentResource<ServiceAccount, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ public KaravanServiceAccount() {
+ super(ServiceAccount.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ServiceAccount desired(Karavan karavan, Context<Karavan> context) {
+ return new ServiceAccountBuilder()
+ .withNewMetadata()
+ .withName(Constants.SERVICEACCOUNT_KARAVAN)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.SERVICEACCOUNT_KARAVAN, Map.of()))
+ .endMetadata()
+ .build();
+ }
+
+ @Override
+ public ReconcileResult<ServiceAccount> reconcile(Karavan karavan, Context<Karavan> context) {
+ ServiceAccount sa = getKubernetesClient().serviceAccounts().inNamespace(karavan.getMetadata().getNamespace()).withName(Constants.SERVICEACCOUNT_KARAVAN).get();
+ if (sa == null) {
+ var desired = desired(karavan, context);
+ var createdResource = handleCreate(desired, karavan, context);
+ return ReconcileResult.resourceCreated(createdResource);
+ } else {
+ return ReconcileResult.noOperation(sa);
+ }
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanSpec.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanSpec.java
index 970aeee..8277a91 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanSpec.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanSpec.java
@@ -3,7 +3,8 @@ package org.apache.camel.karavan;
public class KaravanSpec {
private int instances;
- private String mode;
+ private String auth;
+ private int nodePort;
public int getInstances() {
return instances;
@@ -13,11 +14,19 @@ public class KaravanSpec {
this.instances = instances;
}
- public String getMode() {
- return mode;
+ public String getAuth() {
+ return auth;
}
- public void setMode(String mode) {
- this.mode = mode;
+ public void setAuth(String auth) {
+ this.auth = auth;
+ }
+
+ public int getNodePort() {
+ return nodePort;
+ }
+
+ public void setNodePort(int nodePort) {
+ this.nodePort = nodePort;
}
}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanStatus.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanStatus.java
index e287550..980dd57 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanStatus.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanStatus.java
@@ -12,6 +12,19 @@ public class KaravanStatus {
private boolean error;
private String message;
+ public KaravanStatus() {
+ }
+
+ public KaravanStatus(State state) {
+ this.state = state;
+ }
+
+ public KaravanStatus(State state, boolean error, String message) {
+ this.state = state;
+ this.error = error;
+ this.message = message;
+ }
+
public State getState() {
return state;
}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanTektonPipeline.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanTektonPipeline.java
new file mode 100644
index 0000000..739653b
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanTektonPipeline.java
@@ -0,0 +1,75 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.EnvVarBuilder;
+import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.Param;
+import io.fabric8.tekton.pipeline.v1beta1.ParamBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.ParamSpecBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.Pipeline;
+import io.fabric8.tekton.pipeline.v1beta1.PipelineBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.PipelineTask;
+import io.fabric8.tekton.pipeline.v1beta1.PipelineTaskBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.PipelineWorkspaceDeclaration;
+import io.fabric8.tekton.pipeline.v1beta1.StepBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.Task;
+import io.fabric8.tekton.pipeline.v1beta1.TaskBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.TaskRef;
+import io.fabric8.tekton.pipeline.v1beta1.TaskRefBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.WorkspaceDeclaration;
+import io.fabric8.tekton.pipeline.v1beta1.WorkspacePipelineTaskBinding;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import javax.inject.Inject;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+public class KaravanTektonPipeline extends CRUDKubernetesDependentResource<Pipeline, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ @ConfigProperty(name = "karavan.version")
+ String version;
+
+ @ConfigProperty(name = "karavan.quarkus-build-image")
+ String image;
+
+ public KaravanTektonPipeline() {
+ super(Pipeline.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Pipeline desired(Karavan karavan, Context<Karavan> context) {
+ return new PipelineBuilder()
+ .withNewMetadata()
+ .withName(Constants.PIPELINE_BUILD_QUARKUS)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.PIPELINE_BUILD_QUARKUS, Map.of()))
+ .endMetadata()
+ .withNewSpec()
+ .withParams(new ParamSpecBuilder().withName("PROJECT_ID").withType("string").withDescription("ProjectId").build())
+ .withTasks(
+ new PipelineTaskBuilder().withName(Constants.TASK_BUILD_QUARKUS)
+ .withParams(new ParamBuilder().withName("project").withNewValue("$(params.PROJECT_ID)").build())
+ .withTaskRef(new TaskRefBuilder().withKind("Task").withName(Constants.TASK_BUILD_QUARKUS).build())
+ .withWorkspaces(
+ new WorkspacePipelineTaskBinding("m2-cache", "", "m2-cache"),
+ new WorkspacePipelineTaskBinding("jbang-cache", "", "jbang-cache")
+ )
+ .build()
+ )
+ .withWorkspaces(
+ new PipelineWorkspaceDeclaration("Maven Cache", "m2-cache", false),
+ new PipelineWorkspaceDeclaration("JBang Cache", "jbang-cache", false)
+ )
+ .endSpec()
+ .build();
+ }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanTektonTask.java b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanTektonTask.java
new file mode 100644
index 0000000..232c360
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/KaravanTektonTask.java
@@ -0,0 +1,95 @@
+package org.apache.camel.karavan;
+
+import io.fabric8.kubernetes.api.model.EnvVarBuilder;
+import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.ArrayOrString;
+import io.fabric8.tekton.pipeline.v1beta1.ParamSpec;
+import io.fabric8.tekton.pipeline.v1beta1.ParamSpecBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.StepBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.Task;
+import io.fabric8.tekton.pipeline.v1beta1.TaskBuilder;
+import io.fabric8.tekton.pipeline.v1beta1.WorkspaceDeclaration;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import javax.inject.Inject;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+public class KaravanTektonTask extends CRUDKubernetesDependentResource<Task, Karavan> {
+
+ @Inject
+ KaravanReconciler karavanReconciler;
+
+ @ConfigProperty(name = "karavan.version")
+ String version;
+
+ @ConfigProperty(name = "karavan.quarkus-build-image")
+ String image;
+
+ public KaravanTektonTask() {
+ super(Task.class);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Task desired(Karavan karavan, Context<Karavan> context) {
+ String script = getScript();
+ return new TaskBuilder()
+ .withNewMetadata()
+ .withName(Constants.TASK_BUILD_QUARKUS)
+ .withNamespace(karavan.getMetadata().getNamespace())
+ .withLabels(karavanReconciler.getLabels(Constants.TASK_BUILD_QUARKUS, Map.of()))
+ .endMetadata()
+ .withNewSpec()
+ .withParams(new ParamSpecBuilder().withName("project").withType("string").withDescription("ProjectId").build())
+ .withSteps(
+ new StepBuilder().withName("karavan-build-deploy")
+ .withScript(script)
+ .withImage(image + ":" + version)
+ .withEnv(
+ new EnvVarBuilder().withName("PROJECTS_GIT_REPOSITORY").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("projects-git-repository").and().build()).build(),
+ new EnvVarBuilder().withName("PROJECTS_GIT_USERNAME").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("projects-git-username").and().build()).build(),
+ new EnvVarBuilder().withName("PROJECTS_GIT_PASSWORD").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("projects-git-password").and().build()).build(),
+ new EnvVarBuilder().withName("PROJECTS_GIT_MAIN").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("projects-git-main").and().build()).build(),
+ new EnvVarBuilder().withName("KAMELETS_GIT_REPOSITORY").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("kamelets-git-repository").and().build()).build(),
+ new EnvVarBuilder().withName("KAMELETS_GIT_USERNAME").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("kamelets-git-username").and().build()).build(),
+ new EnvVarBuilder().withName("KAMELETS_GIT_PASSWORD").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("kamelets-git-password").and().build()).build(),
+ new EnvVarBuilder().withName("KAMELETS_GIT_MAIN").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("kamelets-git-main").and().build()).build(),
+ new EnvVarBuilder().withName("IMAGE_REGISTRY").withValueFrom(
+ new EnvVarSourceBuilder().withNewSecretKeyRef().withName("karavan").withKey("image-registry").and().build()).build()
+ )
+ .build()
+ )
+ .withWorkspaces(
+ new WorkspaceDeclaration("Maven Cache", "/root/.m2", "m2-cache", false, false),
+ new WorkspaceDeclaration("JBang Cache", "/jbang/.jbang/cache", "jbang-cache", false, false)
+ )
+ .endSpec()
+ .build();
+ }
+
+ protected String getScript() {
+ try {
+ InputStream inputStream = KaravanTektonTask.class.getResourceAsStream("/karavan-quarkus-builder-script.sh");
+ String data = new BufferedReader(new InputStreamReader(inputStream))
+ .lines().collect(Collectors.joining(System.getProperty("line.separator")));
+ return data;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/karavan-operator/src/main/resources/application.properties b/karavan-operator/src/main/resources/application.properties
index 2f80612..4d1ad1f 100644
--- a/karavan-operator/src/main/resources/application.properties
+++ b/karavan-operator/src/main/resources/application.properties
@@ -1,9 +1,13 @@
-quarkus.container-image.build=true
# set to true to automatically apply CRDs to the cluster when they get regenerated
quarkus.operator-sdk.crd.apply=true
quarkus.operator-sdk.crd.validate=false
+quarkus.container-image.build=true
quarkus.container-image.builder=jib
quarkus.container-image.group=apache
-quarkus.container-image.name=camel-karavan
-quarkus.container-image.tag=latest
\ No newline at end of file
+quarkus.container-image.name=camel-karavan-operator
+quarkus.container-image.tag=3.18.5
+
+karavan.version=3.18.5
+karavan.image=ghcr.io/apache/camel-karavan
+karavan.quarkus-build-image=ghcr.io/apache/camel-karavan-builder
diff --git a/karavan-operator/src/main/resources/karavan-quarkus-builder-script.sh b/karavan-operator/src/main/resources/karavan-quarkus-builder-script.sh
new file mode 100644
index 0000000..4b15ea5
--- /dev/null
+++ b/karavan-operator/src/main/resources/karavan-quarkus-builder-script.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+KAMELETS_DIR="/kamelets"
+
+if [[ $KAMELETS_GIT_REPOSITORY == https* ]] ;
+then
+ replacer=https://$KAMELETS_GIT_PASSWORD@
+ prefix=https://
+ url="${KAMELETS_GIT_REPOSITORY/$prefix/$replacer}"
+ git clone --depth 1 --branch ${KAMELETS_GIT_MAIN} $url ${KAMELETS_DIR}
+else
+ git clone --depth 1 --branch ${KAMELETS_GIT_MAIN} ${KAMELETS_GIT_REPOSITORY} ${KAMELETS_DIR}
+fi
+
+CHECKOUT_DIR="/scripts"
+
+if [[ $PROJECTS_GIT_REPOSITORY == https* ]] ;
+then
+ replacer=https://$PROJECTS_GIT_PASSWORD@
+ prefix=https://
+ url="${PROJECTS_GIT_REPOSITORY/$prefix/$replacer}"
+ git clone --depth 1 --branch ${PROJECTS_GIT_MAIN} $url ${CHECKOUT_DIR}
+else
+ git clone --depth 1 --branch ${PROJECTS_GIT_MAIN} ${PROJECTS_GIT_REPOSITORY} ${CHECKOUT_DIR}
+fi
+
+cd ${CHECKOUT_DIR}/$(inputs.params.project)
+
+entrypoint -Dcamel.jbang.version=3.18.2 camel@apache/camel export --local-kamelet-dir=${KAMELETS_DIR}
+
+export LAST_COMMIT=$(git rev-parse --short HEAD)
+export DATE=$(date '+%Y%m%d%H%M%S')
+export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
+export NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
+
+/opt/mvnd/bin/mvnd package \
+ -Dquarkus.container-image.build=true \
+ -Dquarkus.container-image.push=true \
+ -Dquarkus.container-image.insecure=true \
+ -Dquarkus.container-image.username=sa \
+ -Dquarkus.container-image.password=${TOKEN} \
+ -Dquarkus.container-image.registry=${IMAGE_REGISTRY} \
+ -Dquarkus.container-image.builder=jib \
+ -Dquarkus.kubernetes-client.master-url=kubernetes.default.svc \
+ -Dquarkus.kubernetes-client.token=${TOKEN} \
+ -Dquarkus.kubernetes.deploy=true \
+ -Dquarkus.openshift.deployment-kind=Deployment \
+ -Dquarkus.openshift.add-version-to-label-selectors=false \
+ -Dquarkus.openshift.labels.\"app.openshift.io/runtime\"=camel \
+ -Dquarkus.container-image.group=${NAMESPACE} \
+ -Dquarkus.container-image.tag=${DATE}
\ No newline at end of file