Repository: kubevela/kubevela (go)

Issue:
Component and trait healthchecks are not run after initial deploy
**Describe the bug**
As of now, it seems that it works like this:

- Deployment process runs in a workflow step
- During the process healthchecks are run to evaluate if the step succeeded or not
- When it's finished, healthchecks are actually not run anymore. So we end up with an infinitely 'Healthy' app.

**To Reproduce**
Consider this Application example:
```yaml
---
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: first-vela-app
  namespace: vela-system
spec:
  components:
    - name: express-server
      type: webservice
      properties:
        image: oamdev/hello-world
        ports:
          - port: 8000
            expose: true
        replicas: 3
        livenessProbe:
          httpGet:
            path: /
            port: 8081
          periodSeconds: 5
          failureThreshold: 3
          initialDelaySeconds: 60
        readinessProbe:
          httpGet:
            path: /
            port: 8000
          periodSeconds: 5
          successThreshold: 2
          initialDelaySeconds: 30
      traits:
        - type: scaler
          properties:
            replicas: 3
  policies:
    - name: target-default
      type: topology
      properties:
        clusters: ["local"]
        namespace: "default"
  workflow:
    steps:
      - name: deploy2default
        type: deploy
        properties:
          policies: ["target-default"]
```
At first it gets successfully deployed, vela status shows `Healthy Ready:3/3`, then eventually all pods go into CrashLoop. Application controller after some time reconciles the app at the moment when everything is down, but vela status still shows `Healthy Ready:3/3`.

**Expected behavior**
Application and component health state updates according to the actual status the moment of reconciliation.

**Screenshots**
```
~ $ kubectl get pods
NAME                              READY   STATUS    RESTARTS      AGE
express-server-5f7f69579d-5gshm   0/1     Running   5 (31s ago)   5m32s
express-server-5f7f69579d-7nj4q   0/1     Running   5 (31s ago)   5m32s
express-server-5f7f69579d-bkq7c   0/1     Running   5 (31s ago)   5m32s
~ $ vela status first-vela-app -n vela-system
About:

  Name:         first-vela-app
  Namespace:    vela-system
  Created at:   2024-10-14 11:54:21 +0500 +05
  Status:       running

Workflow:

  mode: StepByStep-DAG
  finished: true
  Suspend: false
  Terminated: false
  Steps
  - id: wbxjnznjze
    name: deploy2default
    type: deploy
    phase: succeeded

Services:

  - Name: express-server
    Cluster: local  Namespace: default
    Type: webservice
    Healthy Ready:3/3
    Traits:
      ✅ scaler
```

**KubeVela Version**
```
~ $ vela version
CLI Version: 1.9.13
Core Version: 1.9.13
GitRevision: git-d56da069
GolangVersion: go1.19.13
```

**Cluster information**
Tested with 1.25.16

Pull request description:
Fix: check component status after initial deployment
### Description of your changes

copilot:all

This PR picks up from the great work already done by @mkls6 on https://github.com/kubevela/kubevela/pull/6640 for issue https://github.com/kubevela/kubevela/issues/6639 

The e2e and MC-e2e tests are now working and some issues have been addressed. The main issue in the e2e tests was that the fix was blocking the installation of addons (fluxcd) needed for e2e. 

Fixes #6639

I have:

- [x] Read and followed KubeVela's [contribution process](https://github.com/kubevela/kubevela/blob/master/contribute/create-pull-request.md).
- [x] [Related Docs](https://github.com/kubevela/kubevela.io) updated properly. In a new feature or configuration option, an update to the documentation is necessary. (N/A)
- [x] Run `make reviewable` to ensure this PR is ready for review.
- [x] Added `backport release-x.y` labels to auto-backport this PR if necessary. (N/A)

### How has this code been tested

- Unit test has been added to ensure that the application enters healthy status as expected, then enters unhealthy when components are in a deteriorated status. 
- Manual testing to ensure that app health and component health stays in sync over a prolonged period and multiple status changes
- Ensured e2e tests were working
- Ensured addons could install


### Special notes for your reviewer



Patch:
diff --git a/pkg/controller/core.oam.dev/v1beta1/application/application_controller.go b/pkg/controller/core.oam.dev/v1beta1/application/application_controller.go
index dfe8b0ea..85d3bc75 100644
--- a/pkg/controller/core.oam.dev/v1beta1/application/application_controller.go
+++ b/pkg/controller/core.oam.dev/v1beta1/application/application_controller.go
@@ -254,11 +254,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 	}
 
 	var phase = common.ApplicationRunning
