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

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


The following commit(s) were added to refs/heads/main by this push:
     new 5dd46e4  fix(integration) Integration not marked as Failed when Camel 
is unable to start
5dd46e4 is described below

commit 5dd46e42fdec82e7aa84bfbc335aea7790e224df
Author: Claudio Miranda <[email protected]>
AuthorDate: Thu May 13 16:39:58 2021 -0300

    fix(integration) Integration not marked as Failed when Camel is unable to 
start
    
    https://github.com/apache/camel-k/issues/2291
    
    * Added unversioned maven directories to .gitignore
---
 .gitignore                            |  2 ++
 e2e/common/files/BadRoute.java        | 28 ++++++++++++++++++++++
 e2e/common/integration_fail_test.go   | 45 +++++++++++++++++++++++++++++++++++
 pkg/controller/integration/error.go   | 36 ++++++++++++++++++++++++++++
 pkg/controller/integration/monitor.go | 34 ++++++++++++++++++++++++++
 pkg/util/kubernetes/client.go         | 16 +++++++++++++
 pkg/util/kubernetes/conditions.go     |  6 ++---
 7 files changed, 164 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index 94ad8cb..26458eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,8 @@ build/_test
 build/_maven_output
 build/_maven_overlay
 build/_kamelets
+build/_maven_overlay/
+build/maven/target/
 /api_*
 
 # envrc
diff --git a/e2e/common/files/BadRoute.java b/e2e/common/files/BadRoute.java
new file mode 100644
index 0000000..9967879
--- /dev/null
+++ b/e2e/common/files/BadRoute.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.Exception;
+import java.lang.Override;
+import org.apache.camel.builder.RouteBuilder;
+
+public class BadRoute extends RouteBuilder {
+  @Override
+  public void configure() throws Exception {
+    
from("mongodb:sample?database=sampledb&collection=mycollection&operation=findfoo").throwException(new
 RuntimeException("Heyyy")).log("bar");
+  }
+}
+
diff --git a/e2e/common/integration_fail_test.go 
b/e2e/common/integration_fail_test.go
new file mode 100644
index 0000000..f2b3940
--- /dev/null
+++ b/e2e/common/integration_fail_test.go
@@ -0,0 +1,45 @@
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> 
Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+import (
+       "testing"
+
+       . "github.com/onsi/gomega"
+
+       . "github.com/apache/camel-k/e2e/support"
+       camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       v1 "k8s.io/api/core/v1"
+)
+
+func TestBadRouteIntegration(t *testing.T) {
+       WithNewTestNamespace(t, func(ns string) {
+               Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
+               t.Run("run bad java route", func(t *testing.T) {
+                       Expect(Kamel("run", "-n", ns, 
"files/BadRoute.java").Execute()).To(Succeed())
+                       Eventually(IntegrationPodPhase(ns, "bad-route"), 
TestTimeoutMedium).Should(Equal(v1.PodRunning))
+                       Eventually(IntegrationPhase(ns, "bad-route"), 
TestTimeoutShort).Should(Equal(camelv1.IntegrationPhaseError))
+                       Eventually(IntegrationCondition(ns, "bad-route", 
camelv1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(v1.ConditionFalse))
+                       Expect(Kamel("delete", "--all", "-n", 
ns).Execute()).To(Succeed())
+               })
+       })
+}
diff --git a/pkg/controller/integration/error.go 
b/pkg/controller/integration/error.go
index 5f19458..a7f490c 100644
--- a/pkg/controller/integration/error.go
+++ b/pkg/controller/integration/error.go
@@ -20,8 +20,13 @@ package integration
 import (
        "context"
 
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
+
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
        "github.com/apache/camel-k/pkg/util/digest"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
 )
 
 // NewErrorAction creates a new error action for an integration
@@ -56,6 +61,37 @@ func (action *errorAction) Handle(ctx context.Context, 
integration *v1.Integrati
                return integration, nil
        }
 
+       if kubernetes.IsConditionTrue(integration, 
v1.IntegrationConditionDeploymentAvailable) {
+               deployment, err := kubernetes.GetDeployment(ctx, action.client, 
integration.Name, integration.Namespace)
+               if err != nil && k8serrors.IsNotFound(err) {
+                       return nil, err
+               }
+
+               // if the integration is in error phase, check if the 
corresponding pod is running ok, the user may have updated the integration.
+               deployAvailable := false
+               progressingOk := false
+               for _, c := range deployment.Status.Conditions {
+                       // first, check if the container is in available state
+                       if c.Type == appsv1.DeploymentAvailable {
+                               deployAvailable = c.Status == 
corev1.ConditionTrue
+                       }
+                       // second, check the progressing and the reasons
+                       if c.Type == appsv1.DeploymentProgressing {
+                               progressingOk = c.Status == 
corev1.ConditionTrue && (c.Reason == "NewReplicaSetAvailable" || c.Reason == 
"ReplicaSetUpdated")
+                       }
+               }
+               if deployAvailable && progressingOk {
+                       availableCondition := v1.IntegrationCondition{
+                               Type:   v1.IntegrationConditionReady, 
+                               Status: corev1.ConditionTrue,
+                               Reason: 
v1.IntegrationConditionReplicaSetReadyReason,
+                       }
+                       integration.Status.SetConditions(availableCondition)
+                       integration.Status.Phase = v1.IntegrationPhaseRunning
+                       return integration, nil
+               }
+       }
+
        // TODO check also if deployment matches (e.g. replicas)
        return nil, nil
 }
