Files
argo-cd/controller/health.go
2026-02-12 09:29:40 -05:00

105 lines
3.9 KiB
Go

package controller
import (
"fmt"
"github.com/argoproj/argo-cd/gitops-engine/pkg/health"
hookutil "github.com/argoproj/argo-cd/gitops-engine/pkg/sync/hook"
"github.com/argoproj/argo-cd/gitops-engine/pkg/sync/ignore"
kubeutil "github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/v3/common"
"github.com/argoproj/argo-cd/v3/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
applog "github.com/argoproj/argo-cd/v3/util/app/log"
"github.com/argoproj/argo-cd/v3/util/lua"
)
// setApplicationHealth updates the health statuses of all resources performed in the comparison
func setApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus, resourceOverrides map[string]appv1.ResourceOverride, app *appv1.Application, persistResourceHealth bool) (health.HealthStatusCode, error) {
var savedErr error
var errCount uint
var containsResources, containsLiveResources bool
appHealthStatus := health.HealthStatusHealthy
for i, res := range resources {
if res.Target != nil && hookutil.Skip(res.Target) {
continue
}
if res.Live != nil && (hookutil.IsHook(res.Live) || ignore.Ignore(res.Live)) {
continue
}
// Contains actual resources that are not hooks
containsResources = true
if res.Live != nil {
containsLiveResources = true
}
// Do not aggregate the health of the resource if the annotation to ignore health check is set to true
if res.Live != nil && res.Live.GetAnnotations() != nil && res.Live.GetAnnotations()[common.AnnotationIgnoreHealthCheck] == "true" {
continue
}
var healthStatus *health.HealthStatus
var err error
healthOverrides := lua.ResourceHealthOverrides(resourceOverrides)
if res.Live == nil {
healthStatus = &health.HealthStatus{Status: health.HealthStatusMissing}
} else {
// App that manages itself should not affect own health
if isSelfReferencedApp(app, kubeutil.GetObjectRef(res.Live)) {
continue
}
healthStatus, err = health.GetResourceHealth(res.Live, healthOverrides)
if err != nil && savedErr == nil {
errCount++
savedErr = fmt.Errorf("failed to get resource health for %q with name %q in namespace %q: %w", res.Live.GetKind(), res.Live.GetName(), res.Live.GetNamespace(), err)
// also log so we don't lose the message
log.WithFields(applog.GetAppLogFields(app)).Warn(savedErr)
}
}
if healthStatus == nil {
continue
}
if persistResourceHealth {
resHealth := appv1.HealthStatus{Status: healthStatus.Status, Message: healthStatus.Message}
statuses[i].Health = &resHealth
} else {
statuses[i].Health = nil
}
// Missing resources should not affect parent app health - the OutOfSync status already indicates resources are missing
if res.Live == nil && healthStatus.Status == health.HealthStatusMissing {
continue
}
// Missing or Unknown health status of child Argo CD app should not affect parent
if res.Kind == application.ApplicationKind && res.Group == application.Group && (healthStatus.Status == health.HealthStatusMissing || healthStatus.Status == health.HealthStatusUnknown) {
continue
}
if health.IsWorse(appHealthStatus, healthStatus.Status) {
appHealthStatus = healthStatus.Status
}
}
// If the app is expected to have resources but does not contain any live resources, set the app health to missing
if containsResources && !containsLiveResources && health.IsWorse(appHealthStatus, health.HealthStatusMissing) {
appHealthStatus = health.HealthStatusMissing
}
if persistResourceHealth {
app.Status.ResourceHealthSource = appv1.ResourceHealthLocationInline
} else {
app.Status.ResourceHealthSource = appv1.ResourceHealthLocationAppTree
}
if savedErr != nil && errCount > 1 {
savedErr = fmt.Errorf("see application-controller logs for %d other errors; most recent error was: %w", errCount-1, savedErr)
}
return appHealthStatus, savedErr
}