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/15 01:41:56 UTC

[camel-karavan] 01/02: Operator Refactoring

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 849145a8b3d69fa1a94048a4e2adfe171e56b949
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Fri Oct 14 21:39:24 2022 -0400

    Operator Refactoring
---
 .../camel/karavan/operator/KaravanReconciler.java  | 231 +++++++++++++++++----
 .../org/apache/camel/karavan/operator/Utils.java   |  20 ++
 .../operator/{ => resource}/KaravanDeployment.java |  11 +-
 .../operator/{ => resource}/KaravanPvcData.java    |  12 +-
 .../operator/{ => resource}/KaravanPvcJbang.java   |  12 +-
 .../operator/{ => resource}/KaravanPvcM2Cache.java |  12 +-
 .../operator/{ => resource}/KaravanRole.java       |  12 +-
 .../{ => resource}/KaravanRoleBinding.java         |  12 +-
 .../{ => resource}/KaravanRoleBindingView.java     |  12 +-
 .../operator/{ => resource}/KaravanRoute.java      |  11 +-
 .../operator/{ => resource}/KaravanService.java    |  12 +-
 .../{ => resource}/KaravanServiceAccount.java      |  11 +-
 .../{ => resource}/KaravanTektonPipeline.java      |  27 ++-
 .../operator/{ => resource}/KaravanTektonTask.java |  19 +-
 .../camel/karavan/operator/{ => spec}/Karavan.java |   3 +-
 .../{ => spec}/KaravanOperatorCSVMetadata.java     |   2 +-
 .../karavan/operator/{ => spec}/KaravanSpec.java   |   2 +-
 .../karavan/operator/{ => spec}/KaravanStatus.java |   2 +-
 .../karavan/operator/watcher/TektonCrdWatcher.java |  31 +++
 .../watcher/TektonSubscriptionWatcher.java         |  29 +++
 20 files changed, 345 insertions(+), 138 deletions(-)

diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanReconciler.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanReconciler.java
index 126feb9..9493d19 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanReconciler.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanReconciler.java
@@ -1,71 +1,212 @@
 package org.apache.camel.karavan.operator;
 
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
+import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionList;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.openshift.client.OpenShiftClient;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
+import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
+import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer;
 import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
 import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
-import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder;
+import io.javaoperatorsdk.operator.processing.event.source.EventSource;
+import org.apache.camel.karavan.operator.resource.KaravanDeployment;
+import org.apache.camel.karavan.operator.resource.KaravanPvcData;
+import org.apache.camel.karavan.operator.resource.KaravanPvcJbang;
+import org.apache.camel.karavan.operator.resource.KaravanPvcM2Cache;
+import org.apache.camel.karavan.operator.resource.KaravanRole;
+import org.apache.camel.karavan.operator.resource.KaravanRoleBinding;
+import org.apache.camel.karavan.operator.resource.KaravanRoleBindingView;
+import org.apache.camel.karavan.operator.resource.KaravanRoute;
+import org.apache.camel.karavan.operator.resource.KaravanService;
+import org.apache.camel.karavan.operator.resource.KaravanServiceAccount;
+import org.apache.camel.karavan.operator.resource.KaravanTektonPipeline;
+import org.apache.camel.karavan.operator.resource.KaravanTektonTask;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.spec.KaravanStatus;
+import org.apache.camel.karavan.operator.watcher.TektonCrdWatcher;
+import org.apache.camel.karavan.operator.watcher.TektonSubscriptionWatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.time.Duration;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_ALL_NAMESPACES;
 
