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 2023/03/22 23:22:31 UTC

[camel-karavan] branch main updated: Add basic integration and e2e tests, env setup (#682)

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


The following commit(s) were added to refs/heads/main by this push:
     new 7d84fe5d Add basic integration and e2e tests, env setup (#682)
7d84fe5d is described below

commit 7d84fe5d21cc08e617267f8009a22c6123563769
Author: Dmitry Volodin <dm...@gmail.com>
AuthorDate: Thu Mar 23 02:22:25 2023 +0300

    Add basic integration and e2e tests, env setup (#682)
    
    * Add basic integration and e2e tests, env setup
    
    * Fix RBAC and optimize tests running
    
    * Fix actions script
    
    * Fix docker exec command
---
 .github/workflows/operator.yml                     |  19 ++
 .github/workflows/vscode.yml                       |   2 +-
 karavan-operator/Makefile                          |  13 +-
 karavan-operator/pom.xml                           |  53 +++++-
 .../camel/karavan/operator/KaravanReconciler.java  |   4 +-
 .../karavan/operator/resource/KaravanRole.java     |   3 +-
 .../camel/karavan/operator/spec/KaravanSpec.java   |  12 ++
 .../karavan/operator/KaravanReconcilerE2E.java     | 146 +++++++++++++++
 .../karavan/operator/KaravanReconcilerTest.java    |  65 +++++++
 .../src/test/resources/application.properties      |   3 +
 .../src/test/resources/kubernetes/kubernetes.yaml  | 202 +++++++++++++++++++++
 .../src/test/resources/kubernetes/pipelines.yaml   |  40 ++++
 .../src/test/resources/kubernetes/tasks.yaml       |  73 ++++++++
 13 files changed, 629 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/operator.yml b/.github/workflows/operator.yml
index 4f35a47b..c235653e 100644
--- a/.github/workflows/operator.yml
+++ b/.github/workflows/operator.yml
@@ -6,6 +6,7 @@ on:
   workflow_dispatch:
   pull_request:
     branches: [ main ]
+    paths: ['karavan-operator/**']
 
 env:
   REGISTRY: ghcr.io
@@ -38,6 +39,24 @@ jobs:
           key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
           restore-keys: ${{ runner.os }}-maven-
 
+      - name: Kubernetes KinD Cluster
+        uses: container-tools/kind-action@v2
+        with:
+          version: v0.17.0
+          node_image: "kindest/node:v1.24.7"
+          registry: false
+
+      - name: Test operator
+        run: |
+          mvn test -f karavan-operator -Pintegration-tests
+
+      - name: Test operator (end-to-end)
+        run: |
+          cd karavan-operator
+          make test-deploy-kind
+          mvn test -Pend-to-end-tests
+          make test-undeploy-kind
+
       #  Build Karavan operator
       - name: Build operator
         run: |
diff --git a/.github/workflows/vscode.yml b/.github/workflows/vscode.yml
index f0be3e8f..2b90b49e 100644
--- a/.github/workflows/vscode.yml
+++ b/.github/workflows/vscode.yml
@@ -1,4 +1,4 @@
-name: vscode xtension
+name: vscode extension
 
 on:
   push:
diff --git a/karavan-operator/Makefile b/karavan-operator/Makefile
index ac3df03f..d1897f0e 100644
--- a/karavan-operator/Makefile
+++ b/karavan-operator/Makefile
@@ -55,7 +55,7 @@ help: ## Display this help.
 ##@ Build
 
 docker-build: ## Build docker image with the manager.
-	mvn package -Dquarkus.container-image.build=true -Dquarkus.container-image.image=${IMG}
+	mvn clean package -Dquarkus.container-image.build=true -Dquarkus.container-image.image=${IMG}
 
 docker-push: ## Push docker image with the manager.
 	mvn package -Dquarkus.container-image.push=true -Dquarkus.container-image.image=${IMG}
@@ -74,6 +74,17 @@ 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
 
+##@ Test
+
+test-deploy-kind: docker-build install ##   Build and deploy controller for tests to the default KinD cluster.
+	kind load docker-image ${IMG}
+	kubectl apply -f src/test/resources/kubernetes/kubernetes.yaml
+	kubectl wait deployment camel-karavan-operator -n camel-karavan-operator --for condition=Available=True --timeout=120s
+
+test-undeploy-kind: install ## Undeploy controller from the test default KinD cluster.
+	kubectl delete -f src/test/resources/kubernetes/kubernetes.yaml
+	docker exec $(shell kind get clusters | head -1)-control-plane crictl rmi ${IMG}
+
 ##@ Bundle
 .PHONY: bundle
 bundle: ## Generate bundle manifests and metadata, then validate generated files.
diff --git a/karavan-operator/pom.xml b/karavan-operator/pom.xml
index b7f5eebd..5bdd3e77 100644
--- a/karavan-operator/pom.xml
+++ b/karavan-operator/pom.xml
@@ -10,6 +10,7 @@
     <packaging>jar</packaging>
     <properties>
         <compiler-plugin.version>3.8.1</compiler-plugin.version>
+        <surefire-plugin.version>3.0.0-M9</surefire-plugin.version>
         <maven.compiler.parameters>true</maven.compiler.parameters>
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
@@ -54,6 +55,10 @@
             <groupId>io.quarkiverse.operatorsdk</groupId>
             <artifactId>quarkus-operator-sdk-bundle-generator</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-container-image-jib</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>
@@ -65,6 +70,11 @@
             <artifactId>quarkus-junit5</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -96,6 +106,47 @@
                 <quarkus.package.type>native</quarkus.package.type>
             </properties>
         </profile>
+        <profile>
+            <id>end-to-end-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>${surefire-plugin.version}</version>
+                        <configuration>
+                            <includes>
+                                <include>**/*E2E.java</include>
+                            </includes>
+                            <excludes>
+                                <exclude>**/*Test.java</exclude>
+                                <exclude>**/*IT.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>integration-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>${surefire-plugin.version}</version>
+                        <configuration>
+                            <includes>
+                                <include>**/*Test.java</include>
+                                <include>**/*IT.java</include>
+                            </includes>
+                            <excludes>
+                                <exclude>**/*E2E.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
-
 </project>
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 93e2333c..f1adc6f5 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
@@ -70,7 +70,7 @@ public class KaravanReconciler implements Reconciler<Karavan>, EventSourceInitia
     static final Logger log = LoggerFactory.getLogger(KaravanReconciler.class);
 
     private boolean isOpenShift;
-    private boolean initTektonInstalled;
+    private final boolean initTektonInstalled;
     private KubernetesClient client;
     private Workflow<Karavan> workflow;
     private KaravanServiceAccount karavanServiceAccount;
@@ -172,7 +172,7 @@ public class KaravanReconciler implements Reconciler<Karavan>, EventSourceInitia
         return EventSourceInitializer.nameEventSources(list.toArray(new EventSource[list.size()]));
     }
 
-    private List<CRUDKubernetesDependentResource> getResources(){
+    private List<CRUDKubernetesDependentResource> getResources() {
         List<CRUDKubernetesDependentResource> list = new ArrayList<>(Arrays.asList(
                 karavanServiceAccount, karavanRole, karavanRoleBinding, karavanRoleBindingView,
                 karavanPvcData, karavanPvcM2Cache, karavanPvcJbang,
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java
index 8bf2c52f..40985977 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/resource/KaravanRole.java
@@ -43,7 +43,8 @@ public class KaravanRole extends CRUDKubernetesDependentResource<Role, Karavan>
                         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("").withResources("pods", "services", "routes", "replicationcontrollers").withVerbs("*").build(),
+                        new PolicyRuleBuilder().withApiGroups("").withResources("pods", "services", "replicationcontrollers").withVerbs("*").build(),
+                        new PolicyRuleBuilder().withApiGroups("route.openshift.io").withResources( "routes").withVerbs("*").build(),
                         new PolicyRuleBuilder().withApiGroups("apps").withResources("deployments").withVerbs("*").build()
                         )
                 .build();
diff --git a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java
index 6ea24684..44c1d83f 100644
--- a/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java
+++ b/karavan-operator/src/main/java/org/apache/camel/karavan/operator/spec/KaravanSpec.java
@@ -25,6 +25,18 @@ public class KaravanSpec {
     private int nodePort;
     private String gitPullInterval;
 
+    public KaravanSpec() {
+    }
+
+    public KaravanSpec(int instances, String auth, String environment, String runtimes, int nodePort, String gitPullInterval) {
+        this.instances = instances;
+        this.auth = auth;
+        this.environment = environment;
+        this.runtimes = runtimes;
+        this.nodePort = nodePort;
+        this.gitPullInterval = gitPullInterval;
+    }
+
     public int getInstances() {
         return instances;
     }
diff --git a/karavan-operator/src/test/java/org/apache/camel/karavan/operator/KaravanReconcilerE2E.java b/karavan-operator/src/test/java/org/apache/camel/karavan/operator/KaravanReconcilerE2E.java
new file mode 100644
index 00000000..577e90ca
--- /dev/null
+++ b/karavan-operator/src/test/java/org/apache/camel/karavan/operator/KaravanReconcilerE2E.java
@@ -0,0 +1,146 @@
+package org.apache.camel.karavan.operator;
+
+import io.fabric8.kubernetes.api.model.*;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.rbac.Role;
+import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import io.fabric8.openshift.api.model.Route;
+import io.fabric8.tekton.pipeline.v1beta1.Pipeline;
+import io.fabric8.tekton.pipeline.v1beta1.Task;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.spec.KaravanSpec;
+import org.apache.camel.karavan.operator.spec.KaravanStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class KaravanReconcilerE2E {
+    private static final String DEFAULT_NAMESPACE = "default";
+
+    KubernetesClient client;
+
+    @BeforeEach
+    void createKubernetesClient() {
+        client = new KubernetesClientBuilder().build();
+    }
+    private Karavan buildKaravan() {
+        var karavan = new Karavan();
+        karavan.setMetadata(new ObjectMetaBuilder().withName("karavan").withNamespace(DEFAULT_NAMESPACE).build());
+        karavan.setSpec(new KaravanSpec(1, "public", "demo", "quarkus,spring-boot", 30668, "10s"));
+
+        return karavan;
+    }
+
+    private class Obj {
+        Class<? extends HasMetadata> type;
+        String name;
+
+        public Obj(Class<? extends HasMetadata> type, String name) {
+            this.type = type;
+            this.name = name;
+        }
+    }
+
+    private List<Obj> getResources() {
+        var array = new ArrayList<>(Arrays.asList(
+                new Obj(Deployment.class, "karavan"),
+                new Obj(Service.class, "karavan"),
+                new Obj(ServiceAccount.class, "karavan"),
+                new Obj(Role.class, "karavan"),
+                new Obj(RoleBinding.class, "karavan"),
+                new Obj(RoleBinding.class, "karavan-view"),
+                new Obj(PersistentVolumeClaim.class, "karavan-data"),
+                new Obj(PersistentVolumeClaim.class, "karavan-m2-cache"),
+                new Obj(PersistentVolumeClaim.class, "karavan-jbang-cache")
+        ));
+        if (Utils.isOpenShift(client)) {
+            array.add(new Obj(Route.class, "karavan"));
+        }
+        return array;
+    }
+
+    private List<Obj> getTektonResources() {
+        var array = new ArrayList<>(Arrays.asList(
+                new Obj(Task.class, "karavan-task-dev-quarkus"),
+                new Obj(Task.class, "karavan-task-dev-spring-boot"),
+                new Obj(Pipeline.class, "karavan-pipeline-dev-quarkus"),
+                new Obj(Pipeline.class, "karavan-pipeline-dev-spring-boot"),
+                new Obj(ServiceAccount.class, "pipeline"),
+                new Obj(Role.class, "deployer"),
+                new Obj(RoleBinding.class, "pipeline-deployer")
+        ));
+        if (Utils.isOpenShift(client)) {
+            array.add(new Obj(Route.class, "karavan"));
+        }
+        return array;
+    }
+
+    private void deleteKaravan(Karavan karavan) {
+        client.resources(Karavan.class).inNamespace(DEFAULT_NAMESPACE).withName(karavan.getMetadata().getName()).delete();
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> {
+            getResources().forEach(c -> assertNull(client.resources(c.type).inNamespace(DEFAULT_NAMESPACE).withName(c.name).get()));
+        });
+    }
+
+    @Test
+    @Order(1)
+    void basicOperatorTest() {
+        var karavan = buildKaravan();
+        client.resource(karavan).create();
+
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> {
+            Karavan updatedKaravan = client.resources(Karavan.class).inNamespace(DEFAULT_NAMESPACE).withName("karavan").get();
+            assertThat(updatedKaravan.getStatus(), is(notNullValue()));
+            assertThat(updatedKaravan.getStatus().getState(), is(KaravanStatus.State.READY));
+        });
+
+        getResources().forEach(c -> assertNotNull(client.resources(c.type).inNamespace(DEFAULT_NAMESPACE).withName(c.name).get()));
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> {
+            var deployment = client.resources(Deployment.class).inNamespace(DEFAULT_NAMESPACE).withName("karavan").get();
+            assertNotNull(deployment);
+            assertEquals(deployment.getSpec().getReplicas(), deployment.getStatus().getAvailableReplicas());
+        });
+
+        deleteKaravan(karavan);
+    }
+
+    @Test
+    @Order(2)
+    void installTektonTest() throws FileNotFoundException {
+        var karavan = buildKaravan();
+        client.resource(karavan).create();
+
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> {
+            Karavan updatedKaravan = client.resources(Karavan.class).inNamespace(DEFAULT_NAMESPACE).withName("karavan").get();
+            assertThat(updatedKaravan.getStatus(), is(notNullValue()));
+            assertThat(updatedKaravan.getStatus().getState(), is(KaravanStatus.State.READY));
+        });
+
+        client.resource(new FileInputStream("src/test/resources/kubernetes/pipelines.yaml")).create();
+        client.resource(new FileInputStream("src/test/resources/kubernetes/tasks.yaml")).create();
+
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> {
+                    getTektonResources().forEach(c -> assertNotNull(client.resources(c.type).inNamespace(DEFAULT_NAMESPACE).withName(c.name).get()));
+                });
+
+        deleteKaravan(karavan);
+        client.resource(new FileInputStream("src/test/resources/kubernetes/pipelines.yaml")).delete();
+        client.resource(new FileInputStream("src/test/resources/kubernetes/tasks.yaml")).delete();
+    }
+}
diff --git a/karavan-operator/src/test/java/org/apache/camel/karavan/operator/KaravanReconcilerTest.java b/karavan-operator/src/test/java/org/apache/camel/karavan/operator/KaravanReconcilerTest.java
new file mode 100644
index 00000000..c407f0fb
--- /dev/null
+++ b/karavan-operator/src/test/java/org/apache/camel/karavan/operator/KaravanReconcilerTest.java
@@ -0,0 +1,65 @@
+package org.apache.camel.karavan.operator;
+
+import io.fabric8.kubernetes.api.model.Namespace;
+import io.fabric8.kubernetes.api.model.NamespaceBuilder;
+import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import io.javaoperatorsdk.operator.Operator;
+import io.quarkus.test.junit.QuarkusTest;
+import org.apache.camel.karavan.operator.spec.Karavan;
+import org.apache.camel.karavan.operator.spec.KaravanSpec;
+import org.apache.camel.karavan.operator.spec.KaravanStatus;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import javax.inject.Inject;
+
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.CoreMatchers.is;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@QuarkusTest
+public class KaravanReconcilerTest {
+    private static final String KARAVAN_OPERATOR_TEST_NAMESPACE = "karavan-operator-test";
+
+    @Inject
+    Operator operator;
+
+    @Inject
+    KubernetesClient client;
+
+    @BeforeAll
+    static void initNamespace() {
+        new KubernetesClientBuilder().build().resource(new NamespaceBuilder().withNewMetadata().withName(KARAVAN_OPERATOR_TEST_NAMESPACE).endMetadata().build()).create();
+    }
+
+    @AfterAll
+    static void cleanupNamespace() {
+        var client = new KubernetesClientBuilder().build();
+        client.resources(Namespace.class).withName(KARAVAN_OPERATOR_TEST_NAMESPACE).delete();
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> assertNull(client.resources(Namespace.class).withName(KARAVAN_OPERATOR_TEST_NAMESPACE).get()));
+    }
+
+    @Test
+    void basicReconcile() {
+        var karavan = new Karavan();
+        karavan.setMetadata(new ObjectMetaBuilder().withName("karavan").withNamespace(KARAVAN_OPERATOR_TEST_NAMESPACE).build());
+        karavan.setSpec(new KaravanSpec(1, "public", "demo", "quarkus,spring-boot", 30668, "10s"));
+
+        operator.start();
+        client.resource(karavan).create();
+
+        await().atMost(5, MINUTES).ignoreExceptions().untilAsserted(() -> {
+            Karavan updatedKaravan = client.resources(Karavan.class).inNamespace(KARAVAN_OPERATOR_TEST_NAMESPACE).withName("karavan").get();
+            assertThat(updatedKaravan.getStatus(), is(notNullValue()));
+            assertThat(updatedKaravan.getStatus().getState(), is(KaravanStatus.State.READY));
+        });
+
+        operator.stop();
+    }
+}
diff --git a/karavan-operator/src/test/resources/application.properties b/karavan-operator/src/test/resources/application.properties
new file mode 100644
index 00000000..87034dc2
--- /dev/null
+++ b/karavan-operator/src/test/resources/application.properties
@@ -0,0 +1,3 @@
+# Predefined designtime namespace for operator testing in Kubernetes cluster
+quarkus.operator-sdk.namespaces=karavan-operator-test
+
diff --git a/karavan-operator/src/test/resources/kubernetes/kubernetes.yaml b/karavan-operator/src/test/resources/kubernetes/kubernetes.yaml
new file mode 100644
index 00000000..4163d8fd
--- /dev/null
+++ b/karavan-operator/src/test/resources/kubernetes/kubernetes.yaml
@@ -0,0 +1,202 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: camel-karavan-operator
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-karavan-operator
+  name: camel-karavan-operator
+  namespace: camel-karavan-operator
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/name: camel-karavan-operator
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/name: camel-karavan-operator
+      namespace: camel-karavan-operator
+    spec:
+      containers:
+        - env:
+            - name: KUBERNETES_NAMESPACE
+              valueFrom:
+                fieldRef:
+                  apiVersion: v1
+                  fieldPath: metadata.namespace
+          image: controller:latest
+          imagePullPolicy: Never
+          livenessProbe:
+            failureThreshold: 3
+            httpGet:
+              path: /q/health/live
+              port: http
+              scheme: HTTP
+            initialDelaySeconds: 5
+            periodSeconds: 10
+            successThreshold: 1
+            timeoutSeconds: 10
+          name: camel-karavan-operator
+          ports:
+            - containerPort: 8080
+              name: http
+              protocol: TCP
+          readinessProbe:
+            failureThreshold: 3
+            httpGet:
+              path: /q/health/ready
+              port: http
+              scheme: HTTP
+            initialDelaySeconds: 5
+            periodSeconds: 10
+            successThreshold: 1
+            timeoutSeconds: 10
+          resources:
+            limits:
+              cpu: 1000m
+              memory: 512Mi
+            requests:
+              cpu: 250m
+              memory: 256Mi
+      restartPolicy: Always
+      serviceAccountName: camel-karavan-operator
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: camel-karavan-operator-cluster-role
+  namespace: camel-karavan-operator
+rules:
+  - apiGroups:
+      - camel.apache.org
+    resources:
+      - karavans
+      - karavans/status
+      - karavans/finalizers
+    verbs:
+      - "*"
+  - apiGroups:
+      - apps
+    resources:
+      - deployments
+    verbs:
+      - "*"
+  - apiGroups:
+      - route.openshift.io
+    resources:
+      - routes
+      - routes/custom-host
+    verbs:
+      - "*"
+  - apiGroups:
+      - ""
+    resources:
+      - pods
+      - serviceaccounts
+      - deployments
+      - services
+      - persistentvolumes
+      - persistentvolumeclaims
+      - replicationcontrollers
+    verbs:
+      - "*"
+  - apiGroups:
+      - tekton.dev
+    resources:
+      - pipelinetasks
+      - pipelines
+      - pipelineruns
+      - tasks
+    verbs:
+      - "*"
+  - apiGroups:
+      - rbac.authorization.k8s.io
+    resources:
+      - roles
+      - rolebindings
+      - clusterroles
+    verbs:
+      - "*"
+  - apiGroups:
+      - apiextensions.k8s.io
+    resources:
+      - customresourcedefinitions
+    verbs:
+      - "*"
+  - apiGroups:
+      - networking.k8s.io
+    resources:
+      - customresourcedefinitions
+      - customresourcedefinitions/status
+    verbs:
+      - get
+      - list
+  - apiGroups:
+      - networking.k8s.io
+    resources:
+      - ingresses
+    verbs:
+      - "*"
+  - apiGroups:
+      - policy
+    resources:
+      - poddisruptionbudgets
+      - poddisruptionbudgets/status
+    verbs:
+      - "*"
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-karavan-operator
+  name: camel-karavan-operator
+  namespace: camel-karavan-operator
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: camel-karavan-operator-cluser-role-binding-view
+  namespace: camel-karavan-operator
+roleRef:
+  kind: ClusterRole
+  apiGroup: rbac.authorization.k8s.io
+  name: view
+subjects:
+  - kind: ServiceAccount
+    name: camel-karavan-operator
+    namespace: camel-karavan-operator
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app.kubernetes.io/name: camel-karavan-operator
+  name: camel-karavan-operator
+  namespace: camel-karavan-operator
+spec:
+  ports:
+    - name: http
+      port: 80
+      protocol: TCP
+      targetPort: 8080
+  selector:
+    app.kubernetes.io/name: camel-karavan-operator
+  type: ClusterIP
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: camel-karavan-operator-cluster-role-binding
+roleRef:
+  kind: ClusterRole
+  apiGroup: rbac.authorization.k8s.io
+  name: camel-karavan-operator-cluster-role
+subjects:
+  - kind: ServiceAccount
+    name: camel-karavan-operator
+    namespace: camel-karavan-operator
diff --git a/karavan-operator/src/test/resources/kubernetes/pipelines.yaml b/karavan-operator/src/test/resources/kubernetes/pipelines.yaml
new file mode 100644
index 00000000..bab0d843
--- /dev/null
+++ b/karavan-operator/src/test/resources/kubernetes/pipelines.yaml
@@ -0,0 +1,40 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  labels:
+    app.kubernetes.io/instance: default
+    app.kubernetes.io/part-of: tekton-pipelines
+    pipeline.tekton.dev/release: devel
+    version: devel
+  name: pipelines.tekton.dev
+spec:
+  group: tekton.dev
+  names:
+    categories:
+    - tekton
+    - tekton-pipelines
+    kind: Pipeline
+    listKind: PipelineList
+    plural: pipelines
+    singular: pipeline
+  scope: Namespaced
+  versions:
+  - name: v1beta1
+    schema:
+      openAPIV3Schema:
+        type: object
+        x-kubernetes-preserve-unknown-fields: true
+    served: true
+    storage: true
+    subresources:
+      status: {}
+  - name: v1
+    schema:
+      openAPIV3Schema:
+        type: object
+        x-kubernetes-preserve-unknown-fields: true
+    served: true
+    storage: false
+    subresources:
+      status: {}
+
diff --git a/karavan-operator/src/test/resources/kubernetes/tasks.yaml b/karavan-operator/src/test/resources/kubernetes/tasks.yaml
new file mode 100644
index 00000000..58f97a2a
--- /dev/null
+++ b/karavan-operator/src/test/resources/kubernetes/tasks.yaml
@@ -0,0 +1,73 @@
+# Copyright 2019 The Tekton Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: tasks.tekton.dev
+  labels:
+    app.kubernetes.io/instance: default
+    app.kubernetes.io/part-of: tekton-pipelines
+    pipeline.tekton.dev/release: "devel"
+    version: "devel"
+spec:
+  group: tekton.dev
+  preserveUnknownFields: false
+  versions:
+  - name: v1beta1
+    served: true
+    storage: true
+    schema:
+      openAPIV3Schema:
+        type: object
+        # One can use x-kubernetes-preserve-unknown-fields: true
+        # at the root of the schema (and inside any properties, additionalProperties)
+        # to get the traditional CRD behaviour that nothing is pruned, despite
+        # setting spec.preserveUnknownProperties: false.
+        #
+        # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/
+        # See issue: https://github.com/knative/serving/issues/912
+        x-kubernetes-preserve-unknown-fields: true
+    # Opt into the status subresource so metadata.generation
+    # starts to increment
+    subresources:
+      status: {}
+  - name: v1
+    served: true
+    storage: false
+    schema:
+      openAPIV3Schema:
+        type: object
+        # TODO(#1461): Add OpenAPIV3 schema
+        # OpenAPIV3 schema allows Kubernetes to perform validation on the schema fields
+        # and use the schema in tooling such as `kubectl explain`.
+        # Using "x-kubernetes-preserve-unknown-fields: true"
+        # at the root of the schema (or within it) allows arbitrary fields.
+        # We currently perform our own validation separately.
+        # See https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema
+        # for more info.
+        x-kubernetes-preserve-unknown-fields: true
+    # Opt into the status subresource so metadata.generation
+    # starts to increment
+    subresources:
+      status: {}
+  names:
+    kind: Task
+    plural: tasks
+    singular: task
+    categories:
+    - tekton
+    - tekton-pipelines
+  scope: Namespaced
+