diff --git a/pkg/controller/integration/monitor.go 
b/pkg/controller/integration/monitor.go
index c92e9e5..515d2d4 100644
--- a/pkg/controller/integration/monitor.go
+++ b/pkg/controller/integration/monitor.go
@@ -108,6 +108,40 @@ func (action *monitorAction) Handle(ctx context.Context, 
integration *v1.Integra
                timeToFirstReadiness.Observe(duration.Seconds())
        }
 
+       // the integration pod may be in running phase, but the corresponding 
container running the integration code
+       // may be in error state, in this case we should check the deployment 
status and set the integration status accordingly.
+       if kubernetes.IsConditionTrue(integration, 
v1.IntegrationConditionDeploymentAvailable) {
+               deployment, err := kubernetes.GetDeployment(ctx, action.client, 
integration.Name, integration.Namespace)
+               if err != nil {
+                       return nil, err
+               }
+
+               deployUnavailable := false
+               progressingFailing := false
+               for _, c := range deployment.Status.Conditions {
+                       // first, check if the container status is not available
+                       if c.Type == appsv1.DeploymentAvailable {
+                               deployUnavailable = c.Status == 
corev1.ConditionFalse
+                       }
+                       // second, check when it is progressing and reason is 
the replicas are available but the number of replicas are zero
+                       // in this case, the container integration is failing
+                       if c.Type == appsv1.DeploymentProgressing {
+                               progressingFailing = c.Status == 
corev1.ConditionTrue && c.Reason == "NewReplicaSetAvailable" && 
deployment.Status.AvailableReplicas < 1
+                       }
+               }
+               if deployUnavailable && progressingFailing {
+                       notAvailableCondition := v1.IntegrationCondition{
+                               Type:   v1.IntegrationConditionReady, 
+                               Status: corev1.ConditionFalse,
+                               Reason: v1.IntegrationConditionErrorReason,
+                               Message: "The corresponding pod(s) may be in 
error state, look at the pod status or log for errors",
+                       }
+                       integration.Status.SetConditions(notAvailableCondition)
+                       integration.Status.Phase = v1.IntegrationPhaseError
+                       return integration, nil
+               }
+       }
+
        return integration, nil
 }
 
diff --git a/pkg/util/kubernetes/client.go b/pkg/util/kubernetes/client.go
index ceecef5..1f8795b 100644
--- a/pkg/util/kubernetes/client.go
+++ b/pkg/util/kubernetes/client.go
@@ -21,6 +21,7 @@ import (
        "context"
        "fmt"
 
+       appsv1 "k8s.io/api/apps/v1"
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -174,3 +175,18 @@ func ResolveValueSource(ctx context.Context, client 
ctrl.Reader, namespace strin
 
        return "", nil
 }
+
+// GetDeployment --
+func GetDeployment(context context.Context, client ctrl.Reader, name string, 
namespace string) (*appsv1.Deployment, error) {
+
+       key := ctrl.ObjectKey{
+               Name:      name,
+               Namespace: namespace,
+       }
+       deployment := appsv1.Deployment{}
+       if err := client.Get(context, key, &deployment); err != nil {
+               return nil, err
+       }
+
+       return &deployment, nil
+}
diff --git a/pkg/util/kubernetes/conditions.go 
b/pkg/util/kubernetes/conditions.go
index 1c83fca..569bf43 100644
--- a/pkg/util/kubernetes/conditions.go
+++ b/pkg/util/kubernetes/conditions.go
@@ -33,9 +33,9 @@ import (
 
 // nolint: gocritic
 func MirrorReadyCondition(ctx context.Context, c client.Client, it 
*v1.Integration) {
-       if isConditionTrue(it, v1.IntegrationConditionDeploymentAvailable) || 
isConditionTrue(it, v1.IntegrationConditionKnativeServiceAvailable) {
+       if IsConditionTrue(it, v1.IntegrationConditionDeploymentAvailable) || 
IsConditionTrue(it, v1.IntegrationConditionKnativeServiceAvailable) {
                mirrorReadyConditionFromReplicaSet(ctx, c, it)
-       } else if isConditionTrue(it, v1.IntegrationConditionCronJobAvailable) {
+       } else if IsConditionTrue(it, v1.IntegrationConditionCronJobAvailable) {
                mirrorReadyConditionFromCronJob(ctx, c, it)
        } else {
                it.Status.SetCondition(
@@ -112,7 +112,7 @@ func mirrorReadyConditionFromCronJob(ctx context.Context, c 
client.Client, it *v
        }
 }
 
-func isConditionTrue(it *v1.Integration, conditionType 
v1.IntegrationConditionType) bool {
+func IsConditionTrue(it *v1.Integration, conditionType 
v1.IntegrationConditionType) bool {
        cond := it.Status.GetCondition(conditionType)
        if cond == nil {
                return false

Reply via email to