-@ControllerConfiguration(namespaces = WATCH_ALL_NAMESPACES, name = "camel-karavan-operator",
-        dependents = {
-                @Dependent(name = "sa", type = KaravanServiceAccount.class),
-                @Dependent(name = "role", type = KaravanRole.class),
-                @Dependent(name = "role-binding", type = KaravanRoleBinding.class, dependsOn = {"role", "sa"}),
-                @Dependent(name = "role-binding-view", type = KaravanRoleBindingView.class),
-                @Dependent(name = Constants.PVC_DATA, type = KaravanPvcData.class),
-                @Dependent(name = Constants.PVC_M2_CACHE, type = KaravanPvcM2Cache.class),
-                @Dependent(name = Constants.PVC_JBANG_CACHE, type = KaravanPvcJbang.class),
-                @Dependent(name = Constants.TASK_BUILD_QUARKUS, type = KaravanTektonTask.class, dependsOn = {Constants.PVC_JBANG_CACHE, Constants.PVC_M2_CACHE}),
-                @Dependent(name = Constants.PIPELINE_BUILD_QUARKUS, type = KaravanTektonPipeline.class, dependsOn = {Constants.TASK_BUILD_QUARKUS, Constants.PVC_JBANG_CACHE, Constants.PVC_M2_CACHE}),
-                @Dependent(name = "deployment", type = KaravanDeployment.class, dependsOn = {Constants.PVC_DATA}),
-                @Dependent(name = "service", type = KaravanService.class, dependsOn = "deployment"),
-                @Dependent(type = KaravanRoute.class, dependsOn = "service", reconcilePrecondition = KaravanRoute.class)
-        })
-public class KaravanReconciler implements Reconciler<Karavan> {
+@ControllerConfiguration(namespaces = WATCH_ALL_NAMESPACES, name = "camel-karavan-operator")
+public class KaravanReconciler implements Reconciler<Karavan>, EventSourceInitializer<Karavan> {
 
     static final Logger log = LoggerFactory.getLogger(KaravanReconciler.class);
 
-    @ConfigProperty(name = "karavan.version")
-    String version;
+    private boolean isOpenShift = false;
+    private boolean isTektonInstalled = false;
+    private KubernetesClient client;
+    private Watch watcher;
+    private Workflow<Karavan> workflow;
+
+    private KaravanServiceAccount karavanServiceAccount;
+    private KaravanRole karavanRole;
+    private KaravanRoleBinding karavanRoleBinding;
+    private KaravanRoleBindingView karavanRoleBindingView;
+    private KaravanPvcData karavanPvcData;
+    private KaravanPvcM2Cache karavanPvcM2Cache;
+    private KaravanPvcJbang karavanPvcJbang;
+    private KaravanTektonTask karavanTektonTask;
+    private KaravanTektonPipeline karavanTektonPipeline;
+    private KaravanDeployment karavanDeployment;
+    private KaravanService karavanService;
+    private KaravanRoute karavanRoute;
+
+    public KaravanReconciler(KubernetesClient client) {
+        this.client = client;
+        checkKubernetes();
+        checkTektonInstalled();
+        addSubscriptionWatcher();
+        initDependentResources();
+        createWorkflow();
+        System.out.println("isTektonInstalled = " + isTektonInstalled);
+    }
 
     @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()) {
-                        log.info("Karavan is exposed and ready to be used at '{}' namespace", namespace);
-                        karavan.setStatus(new KaravanStatus(KaravanStatus.State.READY));
-                        return UpdateControl.updateStatus(karavan);
-                    } else {
-                        final var duration = Duration.ofSeconds(5);
-                        log.info("Karavan is not ready yet, rescheduling reconciliation after {}s", name, duration.toSeconds());
-                        return UpdateControl.<Karavan>noUpdate().rescheduleAfter(duration);
-                    }
-                }).orElseThrow();
+        WorkflowReconcileResult result = workflow.reconcile(karavan, context);
+        if (result.allDependentResourcesReady()) {
+            log.info("Karavan is exposed and ready to be used at '{}' namespace", namespace);
+            karavan.setStatus(new KaravanStatus(KaravanStatus.State.READY));
+            return UpdateControl.updateStatus(karavan);
+        } else {
+            final var duration = Duration.ofSeconds(5);
+            log.info("Karavan is not ready yet, rescheduling reconciliation after {}s", name, duration.toSeconds());
+            return UpdateControl.<Karavan>noUpdate().rescheduleAfter(duration);
+        }
+    }
+
+    private void addSubscriptionWatcher() {
+        if (this.isOpenShift) {
+            this.watcher = client.adapt(OpenShiftClient.class).operatorHub().subscriptions().watch(new TektonSubscriptionWatcher(this));
+        } else {
+            this.watcher = client.apiextensions().v1().customResourceDefinitions().watch(new TektonCrdWatcher(this));
+        }
+    }
+    private void checkKubernetes() {
+        if (client.isAdaptable(OpenShiftClient.class)) {
+            this.isOpenShift = true;
+        }
+    }
+
+    private void checkTektonInstalled() {
+        CustomResourceDefinitionList list = client.apiextensions().v1().customResourceDefinitions().list();
+        if (list != null) {
+            List<CustomResourceDefinition> items = list.getItems();
+            long crds = items.stream().filter(crd -> crd.getMetadata().getName().equalsIgnoreCase("pipelines.tekton.dev")
+                    || crd.getMetadata().getName().equalsIgnoreCase("tasks.tekton.dev")
+            ).count();
+            if (crds == 2) {
+                if (this.isOpenShift) {
+                    long oper = client.adapt(OpenShiftClient.class).operatorHub().subscriptions().list().getItems().stream()
+                            .filter(sub -> sub.getMetadata().getName().contains("openshift-pipelines-operator")).count();
+                    this.isTektonInstalled = oper > 0;
+                } else {
+                    this.isTektonInstalled = true;
+                }
+            }
+        }
+    }
+
+    private void createWorkflow() {
+        WorkflowBuilder workflowBuilder = new WorkflowBuilder<Karavan>();
+        workflowBuilder.addDependentResource(karavanServiceAccount);
+        workflowBuilder.addDependentResource(karavanRole);
+        workflowBuilder.addDependentResource(karavanRoleBinding);
+        workflowBuilder.addDependentResource(karavanRoleBindingView);
+        workflowBuilder.addDependentResource(karavanPvcData);
+        workflowBuilder.addDependentResource(karavanPvcM2Cache);
+        workflowBuilder.addDependentResource(karavanPvcJbang);
+        workflowBuilder.addDependentResource(karavanDeployment);
+        workflowBuilder.addDependentResource(karavanService);
+        if (isOpenShift){
+            workflowBuilder.addDependentResource(karavanRoute);
+        }
+        if (isTektonInstalled){
+            workflowBuilder.addDependentResource(karavanTektonPipeline);
+            workflowBuilder.addDependentResource(karavanTektonTask);
+        }
+        this.workflow = workflowBuilder.build();
+    }
+
+    private void initDependentResources() {
+        this.karavanServiceAccount = new KaravanServiceAccount();
+        this.karavanRole = new KaravanRole();
+        this.karavanRoleBinding = new KaravanRoleBinding();
+        this.karavanRoleBindingView = new KaravanRoleBindingView();
+        this.karavanPvcData = new KaravanPvcData();
+        this.karavanPvcM2Cache = new KaravanPvcM2Cache();
+        this.karavanPvcJbang = new KaravanPvcJbang();
+        this.karavanDeployment = new KaravanDeployment();
+        this.karavanService = new KaravanService();
+
+        if (isOpenShift) {
+            this.karavanRoute = new KaravanRoute();
+        }
+        if (isTektonInstalled) {
+            this.karavanTektonTask = new KaravanTektonTask();
+            this.karavanTektonPipeline = new KaravanTektonPipeline();
+        }
+        getResources().forEach(dr -> {
+            dr.setKubernetesClient(client);
+            dr.configureWith(new KubernetesDependentResourceConfig());
+        });
+    }
+
+    public void addTektonResources() {
+        isTektonInstalled = true;
+        if (karavanTektonTask == null) {
+            karavanTektonTask = new KaravanTektonTask();
+            karavanTektonTask.setKubernetesClient(client);
+            karavanTektonTask.configureWith(new KubernetesDependentResourceConfig());
+        }
+        if (karavanTektonPipeline == null) {
+            karavanTektonPipeline = new KaravanTektonPipeline();
+            karavanTektonPipeline.setKubernetesClient(client);
+            karavanTektonPipeline.configureWith(new KubernetesDependentResourceConfig());
+        }
+        createWorkflow();
+    }
+
+    @Override
+    public Map<String, EventSource> prepareEventSources(EventSourceContext<Karavan> context) {
+        List<EventSource> list = getResources().stream().map(crd -> crd.initEventSource(context)).collect(Collectors.toList());
+        return EventSourceInitializer.nameEventSources(list.toArray(new EventSource[list.size()]));
     }
 