-	if !hasHealthCheckPolicy(appFile.ParsedPolicies) {
-		app.Status.Services = handler.services
-		if !isHealthy(handler.services) {
-			phase = common.ApplicationUnhealthy
-		}
+	isHealthy := evalStatus(logCtx, handler, appFile, appParser)
+	if !isHealthy {
+		phase = common.ApplicationUnhealthy
 	}
 
 	r.stateKeep(logCtx, handler, app)
@@ -690,3 +688,23 @@ func setVelaVersion(app *v1beta1.Application) {
 		metav1.SetMetaDataAnnotation(&app.ObjectMeta, oam.AnnotationKubeVelaVersion, version.VelaVersion)
 	}
 }
+
+func evalStatus(ctx monitorContext.Context, handler *AppHandler, appFile *appfile.Appfile, appParser *appfile.Parser) bool {
+	healthCheck := handler.checkComponentHealth(appParser, appFile)
+	if !hasHealthCheckPolicy(appFile.ParsedPolicies) {
+		for idx, svc := range handler.services {
+			for _, component := range handler.app.Spec.Components {
+				healthy, status, _, _, err := healthCheck(ctx, component, nil, svc.Cluster, svc.Namespace)
+				if err != nil {
+					ctx.Error(err, "Failed to collect health status")
+				} else if status != nil {
+					handler.services[idx].Healthy = healthy
+					handler.services[idx].Message = status.Message
+				}
+			}
+		}
+		handler.app.Status.Services = handler.services
+		return isHealthy(handler.services)
+	}
+	return true
+}
diff --git a/pkg/controller/core.oam.dev/v1beta1/application/generator.go b/pkg/controller/core.oam.dev/v1beta1/application/generator.go
index 978e7ba0..656def75 100644
--- a/pkg/controller/core.oam.dev/v1beta1/application/generator.go
+++ b/pkg/controller/core.oam.dev/v1beta1/application/generator.go
@@ -314,20 +314,20 @@ func (h *AppHandler) renderComponentFunc(appParser *appfile.Parser, af *appfile.
 }
 
 func (h *AppHandler) checkComponentHealth(appParser *appfile.Parser, af *appfile.Appfile) oamprovidertypes.ComponentHealthCheck {
-	return func(baseCtx context.Context, comp common.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+	return func(baseCtx context.Context, comp common.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *common.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 		ctx := multicluster.ContextWithClusterName(baseCtx, clusterName)
 		ctx = contextWithComponentNamespace(ctx, overrideNamespace)
 		ctx = contextWithReplicaKey(ctx, comp.ReplicaKey)
 
 		wl, manifest, err := h.prepareWorkloadAndManifests(ctx, appParser, comp, patcher, af)
 		if err != nil {
-			return false, nil, nil, err
+			return false, nil, nil, nil, err
 		}
 		wl.Ctx.SetCtx(auth.ContextWithUserInfo(ctx, h.app))
 
 		readyWorkload, readyTraits, err := renderComponentsAndTraits(manifest, h.currentAppRev, clusterName, overrideNamespace)
 		if err != nil {
-			return false, nil, nil, err
+			return false, nil, nil, nil, err
 		}
 		checkSkipApplyWorkload(wl)
 
@@ -336,15 +336,15 @@ func (h *AppHandler) checkComponentHealth(appParser *appfile.Parser, af *appfile
 			dispatchResources = append([]*unstructured.Unstructured{readyWorkload}, readyTraits...)
 		}
 		if !h.resourceKeeper.ContainsResources(dispatchResources) {
-			return false, nil, nil, err
+			return false, nil, nil, nil, err
 		}
 
-		_, output, outputs, isHealth, err := h.collectHealthStatus(auth.ContextWithUserInfo(ctx, h.app), wl, overrideNamespace, false)
+		status, output, outputs, isHealth, err := h.collectHealthStatus(auth.ContextWithUserInfo(ctx, h.app), wl, overrideNamespace, false)
 		if err != nil {
-			return false, nil, nil, err
+			return false, nil, nil, nil, err
 		}
 
-		return isHealth, output, outputs, err
+		return isHealth, status, output, outputs, err
 	}
 }
 
diff --git a/pkg/workflow/providers/legacy/multicluster/deploy.go b/pkg/workflow/providers/legacy/multicluster/deploy.go
index 7eb86318..0fbf9c1f 100644
--- a/pkg/workflow/providers/legacy/multicluster/deploy.go
+++ b/pkg/workflow/providers/legacy/multicluster/deploy.go
@@ -340,7 +340,7 @@ HealthCheck:
 			break HealthCheck
 		}
 		checkResults := slices.ParMap[*applyTask, *applyTaskResult](checkTasks, func(task *applyTask) *applyTaskResult {
-			healthy, output, outputs, err := healthCheck(ctx, task.component, nil, task.placement.Cluster, task.placement.Namespace)
+			healthy, _, output, outputs, err := healthCheck(ctx, task.component, nil, task.placement.Cluster, task.placement.Namespace)
 			task.healthy = ptr.To(healthy)
 			if healthy {
 				err = task.generateOutput(output, outputs, cache, makeValue)
diff --git a/pkg/workflow/providers/multicluster/deploy.go b/pkg/workflow/providers/multicluster/deploy.go
index 7eb86318..0fbf9c1f 100644
--- a/pkg/workflow/providers/multicluster/deploy.go
+++ b/pkg/workflow/providers/multicluster/deploy.go
@@ -340,7 +340,7 @@ HealthCheck:
 			break HealthCheck
 		}
 		checkResults := slices.ParMap[*applyTask, *applyTaskResult](checkTasks, func(task *applyTask) *applyTaskResult {
-			healthy, output, outputs, err := healthCheck(ctx, task.component, nil, task.placement.Cluster, task.placement.Namespace)
+			healthy, _, output, outputs, err := healthCheck(ctx, task.component, nil, task.placement.Cluster, task.placement.Namespace)
 			task.healthy = ptr.To(healthy)
 			if healthy {
 				err = task.generateOutput(output, outputs, cache, makeValue)
diff --git a/pkg/workflow/providers/types/types.go b/pkg/workflow/providers/types/types.go
index 08d0bbcf..914bb17f 100644
--- a/pkg/workflow/providers/types/types.go
+++ b/pkg/workflow/providers/types/types.go
@@ -42,7 +42,7 @@ type ComponentApply func(ctx context.Context, comp common.ApplicationComponent,
 type ComponentRender func(ctx context.Context, comp common.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, error)
 
 // ComponentHealthCheck health check oam component.
-type ComponentHealthCheck func(ctx context.Context, comp common.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error)
+type ComponentHealthCheck func(ctx context.Context, comp common.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *common.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error)
 
 // WorkloadRender render application component into workload
 type WorkloadRender func(ctx context.Context, comp common.ApplicationComponent) (*appfile.Component, error)


Test patch:
diff --git a/pkg/controller/core.oam.dev/v1beta1/application/application_controller_test.go b/pkg/controller/core.oam.dev/v1beta1/application/application_controller_test.go
index cb2299f6..c42f4b47 100644
--- a/pkg/controller/core.oam.dev/v1beta1/application/application_controller_test.go
+++ b/pkg/controller/core.oam.dev/v1beta1/application/application_controller_test.go
@@ -22,10 +22,13 @@ import (
 	"fmt"
 	"path/filepath"
 	sysruntime "runtime"
+	"strconv"
 	"strings"
 	"testing"
 	"time"
 
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+
 	"github.com/oam-dev/kubevela/pkg/features"
 
 	"github.com/google/go-cmp/cmp"
@@ -3986,6 +3989,105 @@ var _ = Describe("Test Application Controller", func() {
 		Expect(k8sClient.Delete(ctx, appWithPreDispatch)).Should(BeNil())
 		Expect(k8sClient.Delete(ctx, ns)).Should(BeNil())
 	})
+
+	It("should reevaluate health after workflow finishes and detect component deterioration", func() {
+		reconcileAppAndExpectPhase := func(app *v1beta1.Application, phase common.ApplicationPhase) {
+			Eventually(func() common.ApplicationPhase {
+				_, err := reconciler.Reconcile(ctx, reconcile.Request{
+					NamespacedName: client.ObjectKeyFromObject(app),
+				})
+				Expect(err).ToNot(HaveOccurred())
+
+				updated := &v1beta1.Application{}
+				Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(app), updated)).To(Succeed())
+
+				return updated.Status.Phase
+			}, time.Second*5, time.Millisecond*500).Should(Equal(phase))
+		}
+
+		patchDeploymentHealthLabel := func(ns corev1.Namespace, val bool) {
+			dep := &v1.Deployment{}
+			Expect(k8sClient.Get(ctx, types.NamespacedName{
+				Name:      "deployment",
+				Namespace: ns.Name,
+			}, dep)).To(Succeed())
+
+			patch := client.MergeFrom(dep.DeepCopy())
+			if dep.Labels == nil {
+				dep.Labels = map[string]string{}
+			}
+			dep.Labels["healthy"] = strconv.FormatBool(val)
+			Expect(k8sClient.Patch(ctx, dep, patch)).To(Succeed())
+			Expect(dep.Labels["healthy"]).To(Equal(strconv.FormatBool(val)))
+		}
+
+		ctx := context.Background()
+		ns := corev1.Namespace{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "health-reeval",
+			},
+		}
+		Expect(k8sClient.Create(ctx, &ns)).Should(Succeed())
+
+		app := &v1beta1.Application{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "reeval-app",
+				Namespace: ns.Name,
+			},
+			Spec: v1beta1.ApplicationSpec{
+				Components: []common.ApplicationComponent{
+					{
+						Name:       "deployment",
+						Type:       "deployment",
+						Properties: &runtime.RawExtension{Raw: []byte(`{"image":"nginx"}`)},
+					},
+				},
+				Policies: []v1beta1.AppPolicy{
+					{
+						Name:       "apply-once",
+						Type:       "apply-once",
+						Properties: &runtime.RawExtension{Raw: []byte(`{"enable":true}`)},
+					},
+				},
+			},
+		}
+
+		By("Applying the test component definition with modifiable health status")
+		defJson, err := yaml.YAMLToJSON([]byte(deploymentComponentDefinitionWithModifiableHealthStatusYaml))
+		Expect(err).ShouldNot(HaveOccurred())
+		u := &unstructured.Unstructured{}
+		err = json.Unmarshal(defJson, u)
+		Expect(err).ShouldNot(HaveOccurred())
+		err = k8sClient.Create(ctx, u.DeepCopy())
+		Expect(err).ShouldNot(HaveOccurred())
+
+		By("Creating the application with the modifiable deployment component")
+		Expect(k8sClient.Create(ctx, app)).Should(Succeed())
+
+		By("Running an initial reconciliation to create the application in a healthy state")
+		reconcileAppAndExpectPhase(app, common.ApplicationRunning)
+
+		By("Manually modifying the deployment to simulate health deterioration")
+		patchDeploymentHealthLabel(ns, false)
+
+		By("Re-reconciling to detect the change and reevaluate health")
+		reconcileAppAndExpectPhase(app, common.ApplicationUnhealthy)
+
+		By("Checking that both the component and application are marked unhealthy")
+		updated := &v1beta1.Application{}
+		Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(app), updated)).To(Succeed())
+		Expect(updated.Status.Services[0].Healthy).To(BeFalse())
+		Expect(updated.Status.Phase).To(Equal(common.ApplicationUnhealthy))
+
+		By("Reconciling without change to ensure health remains unhealthy")
+		reconcileAppAndExpectPhase(app, common.ApplicationUnhealthy)
+
+		By("Manually modifying the deployment to simulate health restoration")
+		patchDeploymentHealthLabel(ns, true)
+
+		By("Re-reconciling to detect the change and ensure healthy state is restored")
+		reconcileAppAndExpectPhase(app, common.ApplicationRunning)
+	})
 })
 
 const (
@@ -4576,6 +4678,59 @@ spec:
         	$params: value: workloads
         }
         parameter: parallelism: int
