Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(health): only consider non-empty health checks (cherry-pick #20232) #20235

Merged
merged 1 commit into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions docs/operator-manual/health.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,27 @@ data:
return hs
```

In order to prevent duplication of the custom health check for potentially multiple resources, it is also possible to specify a wildcard in the resource kind, and anywhere in the resource group, like this:
In order to prevent duplication of custom health checks for potentially multiple resources, it is also possible to
specify a wildcard in the resource kind, and anywhere in the resource group, like this:

```yaml
resource.customizations.health.ec2.aws.crossplane.io_*: |
...
resource.customizations: |
ec2.aws.crossplane.io/*:
health.lua: |
...
```

```yaml
resource.customizations.health.*.aws.crossplane.io_*: |
...
# If a key _begins_ with a wildcard, please ensure that the GVK key is quoted.
resource.customizations: |
"*.aws.crossplane.io/*":
health.lua: |
...
```

!!!important
Please, note that there can be ambiguous resolution of wildcards, see [#16905](https://github.com/argoproj/argo-cd/issues/16905)
Please, note that wildcards are only supported when using the `resource.customizations` key, the `resource.customizations.health.<group>_<kind>`
style keys do not work since wildcards (`*`) are not supported in Kubernetes configmap keys.

The `obj` is a global variable which contains the resource. The script must return an object with status and optional message field.
The custom health check might return one of the following health statuses:
Expand All @@ -121,7 +128,7 @@ The custom health check might return one of the following health statuses:
* `Degraded` - the resource is degraded
* `Suspended` - the resource is suspended and waiting for some external event to resume (e.g. suspended CronJob or paused Deployment)

By default health typically returns `Progressing` status.
By default, health typically returns a `Progressing` status.

NOTE: As a security measure, access to the standard Lua libraries will be disabled by default. Admins can control access by
setting `resource.customizations.useOpenLibs.<group>_<kind>`. In the following example, standard libraries are enabled for health check of `cert-manager.io/Certificate`.
Expand Down
23 changes: 12 additions & 11 deletions util/lua/lua.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,11 @@ func (vm VM) GetHealthScript(obj *unstructured.Unstructured) (string, bool, erro
return script.HealthLua, script.UseOpenLibs, nil
}

// if not found as is, perhaps it matches wildcard entries in the configmap
wildcardKey := GetWildcardConfigMapKey(vm, obj.GroupVersionKind())
// if not found as is, perhaps it matches a wildcard entry in the configmap
getWildcardHealthOverride, useOpenLibs := getWildcardHealthOverrideLua(vm.ResourceOverrides, obj.GroupVersionKind())

if wildcardKey != "" {
if wildcardScript, ok := vm.ResourceOverrides[wildcardKey]; ok && wildcardScript.HealthLua != "" {
return wildcardScript.HealthLua, wildcardScript.UseOpenLibs, nil
}
if getWildcardHealthOverride != "" {
return getWildcardHealthOverride, useOpenLibs, nil
}

// if not found in the ResourceOverrides at all, search it as is in the built-in scripts
Expand Down Expand Up @@ -426,15 +424,18 @@ func GetConfigMapKey(gvk schema.GroupVersionKind) string {
return fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind)
}

func GetWildcardConfigMapKey(vm VM, gvk schema.GroupVersionKind) string {
// getWildcardHealthOverrideLua returns the first encountered resource override which matches the wildcard and has a
// non-empty health script. Having multiple wildcards with non-empty health checks that can match the GVK is
// non-deterministic.
func getWildcardHealthOverrideLua(overrides map[string]appv1.ResourceOverride, gvk schema.GroupVersionKind) (string, bool) {
gvkKeyToMatch := GetConfigMapKey(gvk)

for key := range vm.ResourceOverrides {
if glob.Match(key, gvkKeyToMatch) {
return key
for key, override := range overrides {
if glob.Match(key, gvkKeyToMatch) && override.HealthLua != "" {
return override.HealthLua, override.UseOpenLibs
}
}
return ""
return "", false
}

func (vm VM) getPredefinedLuaScripts(objKey string, scriptFile string) (string, error) {
Expand Down
37 changes: 37 additions & 0 deletions util/lua/lua_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,11 @@ return hs`
const healthWildcardOverrideScript = `
hs = {}
hs.status = "Healthy"
return hs`

const healthWildcardOverrideScriptUnhealthy = `
hs = {}
hs.status = "UnHealthy"
return hs`

getHealthOverride := func(openLibs bool) ResourceHealthOverrides {
Expand All @@ -804,6 +809,21 @@ return hs`
},
}

getMultipleWildcardHealthOverrides := ResourceHealthOverrides{
"*.aws.crossplane.io/*": appv1.ResourceOverride{
HealthLua: "",
},
"*.aws*": appv1.ResourceOverride{
HealthLua: healthWildcardOverrideScriptUnhealthy,
},
}

getBaseWildcardHealthOverrides := ResourceHealthOverrides{
"*/*": appv1.ResourceOverride{
HealthLua: "",
},
}

t.Run("Enable Lua standard lib", func(t *testing.T) {
testObj := StrToUnstructured(testSA)
overrides := getHealthOverride(true)
Expand Down Expand Up @@ -837,6 +857,23 @@ return hs`
assert.Equal(t, expectedStatus, status)
})

t.Run("Get resource health for wildcard override with non-empty health.lua", func(t *testing.T) {
testObj := StrToUnstructured(ec2AWSCrossplaneObjJson)
overrides := getMultipleWildcardHealthOverrides
status, err := overrides.GetResourceHealth(testObj)
require.NoError(t, err)
expectedStatus := &health.HealthStatus{Status: "Unknown", Message: "Lua returned an invalid health status"}
assert.Equal(t, expectedStatus, status)
})

t.Run("Get resource health for */* override with empty health.lua", func(t *testing.T) {
testObj := StrToUnstructured(ec2AWSCrossplaneObjJson)
overrides := getBaseWildcardHealthOverrides
status, err := overrides.GetResourceHealth(testObj)
require.NoError(t, err)
assert.Nil(t, status)
})

t.Run("Resource health for wildcard override not found", func(t *testing.T) {
testObj := StrToUnstructured(testSA)
overrides := getWildcardHealthOverride
Expand Down
Loading