-    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
+    private List<CRUDKubernetesDependentResource> getResources(){
+        List<CRUDKubernetesDependentResource> list = new ArrayList<>(Arrays.asList(
+                karavanServiceAccount, karavanRole, karavanRoleBinding, karavanRoleBindingView,
+                karavanPvcData, karavanPvcM2Cache, karavanPvcJbang,
+                karavanDeployment, karavanService
         ));
-        result.putAll(labels);
-        return result;
+        if (isOpenShift) {
+            list.add(karavanRoute);
+        }
+        if (isTektonInstalled) {
+            list.add(karavanTektonPipeline);
+            list.add(karavanTektonTask);
+        }
+        return list;
     }
 }
 
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/Utils.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/Utils.java
new file mode 100644
index 0000000..bdca4d9
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/Utils.java
@@ -0,0 +1,20 @@
+package org.apache.camel.karavan.operator;
+
+import org.eclipse.microprofile.config.ConfigProvider;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Utils {
+
+    public static 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", ConfigProvider.getConfig().getValue("karavan.version", String.class),
+                "app.kubernetes.io/part-of", Constants.NAME
+        ));
+        result.putAll(labels);
+        return result;
+    }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanDeployment.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanDeployment.java
similarity index 94%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanDeployment.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanDeployment.java
index a7a2459..10bf946 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanDeployment.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanDeployment.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.EnvVar;
 import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder;
