mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
fix: missing live resources without a health check should not affect application health (#6446)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f533ed17f0
commit
2da0e81b05
64
controller/health.go
Normal file
64
controller/health.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/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) (*appv1.HealthStatus, error) {
|
||||
var savedErr error
|
||||
appHealth := appv1.HealthStatus{Status: 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
|
||||
}
|
||||
|
||||
var healthStatus *health.HealthStatus
|
||||
var err error
|
||||
healthOverrides := lua.ResourceHealthOverrides(resourceOverrides)
|
||||
gvk := schema.GroupVersionKind{Group: res.Group, Version: res.Version, Kind: res.Kind}
|
||||
if res.Live == nil {
|
||||
healthStatus = &health.HealthStatus{Status: health.HealthStatusMissing}
|
||||
} else {
|
||||
// App the 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 {
|
||||
savedErr = err
|
||||
}
|
||||
}
|
||||
if healthStatus != nil {
|
||||
resHealth := appv1.HealthStatus{Status: healthStatus.Status, Message: healthStatus.Message}
|
||||
statuses[i].Health = &resHealth
|
||||
|
||||
// Is health status is missing but resource has not built-in/custom health check then it should not affect parent app health
|
||||
if _, hasOverride := healthOverrides[lua.GetConfigMapKey(gvk)]; healthStatus.Status == health.HealthStatusMissing && !hasOverride && health.GetHealthCheckFunc(gvk) == nil {
|
||||
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(appHealth.Status, healthStatus.Status) {
|
||||
appHealth.Status = healthStatus.Status
|
||||
}
|
||||
}
|
||||
}
|
||||
return &appHealth, savedErr
|
||||
}
|
||||
161
controller/health_test.go
Normal file
161
controller/health_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/lua"
|
||||
)
|
||||
|
||||
var app = &appv1.Application{}
|
||||
|
||||
func initStatuses(resources []managedResource) []appv1.ResourceStatus {
|
||||
statuses := make([]appv1.ResourceStatus, len(resources))
|
||||
for i := range resources {
|
||||
statuses[i] = appv1.ResourceStatus{Group: resources[i].Group, Kind: resources[i].Kind, Version: resources[i].Version}
|
||||
}
|
||||
return statuses
|
||||
}
|
||||
|
||||
func resourceFromFile(filePath string) unstructured.Unstructured {
|
||||
yamlBytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var res unstructured.Unstructured
|
||||
err = yaml.Unmarshal(yamlBytes, &res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func TestSetApplicationHealth(t *testing.T) {
|
||||
failedJob := resourceFromFile("./testdata/job-failed.yaml")
|
||||
runningPod := resourceFromFile("./testdata/pod-running-restart-always.yaml")
|
||||
|
||||
resources := []managedResource{{
|
||||
Group: "", Version: "v1", Kind: "Pod", Live: &runningPod}, {
|
||||
Group: "batch", Version: "v1", Kind: "Job", Live: &failedJob,
|
||||
}}
|
||||
resourceStatuses := initStatuses(resources)
|
||||
|
||||
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status)
|
||||
|
||||
// now mark the job as a hook and retry. it should ignore the hook and consider the app healthy
|
||||
failedJob.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
|
||||
healthStatus, err = setApplicationHealth(resources, resourceStatuses, nil, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
}
|
||||
|
||||
func TestSetApplicationHealth_MissingResource(t *testing.T) {
|
||||
pod := resourceFromFile("./testdata/pod-running-restart-always.yaml")
|
||||
|
||||
resources := []managedResource{{
|
||||
Group: "", Version: "v1", Kind: "Pod", Target: &pod}, {}}
|
||||
resourceStatuses := initStatuses(resources)
|
||||
|
||||
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusMissing, healthStatus.Status)
|
||||
}
|
||||
|
||||
func TestSetApplicationHealth_MissingResourceNoBuiltHealthCheck(t *testing.T) {
|
||||
cm := resourceFromFile("./testdata/configmap.yaml")
|
||||
|
||||
resources := []managedResource{{
|
||||
Group: "", Version: "v1", Kind: "ConfigMap", Target: &cm}}
|
||||
resourceStatuses := initStatuses(resources)
|
||||
|
||||
t.Run("NoOverride", func(t *testing.T) {
|
||||
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
assert.Equal(t, resourceStatuses[0].Health.Status, health.HealthStatusMissing)
|
||||
})
|
||||
|
||||
t.Run("HasOverride", func(t *testing.T) {
|
||||
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{
|
||||
lua.GetConfigMapKey(schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}): appv1.ResourceOverride{
|
||||
HealthLua: "some health check",
|
||||
},
|
||||
}, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusMissing, healthStatus.Status)
|
||||
})
|
||||
}
|
||||
|
||||
func newAppLiveObj(status health.HealthStatusCode) *unstructured.Unstructured {
|
||||
app := appv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
Kind: "Application",
|
||||
},
|
||||
Status: appv1.ApplicationStatus{
|
||||
Health: appv1.HealthStatus{
|
||||
Status: status,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return kube.MustToUnstructured(&app)
|
||||
}
|
||||
|
||||
func TestChildAppHealth(t *testing.T) {
|
||||
overrides := lua.ResourceHealthOverrides{
|
||||
lua.GetConfigMapKey(appv1.ApplicationSchemaGroupVersionKind): appv1.ResourceOverride{
|
||||
HealthLua: `
|
||||
hs = {}
|
||||
hs.status = "Progressing"
|
||||
hs.message = ""
|
||||
if obj.status ~= nil then
|
||||
if obj.status.health ~= nil then
|
||||
hs.status = obj.status.health.status
|
||||
if obj.status.health.message ~= nil then
|
||||
hs.message = obj.status.health.message
|
||||
end
|
||||
end
|
||||
end
|
||||
return hs`,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("ChildAppDegraded", func(t *testing.T) {
|
||||
degradedApp := newAppLiveObj(health.HealthStatusDegraded)
|
||||
resources := []managedResource{{
|
||||
Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp}, {}}
|
||||
resourceStatuses := initStatuses(resources)
|
||||
|
||||
healthStatus, err := setApplicationHealth(resources, resourceStatuses, overrides, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status)
|
||||
})
|
||||
|
||||
t.Run("ChildAppMissing", func(t *testing.T) {
|
||||
degradedApp := newAppLiveObj(health.HealthStatusMissing)
|
||||
resources := []managedResource{{
|
||||
Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp}, {}}
|
||||
resourceStatuses := initStatuses(resources)
|
||||
|
||||
healthStatus, err := setApplicationHealth(resources, resourceStatuses, overrides, app)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
})
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
argohealth "github.com/argoproj/argo-cd/v2/util/health"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/stats"
|
||||
@@ -59,20 +58,6 @@ type managedResource struct {
|
||||
ResourceVersion string
|
||||
}
|
||||
|
||||
func GetLiveObjsForApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus) ([]*appv1.ResourceStatus, []*unstructured.Unstructured) {
|
||||
liveObjs := make([]*unstructured.Unstructured, 0)
|
||||
resStatuses := make([]*appv1.ResourceStatus, 0)
|
||||
for i, resource := range resources {
|
||||
if resource.Target != nil && hookutil.Skip(resource.Target) {
|
||||
continue
|
||||
}
|
||||
|
||||
liveObjs = append(liveObjs, resource.Live)
|
||||
resStatuses = append(resStatuses, &statuses[i])
|
||||
}
|
||||
return resStatuses, liveObjs
|
||||
}
|
||||
|
||||
// AppStateManager defines methods which allow to compare application spec and actual application state.
|
||||
type AppStateManager interface {
|
||||
CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string) *comparisonResult
|
||||
@@ -621,11 +606,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
ts.AddCheckpoint("sync_ms")
|
||||
|
||||
resSumForAppHealth, liveObjsForAppHealth := GetLiveObjsForApplicationHealth(managedResources, resourceSummaries)
|
||||
healthStatus, err := argohealth.SetApplicationHealth(resSumForAppHealth, liveObjsForAppHealth, resourceOverrides, func(obj *unstructured.Unstructured) bool {
|
||||
return !isSelfReferencedApp(app, kubeutil.GetObjectRef(obj))
|
||||
})
|
||||
|
||||
healthStatus, err := setApplicationHealth(managedResources, resourceSummaries, resourceOverrides, app)
|
||||
if err != nil {
|
||||
conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
}
|
||||
|
||||
6
controller/testdata/configmap.yaml
vendored
Normal file
6
controller/testdata/configmap.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
data:
|
||||
foo: bar
|
||||
2
go.mod
2
go.mod
@@ -7,7 +7,7 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/alicebob/miniredis/v2 v2.14.2
|
||||
github.com/argoproj/gitops-engine v0.3.1-0.20210608200535-ddc92c9bdbe9
|
||||
github.com/argoproj/gitops-engine v0.3.1-0.20210610000233-6884d330a049
|
||||
github.com/argoproj/pkg v0.9.1-0.20210512035321-be5ba22dca5b
|
||||
github.com/bombsimon/logrusr v1.0.0
|
||||
github.com/bradleyfalzon/ghinstallation v1.1.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -86,8 +86,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/argoproj/gitops-engine v0.3.1-0.20210608200535-ddc92c9bdbe9 h1:C4Im0i0XYjn5bY6DUJZt8gF2kcmXNF28DPgSIRZi2bo=
|
||||
github.com/argoproj/gitops-engine v0.3.1-0.20210608200535-ddc92c9bdbe9/go.mod h1:EdFe8qIOqsmbyxRhtIydU4BUeyZ4VTsY6R3XVQhU9LA=
|
||||
github.com/argoproj/gitops-engine v0.3.1-0.20210610000233-6884d330a049 h1:Swa2K+be8ZB1YeyCe5s2kR/b+gyawo5jcPRMwT3L5zY=
|
||||
github.com/argoproj/gitops-engine v0.3.1-0.20210610000233-6884d330a049/go.mod h1:EdFe8qIOqsmbyxRhtIydU4BUeyZ4VTsY6R3XVQhU9LA=
|
||||
github.com/argoproj/pkg v0.9.1-0.20210512035321-be5ba22dca5b h1:qtlM7ioAFP40LPN7A5ZqquVmAtv08LLSZTcCNYUQx8s=
|
||||
github.com/argoproj/pkg v0.9.1-0.20210512035321-be5ba22dca5b/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestFixingDegradedApp(t *testing.T) {
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationFailed)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusMissing)).
|
||||
Expect(HealthIs(health.HealthStatusDegraded)).
|
||||
Expect(ResourceResultNumbering(1)).
|
||||
Expect(ResourceSyncStatusIs("ConfigMap", "cm-1", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("ConfigMap", "cm-1", health.HealthStatusDegraded)).
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/lua"
|
||||
)
|
||||
|
||||
// SetApplicationHealth updates the health statuses of all resources performed in the comparison
|
||||
func SetApplicationHealth(resStatuses []*appv1.ResourceStatus, liveObjs []*unstructured.Unstructured, resourceOverrides map[string]appv1.ResourceOverride, filter func(obj *unstructured.Unstructured) bool) (*appv1.HealthStatus, error) {
|
||||
var savedErr error
|
||||
appHealth := appv1.HealthStatus{Status: health.HealthStatusHealthy}
|
||||
for i, liveObj := range liveObjs {
|
||||
var healthStatus *health.HealthStatus
|
||||
var err error
|
||||
if liveObj == nil {
|
||||
healthStatus = &health.HealthStatus{Status: health.HealthStatusMissing}
|
||||
} else {
|
||||
if filter(liveObj) {
|
||||
healthStatus, err = health.GetResourceHealth(liveObj, lua.ResourceHealthOverrides(resourceOverrides))
|
||||
if err != nil && savedErr == nil {
|
||||
savedErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
if healthStatus != nil {
|
||||
resHealth := appv1.HealthStatus{Status: healthStatus.Status, Message: healthStatus.Message}
|
||||
resStatuses[i].Health = &resHealth
|
||||
ignore := ignoreLiveObjectHealth(liveObj, resHealth)
|
||||
if !ignore && health.IsWorse(appHealth.Status, healthStatus.Status) {
|
||||
appHealth.Status = healthStatus.Status
|
||||
}
|
||||
}
|
||||
}
|
||||
return &appHealth, savedErr
|
||||
}
|
||||
|
||||
// ignoreLiveObjectHealth determines if we should not allow the live object to affect the overall
|
||||
// health of the application (e.g. hooks, missing child applications)
|
||||
func ignoreLiveObjectHealth(liveObj *unstructured.Unstructured, resHealth appv1.HealthStatus) bool {
|
||||
if liveObj != nil {
|
||||
// Don't allow resource hooks to affect health status
|
||||
if hookutil.IsHook(liveObj) || ignore.Ignore(liveObj) {
|
||||
return true
|
||||
}
|
||||
gvk := liveObj.GroupVersionKind()
|
||||
if gvk.Group == "argoproj.io" && gvk.Kind == "Application" && (resHealth.Status == health.HealthStatusMissing || resHealth.Status == health.HealthStatusUnknown) {
|
||||
// Covers the app-of-apps corner case where child app is deployed but that app itself
|
||||
// has a status of 'Missing', which we don't want to cause the parent's health status
|
||||
// to also be Missing
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestSetApplicationHealth(t *testing.T) {
|
||||
yamlBytes, err := ioutil.ReadFile("./testdata/job-failed.yaml")
|
||||
assert.Nil(t, err)
|
||||
var failedJob unstructured.Unstructured
|
||||
err = yaml.Unmarshal(yamlBytes, &failedJob)
|
||||
assert.Nil(t, err)
|
||||
|
||||
yamlBytes, err = ioutil.ReadFile("./testdata/pod-running-restart-always.yaml")
|
||||
assert.Nil(t, err)
|
||||
var runningPod unstructured.Unstructured
|
||||
err = yaml.Unmarshal(yamlBytes, &runningPod)
|
||||
assert.Nil(t, err)
|
||||
|
||||
resources := []*appv1.ResourceStatus{
|
||||
&appv1.ResourceStatus{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
Name: runningPod.GetName(),
|
||||
},
|
||||
&appv1.ResourceStatus{
|
||||
Group: "batch",
|
||||
Version: "v1",
|
||||
Kind: "Job",
|
||||
Name: failedJob.GetName(),
|
||||
},
|
||||
}
|
||||
liveObjs := []*unstructured.Unstructured{
|
||||
&runningPod,
|
||||
&failedJob,
|
||||
}
|
||||
healthStatus, err := SetApplicationHealth(resources, liveObjs, nil, noFilter)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status)
|
||||
|
||||
// now mark the job as a hook and retry. it should ignore the hook and consider the app healthy
|
||||
failedJob.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
|
||||
healthStatus, err = SetApplicationHealth(resources, liveObjs, nil, noFilter)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
}
|
||||
|
||||
func TestAppOfAppsHealth(t *testing.T) {
|
||||
newAppLiveObj := func(name string, status health.HealthStatusCode) (*unstructured.Unstructured, *appv1.ResourceStatus) {
|
||||
app := appv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
Kind: "Application",
|
||||
},
|
||||
Status: appv1.ApplicationStatus{
|
||||
Health: appv1.HealthStatus{
|
||||
Status: status,
|
||||
},
|
||||
},
|
||||
}
|
||||
resStatus := &appv1.ResourceStatus{
|
||||
Group: "argoproj.io",
|
||||
Version: "v1alpha1",
|
||||
Kind: "Application",
|
||||
Name: name,
|
||||
}
|
||||
return kube.MustToUnstructured(&app), resStatus
|
||||
}
|
||||
|
||||
missingApp, missingStatus := newAppLiveObj("foo", health.HealthStatusMissing)
|
||||
unknownApp, unknownStatus := newAppLiveObj("fooz", health.HealthStatusUnknown)
|
||||
healthyApp, healthyStatus := newAppLiveObj("bar", health.HealthStatusHealthy)
|
||||
degradedApp, degradedStatus := newAppLiveObj("baz", health.HealthStatusDegraded)
|
||||
|
||||
// verify missing child app does not affect app health
|
||||
{
|
||||
missingAndHealthyStatuses := []*appv1.ResourceStatus{missingStatus, healthyStatus}
|
||||
missingAndHealthyLiveObjects := []*unstructured.Unstructured{missingApp, healthyApp}
|
||||
healthStatus, err := SetApplicationHealth(missingAndHealthyStatuses, missingAndHealthyLiveObjects, nil, noFilter)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
}
|
||||
|
||||
// verify unknown child app does not affect app health
|
||||
{
|
||||
unknownAndHealthyStatuses := []*appv1.ResourceStatus{unknownStatus, healthyStatus}
|
||||
unknownAndHealthyLiveObjects := []*unstructured.Unstructured{unknownApp, healthyApp}
|
||||
healthStatus, err := SetApplicationHealth(unknownAndHealthyStatuses, unknownAndHealthyLiveObjects, nil, noFilter)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
}
|
||||
|
||||
// verify degraded does affect
|
||||
{
|
||||
degradedAndHealthyStatuses := []*appv1.ResourceStatus{degradedStatus, healthyStatus}
|
||||
degradedAndHealthyLiveObjects := []*unstructured.Unstructured{degradedApp, healthyApp}
|
||||
healthStatus, err := SetApplicationHealth(degradedAndHealthyStatuses, degradedAndHealthyLiveObjects, nil, noFilter)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func noFilter(obj *unstructured.Unstructured) bool {
|
||||
return true
|
||||
}
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/resource_customizations"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
luajson "layeh.com/gopher-json"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/resource_customizations"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -122,7 +122,7 @@ func (vm VM) ExecuteHealthLua(obj *unstructured.Unstructured, script string) (*h
|
||||
|
||||
// GetHealthScript attempts to read lua script from config and then filesystem for that resource
|
||||
func (vm VM) GetHealthScript(obj *unstructured.Unstructured) (string, bool, error) {
|
||||
key := getConfigMapKey(obj)
|
||||
key := GetConfigMapKey(obj.GroupVersionKind())
|
||||
if script, ok := vm.ResourceOverrides[key]; ok && script.HealthLua != "" {
|
||||
return script.HealthLua, script.UseOpenLibs, nil
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func noAvailableActions(jsonBytes []byte) bool {
|
||||
}
|
||||
|
||||
func (vm VM) GetResourceActionDiscovery(obj *unstructured.Unstructured) (string, error) {
|
||||
key := getConfigMapKey(obj)
|
||||
key := GetConfigMapKey(obj.GroupVersionKind())
|
||||
override, ok := vm.ResourceOverrides[key]
|
||||
if ok && override.Actions != "" {
|
||||
actions, err := override.GetActions()
|
||||
@@ -300,7 +300,7 @@ func (vm VM) GetResourceActionDiscovery(obj *unstructured.Unstructured) (string,
|
||||
|
||||
// GetResourceAction attempts to read lua script from config and then filesystem for that resource
|
||||
func (vm VM) GetResourceAction(obj *unstructured.Unstructured, actionName string) (appv1.ResourceActionDefinition, error) {
|
||||
key := getConfigMapKey(obj)
|
||||
key := GetConfigMapKey(obj.GroupVersionKind())
|
||||
override, ok := vm.ResourceOverrides[key]
|
||||
if ok && override.Actions != "" {
|
||||
actions, err := override.GetActions()
|
||||
@@ -326,13 +326,11 @@ func (vm VM) GetResourceAction(obj *unstructured.Unstructured, actionName string
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getConfigMapKey(obj *unstructured.Unstructured) string {
|
||||
gvk := obj.GroupVersionKind()
|
||||
func GetConfigMapKey(gvk schema.GroupVersionKind) string {
|
||||
if gvk.Group == "" {
|
||||
return gvk.Kind
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind)
|
||||
|
||||
}
|
||||
|
||||
func (vm VM) getPredefinedLuaScripts(objKey string, scriptFile string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user