+`
+	deploymentComponentDefinitionWithModifiableHealthStatusYaml = `
+apiVersion: core.oam.dev/v1beta1
+kind: ComponentDefinition
+metadata:
+  annotations:
+    definition.oam.dev/description: Simple deployment
+  name: deployment
+  namespace: vela-system
+spec:
+  workload:
+    definition:
+      apiVersion: apps/v1
+      kind: Deployment
+  status:
+    healthPolicy: |
+      isHealth: context.output.metadata.labels.healthy == "true"
+  schematic:
+    cue:
+      template: |
+        parameter: {
+          image: string
+        }
+        output: {
+          apiVersion: "apps/v1"
+          kind: "Deployment"
+          metadata: {
+            name: "deployment"
+            namespace: context.namespace
+            labels: {
+              app: context.name
+              healthy: "true"
+            }
+          }
+          spec: {
+            replicas: 1
+            selector: matchLabels: {
+              app: context.name
+            }
+            template: {
+              metadata: labels: {
+                app: context.name
+              }
+              spec: containers: [{
+                name:  context.name
+                image: parameter.image
+                ports: [{
+                  containerPort: 80
+                }]
+              }]
+            }
+          }
+        }
 `
 )
 
diff --git a/pkg/workflow/providers/legacy/multicluster/deploy_test.go b/pkg/workflow/providers/legacy/multicluster/deploy_test.go
index 4289a0d9..95aaf7d3 100644
--- a/pkg/workflow/providers/legacy/multicluster/deploy_test.go
+++ b/pkg/workflow/providers/legacy/multicluster/deploy_test.go
@@ -119,9 +119,9 @@ func TestApplyComponentsDepends(t *testing.T) {
 		applyMap.Store(fmt.Sprintf("%s/%s", clusterName, comp.Name), true)
 		return nil, nil, true, nil
 	}
-	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *apicommon.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 		_, found := applyMap.Load(fmt.Sprintf("%s/%s", clusterName, comp.Name))
-		return found, nil, nil, nil
+		return found, nil, nil, nil, nil
 	}
 	parallelism := 10
 
@@ -163,9 +163,9 @@ func TestApplyComponentsIO(t *testing.T) {
 		applyMap.Store(fmt.Sprintf("%s/%s", clusterName, comp.Name), true)
 		return nil, nil, true, nil
 	}
-	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *apicommon.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 		_, found := applyMap.Load(fmt.Sprintf("%s/%s", clusterName, comp.Name))
-		return found, &unstructured.Unstructured{Object: map[string]interface{}{
+		return found, nil, &unstructured.Unstructured{Object: map[string]interface{}{
 				"spec": map[string]interface{}{
 					"path": fmt.Sprintf("%s/%s", clusterName, comp.Name),
 				},
@@ -320,11 +320,11 @@ func TestApplyComponentsIO(t *testing.T) {
 			applyMap.Store(storeKey(clusterName, comp), result)
 			return nil, nil, true, nil
 		}
-		healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+		healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *apicommon.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 			key := storeKey(clusterName, comp)
 			r, found := applyMap.Load(key)
 			result, _ := r.(applyResult)
-			return found, result.output, result.outputs, nil
+			return found, nil, result.output, result.outputs, nil
 		}
 
 		inputSlot := "input_slot"
diff --git a/pkg/workflow/providers/multicluster/deploy_test.go b/pkg/workflow/providers/multicluster/deploy_test.go
index 4289a0d9..95aaf7d3 100644
--- a/pkg/workflow/providers/multicluster/deploy_test.go
+++ b/pkg/workflow/providers/multicluster/deploy_test.go
@@ -119,9 +119,9 @@ func TestApplyComponentsDepends(t *testing.T) {
 		applyMap.Store(fmt.Sprintf("%s/%s", clusterName, comp.Name), true)
 		return nil, nil, true, nil
 	}
-	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *apicommon.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 		_, found := applyMap.Load(fmt.Sprintf("%s/%s", clusterName, comp.Name))
-		return found, nil, nil, nil
+		return found, nil, nil, nil, nil
 	}
 	parallelism := 10
 
@@ -163,9 +163,9 @@ func TestApplyComponentsIO(t *testing.T) {
 		applyMap.Store(fmt.Sprintf("%s/%s", clusterName, comp.Name), true)
 		return nil, nil, true, nil
 	}
-	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+	healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *apicommon.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 		_, found := applyMap.Load(fmt.Sprintf("%s/%s", clusterName, comp.Name))
-		return found, &unstructured.Unstructured{Object: map[string]interface{}{
+		return found, nil, &unstructured.Unstructured{Object: map[string]interface{}{
 				"spec": map[string]interface{}{
 					"path": fmt.Sprintf("%s/%s", clusterName, comp.Name),
 				},
@@ -320,11 +320,11 @@ func TestApplyComponentsIO(t *testing.T) {
 			applyMap.Store(storeKey(clusterName, comp), result)
 			return nil, nil, true, nil
 		}
-		healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
+		healthCheck := func(_ context.Context, comp apicommon.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (bool, *apicommon.ApplicationComponentStatus, *unstructured.Unstructured, []*unstructured.Unstructured, error) {
 			key := storeKey(clusterName, comp)
 			r, found := applyMap.Load(key)
 			result, _ := r.(applyResult)
-			return found, result.output, result.outputs, nil
+			return found, nil, result.output, result.outputs, nil
 		}
 
 		inputSlot := "input_slot"