@@ -15,9 +15,11 @@ 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.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 
-import javax.inject.Inject;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -30,9 +32,6 @@ public class KaravanDeployment extends CRUDKubernetesDependentResource<Deploymen
     @ConfigProperty(name = "karavan.image")
     String baseImage;
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanDeployment() {
         super(Deployment.class);
     }
@@ -69,7 +68,7 @@ public class KaravanDeployment extends CRUDKubernetesDependentResource<Deploymen
                 .withNewMetadata()
                 .withName(Constants.NAME)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.NAME, Map.of("app.kubernetes.io/runtime", "quarkus")))
+                .withLabels(Utils.getLabels(Constants.NAME, Map.of("app.kubernetes.io/runtime", "quarkus")))
                 .withOwnerReferences(this.createOwnerReference(karavan))
                 .endMetadata()
 
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcData.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcData.java
similarity index 87%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcData.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcData.java
index d1cfe21..f15a64d 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcData.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcData.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
@@ -7,16 +7,14 @@ 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanPvcData extends CRUDKubernetesDependentResource<PersistentVolumeClaim, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanPvcData() {
         super(PersistentVolumeClaim.class);
     }
@@ -28,7 +26,7 @@ public class KaravanPvcData extends CRUDKubernetesDependentResource<PersistentVo
                 .withNewMetadata()
                 .withName(Constants.PVC_DATA)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.PVC_DATA, Map.of()))
+                .withLabels(Utils.getLabels(Constants.PVC_DATA, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("10Gi"))).build())
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcJbang.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcJbang.java
similarity index 87%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcJbang.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcJbang.java
index 4003ffb..de678be 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcJbang.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcJbang.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
@@ -7,16 +7,14 @@ 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanPvcJbang extends CRUDKubernetesDependentResource<PersistentVolumeClaim, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanPvcJbang() {
         super(PersistentVolumeClaim.class);
     }
@@ -28,7 +26,7 @@ public class KaravanPvcJbang extends CRUDKubernetesDependentResource<PersistentV
                 .withNewMetadata()
                 .withName(Constants.PVC_JBANG_CACHE)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.PVC_JBANG_CACHE, Map.of()))
+                .withLabels(Utils.getLabels(Constants.PVC_JBANG_CACHE, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("2Gi"))).build())
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcM2Cache.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcM2Cache.java
similarity index 87%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcM2Cache.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcM2Cache.java
index 7bcda77..7ebbb18 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanPvcM2Cache.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanPvcM2Cache.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
@@ -7,16 +7,14 @@ 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanPvcM2Cache extends CRUDKubernetesDependentResource<PersistentVolumeClaim, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanPvcM2Cache() {
         super(PersistentVolumeClaim.class);
     }
