Skip to content

Commit 9149021

Browse files
feat(health): add crossplane and upbound health checks (#21479) (#22919)
Signed-off-by: Michael Crenshaw <[email protected]> Signed-off-by: Alexandre Gaudreault <[email protected]> Co-authored-by: Alexandre Gaudreault <[email protected]>
1 parent 722da4e commit 9149021

File tree

10 files changed

+316
-21
lines changed

10 files changed

+316
-21
lines changed

docs/operator-manual/health.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,33 @@ To test the implemented custom health checks, run `go test -v ./util/lua/`.
168168

169169
The [PR#1139](https://github.com/argoproj/argo-cd/pull/1139) is an example of Cert Manager CRDs custom health check.
170170

171-
Please note that bundled health checks with wildcards are not supported.
171+
#### Wildcard Support for Built-in Health Checks
172+
173+
You can use a single health check for multiple resources by using a wildcard in the group or kind directory names.
174+
175+
The `_` character behaves like a `*` wildcard. For example, consider the following directory structure:
176+
177+
```
178+
argo-cd
179+
|-- resource_customizations
180+
| |-- _.group.io # CRD group
181+
| | |-- _ # Resource kind
182+
| | | |-- health.lua # Health check
183+
```
184+
185+
Any resource with a group that ends with `.group.io` will use the health check in `health.lua`.
186+
187+
Wildcard checks are only evaluated if there is no specific check for the resource.
188+
189+
If multiple wildcard checks match, the first one in the directory structure is used.
190+
191+
We use the [doublestar](https://github.com/bmatcuk/doublestar) glob library to match the wildcard checks. We currently
192+
only treat a path as a wildcard if it contains a `_` character, but this may change in the future.
193+
194+
!!!important "Avoid Massive Scripts"
195+
196+
Avoid writing massive scripts to handle multiple resources. They'll get hard to read and maintain. Instead, just
197+
duplicate the relevant parts in resource-specific scripts.
172198

173199
## Overriding Go-Based Health Checks
174200

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
-- Health check copied from here: https://github.com/crossplane/docs/blob/bd701357e9d5eecf529a0b42f23a78850a6d1d87/content/master/guides/crossplane-with-argo-cd.md
2+
3+
health_status = {
4+
status = "Progressing",
5+
message = "Provisioning ..."
6+
}
7+
8+
local function contains (table, val)
9+
for i, v in ipairs(table) do
10+
if v == val then
11+
return true
12+
end
13+
end
14+
return false
15+
end
16+
17+
local has_no_status = {
18+
"Composition",
19+
"CompositionRevision",
20+
"DeploymentRuntimeConfig",
21+
"ControllerConfig",
22+
"ProviderConfig",
23+
"ProviderConfigUsage"
24+
}
25+
if obj.status == nil or next(obj.status) == nil and contains(has_no_status, obj.kind) then
26+
health_status.status = "Healthy"
27+
health_status.message = "Resource is up-to-date."
28+
return health_status
29+
end
30+
31+
if obj.status == nil or next(obj.status) == nil or obj.status.conditions == nil then
32+
if obj.kind == "ProviderConfig" and obj.status.users ~= nil then
33+
health_status.status = "Healthy"
34+
health_status.message = "Resource is in use."
35+
return health_status
36+
end
37+
return health_status
38+
end
39+
40+
for i, condition in ipairs(obj.status.conditions) do
41+
if condition.type == "LastAsyncOperation" then
42+
if condition.status == "False" then
43+
health_status.status = "Degraded"
44+
health_status.message = condition.message
45+
return health_status
46+
end
47+
end
48+
49+
if condition.type == "Synced" then
50+
if condition.status == "False" then
51+
health_status.status = "Degraded"
52+
health_status.message = condition.message
53+
return health_status
54+
end
55+
end
56+
57+
if contains({"Ready", "Healthy", "Offered", "Established"}, condition.type) then
58+
if condition.status == "True" then
59+
health_status.status = "Healthy"
60+
health_status.message = "Resource is up-to-date."
61+
return health_status
62+
end
63+
end
64+
end
65+
66+
return health_status
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
tests:
2+
- healthStatus:
3+
status: Healthy
4+
message: "Resource is up-to-date."
5+
inputPath: testdata/composition_healthy.yaml
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Taken from here May 9, 2025: https://docs.crossplane.io/latest/concepts/compositions/
2+
apiVersion: apiextensions.crossplane.io/v1
3+
kind: Composition
4+
metadata:
5+
name: example
6+
spec:
7+
compositeTypeRef:
8+
apiVersion: custom-api.example.org/v1alpha1
9+
kind: AcmeBucket
10+
mode: Pipeline
11+
pipeline:
12+
- step: patch-and-transform
13+
functionRef:
14+
name: function-patch-and-transform
15+
input:
16+
apiVersion: pt.fn.crossplane.io/v1beta1
17+
kind: Resources
18+
resources:
19+
- name: storage-bucket
20+
base:
21+
apiVersion: s3.aws.upbound.io/v1beta1
22+
kind: Bucket
23+
spec:
24+
forProvider:
25+
region: "us-east-2"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
-- Health check copied from here: https://github.com/crossplane/docs/blob/bd701357e9d5eecf529a0b42f23a78850a6d1d87/content/master/guides/crossplane-with-argo-cd.md
2+
3+
health_status = {
4+
status = "Progressing",
5+
message = "Provisioning ..."
6+
}
7+
8+
local function contains (table, val)
9+
for i, v in ipairs(table) do
10+
if v == val then
11+
return true
12+
end
13+
end
14+
return false
15+
end
16+
17+
local has_no_status = {
18+
"ProviderConfig",
19+
"ProviderConfigUsage"
20+
}
21+
22+
if obj.status == nil or next(obj.status) == nil and contains(has_no_status, obj.kind) then
23+
health_status.status = "Healthy"
24+
health_status.message = "Resource is up-to-date."
25+
return health_status
26+
end
27+
28+
if obj.status == nil or next(obj.status) == nil or obj.status.conditions == nil then
29+
if obj.kind == "ProviderConfig" and obj.status.users ~= nil then
30+
health_status.status = "Healthy"
31+
health_status.message = "Resource is in use."
32+
return health_status
33+
end
34+
return health_status
35+
end
36+
37+
for i, condition in ipairs(obj.status.conditions) do
38+
if condition.type == "LastAsyncOperation" then
39+
if condition.status == "False" then
40+
health_status.status = "Degraded"
41+
health_status.message = condition.message
42+
return health_status
43+
end
44+
end
45+
46+
if condition.type == "Synced" then
47+
if condition.status == "False" then
48+
health_status.status = "Degraded"
49+
health_status.message = condition.message
50+
return health_status
51+
end
52+
end
53+
54+
if condition.type == "Ready" then
55+
if condition.status == "True" then
56+
health_status.status = "Healthy"
57+
health_status.message = "Resource is up-to-date."
58+
return health_status
59+
end
60+
end
61+
end
62+
63+
return health_status
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
tests:
2+
- healthStatus:
3+
status: Healthy
4+
message: "Resource is up-to-date."
5+
inputPath: testdata/providerconfig_healthy.yaml
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: aws.upbound.io/v1beta1
2+
kind: ProviderConfig
3+
metadata:
4+
name: irsa-with-role-chaining
5+
spec:
6+
credentials:
7+
source: IRSA
8+
assumeRoleChain:
9+
- roleARN: <roleARN-1>
10+
- roleARN: <roleARN-2>

resource_customizations/embed.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ import (
66

77
// Embedded contains embedded resource customization
88
//
9-
//go:embed *
9+
//go:embed all:*
1010
var Embedded embed.FS

0 commit comments

Comments
 (0)