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

gfournier 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 0349764f5 feat(api): introduce an Uknown status
0349764f5 is described below

commit 0349764f5a773e0cf36eab5476f2fa4379eb24c4
Author: Pasquale Congiusti <pasquale.congiu...@gmail.com>
AuthorDate: Wed Sep 11 17:23:44 2024 +0200

    feat(api): introduce an Uknown status
    
    During an IntegrationPlatform recreation, there is now way to properly set 
the traits, resulting in monitoring errors.
    
    Closes #5836
---
 pkg/apis/camel/v1/integration_types.go             |   2 +
 .../integration/integration_controller.go          |   9 +-
 pkg/controller/integration/monitor.go              |   8 +-
 pkg/controller/integration/monitor_test.go         | 133 +++++++++++++++++++++
 pkg/controller/integration/monitor_unknown.go      |  61 ++++++++++
 5 files changed, 211 insertions(+), 2 deletions(-)

diff --git a/pkg/apis/camel/v1/integration_types.go 
b/pkg/apis/camel/v1/integration_types.go
index a68533cda..96e429bd1 100644
--- a/pkg/apis/camel/v1/integration_types.go
+++ b/pkg/apis/camel/v1/integration_types.go
@@ -159,6 +159,8 @@ const (
        IntegrationPhaseRunning IntegrationPhase = "Running"
        // IntegrationPhaseError --.
        IntegrationPhaseError IntegrationPhase = "Error"
+       // IntegrationPhaseUnknown --.
+       IntegrationPhaseUnknown IntegrationPhase = "Unknown"
 
        // IntegrationConditionReady --.
        IntegrationConditionReady IntegrationConditionType = "Ready"
diff --git a/pkg/controller/integration/integration_controller.go 
b/pkg/controller/integration/integration_controller.go
index 04cb6017b..24d9d3538 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -56,6 +56,8 @@ import (
        utilResource "github.com/apache/camel-k/v2/pkg/util/resource"
 )
 
+const retryMonitoring = 5
+
 func Add(ctx context.Context, mgr manager.Manager, c client.Client) error {
        err := mgr.GetFieldIndexer().IndexField(ctx, &corev1.Pod{}, 
"status.phase",
                func(obj ctrl.Object) []string {
@@ -529,7 +531,7 @@ func (r *reconcileIntegration) Reconcile(ctx 
context.Context, request reconcile.
        if instance.IsSynthetic() {
                actions = append(actions, NewMonitorSyntheticAction())
        } else {
-               actions = append(actions, NewMonitorAction())
+               actions = append(actions, NewMonitorAction(), 
NewMonitorUnknownAction())
        }
 
        for _, a := range actions {
@@ -557,6 +559,11 @@ func (r *reconcileIntegration) Reconcile(ctx 
context.Context, request reconcile.
                                camelevent.NotifyIntegrationError(ctx, 
r.client, r.recorder, &instance, newTarget, err)
                                return reconcile.Result{}, err
                        }
+
+                       if newTarget.Status.Phase == v1.IntegrationPhaseUnknown 
{
+                               // Wait for some time before trying to monitor 
again
+                               return reconcile.Result{RequeueAfter: 
retryMonitoring * time.Second}, nil
+                       }
                }
 
                // handle one action at time so the resource
diff --git a/pkg/controller/integration/monitor.go 
b/pkg/controller/integration/monitor.go
index af90cc3db..5c395a86d 100644
--- a/pkg/controller/integration/monitor.go
+++ b/pkg/controller/integration/monitor.go
@@ -137,7 +137,13 @@ func (action *monitorAction) Handle(ctx context.Context, 
integration *v1.Integra
                        v1.IntegrationConditionInitializationFailedReason, 
err.Error())
                return integration, err
        }
-
+       // If the platform is not in ready status (it may happen when a new 
IntegrationPlatform is created), then, we may not be able to
+       // properly apply all the traits. We must set the phase in an unknown 
status which should be periodically reconciled in order to make sure that
+       // we eventually return in a ready phase (likely once the platform is 
done)
+       if environment.Platform != nil && environment.Platform.Status.Phase != 
v1.IntegrationPlatformPhaseReady {
+               integration.Status.Phase = v1.IntegrationPhaseUnknown
+               return integration, nil
+       }
        action.checkTraitAnnotationsDeprecatedNotice(integration)
 
        return action.monitorPods(ctx, environment, integration)
diff --git a/pkg/controller/integration/monitor_test.go 
b/pkg/controller/integration/monitor_test.go
index a655edea9..dbd56507a 100644
--- a/pkg/controller/integration/monitor_test.go
+++ b/pkg/controller/integration/monitor_test.go
@@ -128,6 +128,139 @@ func TestMonitorFailureIntegration(t *testing.T) {
        assert.Equal(t, v1.IntegrationConditionInitializationFailedReason, 
handledIt.Status.GetCondition(v1.IntegrationConditionReady).Reason)
 }
 
+func TestMonitorIntegrationWhilePlatformRecreating(t *testing.T) {
+       catalog := &v1.CamelCatalog{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1.SchemeGroupVersion.String(),
+                       Kind:       v1.CamelCatalogKind,
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "ns",
+                       Name:      "camel-k-catalog",
+               },
+               Spec: v1.CamelCatalogSpec{
+                       Runtime: v1.RuntimeSpec{
+                               Provider: v1.RuntimeProviderQuarkus,
+                               Version:  defaults.DefaultRuntimeVersion,
+                       },
+               },
+       }
+       platform := &v1.IntegrationPlatform{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1.SchemeGroupVersion.String(),
+                       Kind:       v1.IntegrationPlatformKind,
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "ns",
+                       Name:      "camel-k",
+               },
+               Status: v1.IntegrationPlatformStatus{
+                       Phase: v1.IntegrationPlatformPhaseNone,
+               },
+       }
+       kit := &v1.IntegrationKit{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1.SchemeGroupVersion.String(),
+                       Kind:       v1.IntegrationKitKind,
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "ns",
+                       Name:      "my-kit",
+               },
+               Status: v1.IntegrationKitStatus{
+                       Phase: v1.IntegrationKitPhaseReady,
+               },
+       }
+       it := &v1.Integration{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1.SchemeGroupVersion.String(),
+                       Kind:       v1.IntegrationKind,
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "ns",
+                       Name:      "my-it",
+               },
+               Status: v1.IntegrationStatus{
+                       RuntimeVersion: defaults.DefaultRuntimeVersion,
+                       Phase:          v1.IntegrationPhaseRunning,
+                       IntegrationKit: &corev1.ObjectReference{
+                               Name:       kit.Name,
+                               Namespace:  kit.Namespace,
+                               Kind:       kit.Kind,
+                               APIVersion: kit.APIVersion,
+                       },
+                       Conditions: []v1.IntegrationCondition{
+                               {
+                                       Type:   
v1.IntegrationConditionDeploymentAvailable,
+                                       Status: corev1.ConditionTrue,
+                               },
+                               {
+                                       Type:   v1.IntegrationConditionReady,
+                                       Status: corev1.ConditionTrue,
+                               },
+                       },
+               },
+       }
+       hash, _ := digest.ComputeForIntegration(it, nil, nil)
+       it.Status.Digest = hash
+       pod := &corev1.Pod{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: appsv1.SchemeGroupVersion.String(),
+                       Kind:       "Pod",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "ns",
+                       Name:      "my-pod",
+                       Labels: map[string]string{
+                               v1.IntegrationLabel: "my-it",
+                       },
+               },
+               Spec: corev1.PodSpec{
+                       Containers: []corev1.Container{
+                               {
+                                       Name:  "my-cnt",
+                                       Image: "my-img",
+                               },
+                       },
+               },
+               Status: corev1.PodStatus{
+                       Phase: corev1.PodRunning,
+                       Conditions: []corev1.PodCondition{
+                               {
+                                       Type:   corev1.PodReady,
+                                       Status: corev1.ConditionTrue,
+                               },
+                       },
+               },
+       }
+       c, err := test.NewFakeClient(catalog, platform, it, kit, pod)
+       require.NoError(t, err)
+
+       a := monitorAction{}
+       a.InjectLogger(log.Log)
+       a.InjectClient(c)
+       assert.Equal(t, "monitor", a.Name())
+       assert.True(t, a.CanHandle(it))
+       handledIt, err := a.Handle(context.TODO(), it)
+       require.NoError(t, err)
+       assert.Equal(t, v1.IntegrationPhaseUnknown, handledIt.Status.Phase)
+}
+
+func TestMonitorIntegrationRecoverFromUnknown(t *testing.T) {
+       c, it, err := nominalEnvironment()
+       it.Status.Phase = v1.IntegrationPhaseUnknown
+       require.NoError(t, err)
+
+       a := monitorUnknownAction{}
+       a.InjectLogger(log.Log)
+       a.InjectClient(c)
+       assert.Equal(t, "monitor-unknown", a.Name())
+       assert.True(t, a.CanHandle(it))
+       handledIt, err := a.Handle(context.TODO(), it)
+       require.NoError(t, err)
+       assert.Equal(t, v1.IntegrationPhaseRunning, handledIt.Status.Phase)
+}
+
 func nominalEnvironment() (client.Client, *v1.Integration, error) {
        catalog := &v1.CamelCatalog{
                TypeMeta: metav1.TypeMeta{
diff --git a/pkg/controller/integration/monitor_unknown.go 
b/pkg/controller/integration/monitor_unknown.go
new file mode 100644
index 000000000..e9e1cbc30
--- /dev/null
+++ b/pkg/controller/integration/monitor_unknown.go
@@ -0,0 +1,61 @@
+/*
+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 integration
+
+import (
+       "context"
+
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/v2/pkg/trait"
+       corev1 "k8s.io/api/core/v1"
+)
+
+// NewMonitorUnknownAction is an action used to verify when an Integration can 
be monitored back again.
+func NewMonitorUnknownAction() Action {
+       return &monitorUnknownAction{}
+}
+
+type monitorUnknownAction struct {
+       baseAction
+}
+
+func (action *monitorUnknownAction) Name() string {
+       return "monitor-unknown"
+}
+
+func (action *monitorUnknownAction) CanHandle(integration *v1.Integration) 
bool {
+       return integration.Status.Phase == v1.IntegrationPhaseUnknown
+}
+
+func (action *monitorUnknownAction) Handle(ctx context.Context, integration 
*v1.Integration) (*v1.Integration, error) {
+       // Run traits that are enabled for the phase
+       environment, err := trait.Apply(ctx, action.client, integration, nil)
+       if err != nil {
+               integration.Status.Phase = v1.IntegrationPhaseError
+               integration.SetReadyCondition(corev1.ConditionFalse,
+                       v1.IntegrationConditionInitializationFailedReason, 
err.Error())
+               return integration, err
+       }
+       // We're good to monitor this again
+       if environment.Platform != nil && environment.Platform.Status.Phase == 
v1.IntegrationPlatformPhaseReady {
+               integration.Status.Phase = v1.IntegrationPhaseRunning
+               return integration, nil
+       }
+
+       return integration, nil
+}

Reply via email to