@@ -28,7 +26,7 @@ public class KaravanPvcM2Cache extends CRUDKubernetesDependentResource<Persisten
                 .withNewMetadata()
                 .withName(Constants.PVC_M2_CACHE)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.PVC_M2_CACHE, Map.of()))
+                .withLabels(Utils.getLabels(Constants.PVC_M2_CACHE, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("10Gi"))).build())
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRole.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java
similarity index 88%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRole.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java
index 80c446f..1579be8 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRole.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.rbac.PolicyRuleBuilder;
 import io.fabric8.kubernetes.api.model.rbac.Role;
@@ -6,16 +6,14 @@ 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanRole extends CRUDKubernetesDependentResource<Role, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanRole() {
         super(Role.class);
     }
@@ -27,7 +25,7 @@ public class KaravanRole extends CRUDKubernetesDependentResource<Role, Karavan>
                 .withNewMetadata()
                 .withName(Constants.ROLE_KARAVAN)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.ROLE_KARAVAN, Map.of()))
+                .withLabels(Utils.getLabels(Constants.ROLE_KARAVAN, Map.of()))
                 .endMetadata()
                 .withRules(
                         new PolicyRuleBuilder().withApiGroups("").withResources("secrets", "configmaps").withVerbs("get", "list").build(),
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoleBinding.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoleBinding.java
similarity index 86%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoleBinding.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoleBinding.java
index 2876dd8..acf627d 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoleBinding.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoleBinding.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
 import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
@@ -6,16 +6,14 @@ 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanRoleBinding extends CRUDKubernetesDependentResource<RoleBinding, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanRoleBinding() {
         super(RoleBinding.class);
     }
@@ -27,7 +25,7 @@ public class KaravanRoleBinding extends CRUDKubernetesDependentResource<RoleBind
                 .withNewMetadata()
                 .withName(Constants.ROLEBINDING_KARAVAN)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.ROLEBINDING_KARAVAN, Map.of()))
+                .withLabels(Utils.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()))
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoleBindingView.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoleBindingView.java
similarity index 85%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoleBindingView.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoleBindingView.java
index fc35174..cb744b1 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoleBindingView.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoleBindingView.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
 import io.fabric8.kubernetes.api.model.rbac.RoleBindingBuilder;
@@ -6,16 +6,14 @@ 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanRoleBindingView extends CRUDKubernetesDependentResource<RoleBinding, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanRoleBindingView() {
         super(RoleBinding.class);
     }
@@ -27,7 +25,7 @@ public class KaravanRoleBindingView extends CRUDKubernetesDependentResource<Role
                 .withNewMetadata()
                 .withName(Constants.ROLEBINDING_KARAVAN_VIEW)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.ROLEBINDING_KARAVAN_VIEW, Map.of()))
+                .withLabels(Utils.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()))
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoute.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoute.java
similarity index 86%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoute.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoute.java
index b502b94..ada906f 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanRoute.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRoute.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.IntOrString;
 import io.fabric8.kubernetes.client.DefaultKubernetesClient;
@@ -11,15 +11,14 @@ import io.fabric8.openshift.client.OpenShiftClient;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
 import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+import org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
 public class KaravanRoute extends CRUDKubernetesDependentResource<Route, Karavan> implements Condition<Route, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanRoute() {
         super(Route.class);
     }
@@ -31,7 +30,7 @@ public class KaravanRoute extends CRUDKubernetesDependentResource<Route, Karavan
                 .withNewMetadata()
                 .withName(Constants.NAME)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.NAME, Map.of()))
+                .withLabels(Utils.getLabels(Constants.NAME, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withPort(new RoutePort(new IntOrString(8080)))
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanService.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanService.java
similarity index 82%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanService.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanService.java
index fb275a5..6aafc97 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanService.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanService.java
@@ -1,20 +1,18 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
 public class KaravanService extends CRUDKubernetesDependentResource<Service, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanService() {
         super(Service.class);
     }
@@ -26,7 +24,7 @@ public class KaravanService extends CRUDKubernetesDependentResource<Service, Kar
                 .withNewMetadata()
                 .withName(Constants.NAME)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.NAME, Map.of()))
+                .withLabels(Utils.getLabels(Constants.NAME, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withType("NodePort")
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanServiceAccount.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanServiceAccount.java
similarity index 83%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanServiceAccount.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanServiceAccount.java
index 5915a0a..a36b8e7 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanServiceAccount.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanServiceAccount.java
@@ -1,19 +1,18 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 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 org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
 public class KaravanServiceAccount extends CRUDKubernetesDependentResource<ServiceAccount, Karavan> {
 
-    @Inject
-    KaravanReconciler karavanReconciler;
-
     public KaravanServiceAccount() {
         super(ServiceAccount.class);
     }
@@ -25,7 +24,7 @@ public class KaravanServiceAccount extends CRUDKubernetesDependentResource<Servi
                 .withNewMetadata()
                 .withName(Constants.SERVICEACCOUNT_KARAVAN)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.SERVICEACCOUNT_KARAVAN, Map.of()))
+                .withLabels(Utils.getLabels(Constants.SERVICEACCOUNT_KARAVAN, Map.of()))
                 .endMetadata()
                 .build();
     }
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanTektonPipeline.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanTektonPipeline.java
similarity index 84%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanTektonPipeline.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanTektonPipeline.java
index ec136b1..2541820 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanTektonPipeline.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanTektonPipeline.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.tekton.client.DefaultTektonClient;
 import io.fabric8.tekton.pipeline.v1beta1.ParamBuilder;
@@ -12,22 +12,14 @@ import io.fabric8.tekton.pipeline.v1beta1.WorkspacePipelineTaskBinding;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+import org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 
-import javax.inject.Inject;
 import java.util.Map;
 
-
-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 class KaravanTektonPipeline extends CRUDKubernetesDependentResource<Pipeline, Karavan>  implements Condition<Pipeline, Karavan> {
 
     public KaravanTektonPipeline() {
         super(Pipeline.class);
@@ -40,7 +32,7 @@ public class KaravanTektonPipeline extends CRUDKubernetesDependentResource<Pipel
                 .withNewMetadata()
                 .withName(Constants.PIPELINE_BUILD_QUARKUS)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.PIPELINE_BUILD_QUARKUS, Map.of()))
+                .withLabels(Utils.getLabels(Constants.PIPELINE_BUILD_QUARKUS, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withParams(new ParamSpecBuilder().withName("PROJECT_ID").withType("string").withDescription("ProjectId").build())
@@ -73,4 +65,9 @@ public class KaravanTektonPipeline extends CRUDKubernetesDependentResource<Pipel
             return ReconcileResult.noOperation(pipeline);
         }
     }
+
+    @Override
+    public boolean isMet(Karavan karavan, Pipeline pipeline, Context<Karavan> context) {
+        return false;
+    }
 }
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanTektonTask.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanTektonTask.java
similarity index 93%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanTektonTask.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanTektonTask.java
index 7c66bd2..253b974 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanTektonTask.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanTektonTask.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.resource;
 
 import io.fabric8.kubernetes.api.model.EnvVarBuilder;
 import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder;
@@ -15,19 +15,19 @@ import io.fabric8.tekton.pipeline.v1beta1.WorkspaceDeclaration;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+import org.apache.camel.karavan.operator.Constants;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.Utils;
 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;
+public class KaravanTektonTask extends CRUDKubernetesDependentResource<Task, Karavan>  implements Condition<Task, Karavan>  {
 
     @ConfigProperty(name = "karavan.version")
     String version;
@@ -47,7 +47,7 @@ public class KaravanTektonTask extends CRUDKubernetesDependentResource<Task, Kar
                 .withNewMetadata()
                 .withName(Constants.TASK_BUILD_QUARKUS)
                 .withNamespace(karavan.getMetadata().getNamespace())
-                .withLabels(karavanReconciler.getLabels(Constants.TASK_BUILD_QUARKUS, Map.of()))
+                .withLabels(Utils.getLabels(Constants.TASK_BUILD_QUARKUS, Map.of()))
                 .endMetadata()
                 .withNewSpec()
                 .withParams(new ParamSpecBuilder().withName("project").withType("string").withDescription("ProjectId").build())
@@ -135,4 +135,9 @@ public class KaravanTektonTask extends CRUDKubernetesDependentResource<Task, Kar
             return defaultValue;
         }
     }
+
+    @Override
+    public boolean isMet(Karavan karavan, Task task, Context<Karavan> context) {
+        return false;
+    }
 }
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/Karavan.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/Karavan.java
similarity index 79%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/Karavan.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/Karavan.java
index f01bd69..69d3d62 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/Karavan.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/Karavan.java
@@ -1,8 +1,9 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.spec;
 
 import io.fabric8.kubernetes.api.model.Namespaced;
 import io.fabric8.kubernetes.client.CustomResource;
 import io.fabric8.kubernetes.model.annotation.*;
+import org.apache.camel.karavan.operator.Constants;
 
 @Group(Constants.CRD_GROUP)
 @Version(Constants.CRD_VERSION)
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanOperatorCSVMetadata.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanOperatorCSVMetadata.java
similarity index 98%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanOperatorCSVMetadata.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanOperatorCSVMetadata.java
index 7772a74..7f5af4c 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanOperatorCSVMetadata.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanOperatorCSVMetadata.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.spec;
 
 import io.quarkiverse.operatorsdk.bundle.runtime.CSVMetadata;
 import io.quarkiverse.operatorsdk.bundle.runtime.SharedCSVMetadata;
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanSpec.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java
similarity index 93%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanSpec.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java
index 83f4ee3..c6c2f33 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanSpec.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.spec;
 
 public class KaravanSpec {
 
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanStatus.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanStatus.java
similarity index 95%
rename from karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanStatus.java
rename to karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanStatus.java
index 2525a91..43f8055 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/KaravanStatus.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanStatus.java
@@ -1,4 +1,4 @@
-package org.apache.camel.karavan.operator;
+package org.apache.camel.karavan.operator.spec;
 
 public class KaravanStatus {
 
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/watcher/TektonCrdWatcher.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/watcher/TektonCrdWatcher.java
new file mode 100644
index 0000000..344fe32
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/watcher/TektonCrdWatcher.java
@@ -0,0 +1,31 @@
+package org.apache.camel.karavan.operator.watcher;
+
+import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import org.apache.camel.karavan.operator.KaravanReconciler;
+
+import java.util.List;
+
+public class TektonCrdWatcher implements Watcher<CustomResourceDefinition> {
+
+    private KaravanReconciler karavanReconciler;
+
+    public TektonCrdWatcher(KaravanReconciler karavanReconciler) {
+        this.karavanReconciler = karavanReconciler;
+    }
+
+    @Override
+    public void eventReceived(Action action, CustomResourceDefinition resource) {
+        if (List.of("MODIFIED", "ADDED").contains(action.name())) {
+            if (List.of("ADDED").contains(action.name()) && resource.getMetadata().getName().contains("pipelines.tekton.dev")) {
+                karavanReconciler.addTektonResources();
+            }
+        }
+    }
+
+    @Override
+    public void onClose(WatcherException cause) {
+
+    }
+}
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/watcher/TektonSubscriptionWatcher.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/watcher/TektonSubscriptionWatcher.java
new file mode 100644
index 0000000..6aeafb7
--- /dev/null
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/watcher/TektonSubscriptionWatcher.java
@@ -0,0 +1,29 @@
+package org.apache.camel.karavan.operator.watcher;
+
+import io.fabric8.kubernetes.client.Watcher;
+import io.fabric8.kubernetes.client.WatcherException;
+import io.fabric8.openshift.api.model.operatorhub.v1alpha1.Subscription;
+import org.apache.camel.karavan.operator.KaravanReconciler;
+
+import java.util.List;
+
+public class TektonSubscriptionWatcher implements Watcher<Subscription> {
+
+    private KaravanReconciler karavanReconciler;
+
+    public TektonSubscriptionWatcher(KaravanReconciler karavanReconciler) {
+        this.karavanReconciler = karavanReconciler;
+    }
+
+    @Override
+    public void eventReceived(Action action, Subscription resource) {
+        if (List.of("ADDED").contains(action.name()) && resource.getMetadata().getName().contains("openshift-pipelines-operator")) {
+            karavanReconciler.addTektonResources();
+        }
+    }
+
+    @Override
+    public void onClose(WatcherException cause) {
+
+    }
+}