Skip to content

Commit 159263b

Browse files
committed
Add Application's health check
1 parent a95d629 commit 159263b

File tree

3 files changed

+126
-99
lines changed

3 files changed

+126
-99
lines changed

internal/controller/argo.go

Lines changed: 109 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ g, admin, role:admin`
9898
MountPath: "/etc/pki/tls/certs",
9999
},
100100
}
101-
102101
initContainers := []v1.Container{
103102
{
104103
Name: "fetch-ca",
@@ -124,6 +123,114 @@ g, admin, role:admin`
124123
},
125124
},
126125
}
126+
resourceHealthChecks := []argooperator.ResourceHealthCheck{
127+
{
128+
// We can drop this custom Subscription healthcheck once https://www.github.com/argoproj/argo-cd/issues/25921 is fixed
129+
Group: "operators.coreos.com",
130+
Kind: "Subscription",
131+
Check: `local health_status = {}
132+
if obj.status ~= nil then
133+
if obj.status.conditions ~= nil then
134+
local numDegraded = 0
135+
local numPending = 0
136+
local msg = ""
137+
138+
-- Check if this is a manual approval scenario where InstallPlanPending is expected
139+
-- and the operator is already installed (upgrade pending, not initial install)
140+
local isManualApprovalPending = false
141+
if obj.spec ~= nil and obj.spec.installPlanApproval == "Manual" then
142+
for _, condition in pairs(obj.status.conditions) do
143+
if condition.type == "InstallPlanPending" and condition.status == "True" and condition.reason == "RequiresApproval" then
144+
-- Only treat as expected healthy state if the operator is already installed
145+
-- (installedCSV is present), meaning this is an upgrade pending approval
146+
if obj.status.installedCSV ~= nil then
147+
isManualApprovalPending = true
148+
end
149+
break
150+
end
151+
end
152+
end
153+
154+
for i, condition in pairs(obj.status.conditions) do
155+
-- Skip InstallPlanPending condition when manual approval is pending (expected behavior)
156+
if isManualApprovalPending and condition.type == "InstallPlanPending" then
157+
-- Do not include in message or count as pending
158+
else
159+
msg = msg .. i .. ": " .. condition.type .. " | " .. condition.status .. "\n"
160+
if condition.type == "InstallPlanPending" and condition.status == "True" then
161+
numPending = numPending + 1
162+
elseif (condition.type == "InstallPlanMissing" and condition.reason ~= "ReferencedInstallPlanNotFound") then
163+
numDegraded = numDegraded + 1
164+
elseif (condition.type == "CatalogSourcesUnhealthy" or condition.type == "InstallPlanFailed" or condition.type == "ResolutionFailed") and condition.status == "True" then
165+
numDegraded = numDegraded + 1
166+
end
167+
end
168+
end
169+
170+
-- Available states: undef/nil, UpgradeAvailable, UpgradePending, UpgradeFailed, AtLatestKnown
171+
-- Source: https://github.com/openshift/operator-framework-olm/blob/5e2c73b7663d0122c9dc3e59ea39e515a31e2719/staging/api/pkg/operators/v1alpha1/subscription_types.go#L17-L23
172+
if obj.status.state == nil then
173+
numPending = numPending + 1
174+
msg = msg .. ".status.state not yet known\n"
175+
elseif obj.status.state == "" or obj.status.state == "UpgradeAvailable" then
176+
numPending = numPending + 1
177+
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
178+
elseif obj.status.state == "UpgradePending" then
179+
-- UpgradePending with manual approval is expected behavior, treat as healthy
180+
if isManualApprovalPending then
181+
msg = msg .. ".status.state is 'AtLatestKnown'\n"
182+
else
183+
numPending = numPending + 1
184+
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
185+
end
186+
elseif obj.status.state == "UpgradeFailed" then
187+
numDegraded = numDegraded + 1
188+
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
189+
else
190+
-- Last possiblity of .status.state: AtLatestKnown
191+
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
192+
end
193+
194+
if numDegraded == 0 and numPending == 0 then
195+
health_status.status = "Healthy"
196+
health_status.message = msg
197+
return health_status
198+
elseif numPending > 0 and numDegraded == 0 then
199+
health_status.status = "Progressing"
200+
health_status.message = msg
201+
return health_status
202+
else
203+
health_status.status = "Degraded"
204+
health_status.message = msg
205+
return health_status
206+
end
207+
end
208+
end
209+
health_status.status = "Progressing"
210+
health_status.message = "An install plan for a subscription is pending installation"
211+
return health_status`,
212+
},
213+
}
214+
if strings.EqualFold(PatternsOperatorConfig.getValueWithDefault("gitops.applicationHealthCheckEnabled"), "true") {
215+
// As of ArgoCD 1.8 the Application health check was dropped (see https://github.com/argoproj/argo-cd/issues/3781),
216+
// but in app-of-apps pattern this is needed in order to implement children apps dependencies via sync-waves
217+
resourceHealthChecks = append(resourceHealthChecks, argooperator.ResourceHealthCheck{
218+
Group: "argoproj.io",
219+
Kind: "Application",
220+
Check: `local health_status = {}
221+
health_status.status = "Progressing"
222+
health_status.message = ""
223+
if obj.status ~= nil then
224+
if obj.status.health ~= nil then
225+
health_status.status = obj.status.health.status
226+
if obj.status.health.message ~= nil then
227+
health_status.message = obj.status.health.message
228+
end
229+
end
230+
end
231+
return health_status`,
232+
})
233+
}
127234

128235
s := argooperator.ArgoCD{
129236
TypeMeta: metav1.TypeMeta{
@@ -254,94 +361,7 @@ g, admin, role:admin`
254361
kinds:
255362
- TaskRun
256363
- PipelineRun`,
257-
// We can drop this custom Subscription healthcheck once https://www.github.com/argoproj/argo-cd/issues/25921 is fixed
258-
ResourceHealthChecks: []argooperator.ResourceHealthCheck{
259-
{
260-
Group: "operators.coreos.com",
261-
Kind: "Subscription",
262-
Check: `local health_status = {}
263-
if obj.status ~= nil then
264-
if obj.status.conditions ~= nil then
265-
local numDegraded = 0
266-
local numPending = 0
267-
local msg = ""
268-
269-
-- Check if this is a manual approval scenario where InstallPlanPending is expected
270-
-- and the operator is already installed (upgrade pending, not initial install)
271-
local isManualApprovalPending = false
272-
if obj.spec ~= nil and obj.spec.installPlanApproval == "Manual" then
273-
for _, condition in pairs(obj.status.conditions) do
274-
if condition.type == "InstallPlanPending" and condition.status == "True" and condition.reason == "RequiresApproval" then
275-
-- Only treat as expected healthy state if the operator is already installed
276-
-- (installedCSV is present), meaning this is an upgrade pending approval
277-
if obj.status.installedCSV ~= nil then
278-
isManualApprovalPending = true
279-
end
280-
break
281-
end
282-
end
283-
end
284-
285-
for i, condition in pairs(obj.status.conditions) do
286-
-- Skip InstallPlanPending condition when manual approval is pending (expected behavior)
287-
if isManualApprovalPending and condition.type == "InstallPlanPending" then
288-
-- Do not include in message or count as pending
289-
else
290-
msg = msg .. i .. ": " .. condition.type .. " | " .. condition.status .. "\n"
291-
if condition.type == "InstallPlanPending" and condition.status == "True" then
292-
numPending = numPending + 1
293-
elseif (condition.type == "InstallPlanMissing" and condition.reason ~= "ReferencedInstallPlanNotFound") then
294-
numDegraded = numDegraded + 1
295-
elseif (condition.type == "CatalogSourcesUnhealthy" or condition.type == "InstallPlanFailed" or condition.type == "ResolutionFailed") and condition.status == "True" then
296-
numDegraded = numDegraded + 1
297-
end
298-
end
299-
end
300-
301-
-- Available states: undef/nil, UpgradeAvailable, UpgradePending, UpgradeFailed, AtLatestKnown
302-
-- Source: https://github.com/openshift/operator-framework-olm/blob/5e2c73b7663d0122c9dc3e59ea39e515a31e2719/staging/api/pkg/operators/v1alpha1/subscription_types.go#L17-L23
303-
if obj.status.state == nil then
304-
numPending = numPending + 1
305-
msg = msg .. ".status.state not yet known\n"
306-
elseif obj.status.state == "" or obj.status.state == "UpgradeAvailable" then
307-
numPending = numPending + 1
308-
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
309-
elseif obj.status.state == "UpgradePending" then
310-
-- UpgradePending with manual approval is expected behavior, treat as healthy
311-
if isManualApprovalPending then
312-
msg = msg .. ".status.state is 'AtLatestKnown'\n"
313-
else
314-
numPending = numPending + 1
315-
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
316-
end
317-
elseif obj.status.state == "UpgradeFailed" then
318-
numDegraded = numDegraded + 1
319-
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
320-
else
321-
-- Last possiblity of .status.state: AtLatestKnown
322-
msg = msg .. ".status.state is '" .. obj.status.state .. "'\n"
323-
end
324-
325-
if numDegraded == 0 and numPending == 0 then
326-
health_status.status = "Healthy"
327-
health_status.message = msg
328-
return health_status
329-
elseif numPending > 0 and numDegraded == 0 then
330-
health_status.status = "Progressing"
331-
health_status.message = msg
332-
return health_status
333-
else
334-
health_status.status = "Degraded"
335-
health_status.message = msg
336-
return health_status
337-
end
338-
end
339-
end
340-
health_status.status = "Progressing"
341-
health_status.message = "An install plan for a subscription is pending installation"
342-
return health_status`,
343-
},
344-
},
364+
ResourceHealthChecks: resourceHealthChecks,
345365
ResourceTrackingMethod: "annotation",
346366
Server: argooperator.ArgoCDServerSpec{
347367
Autoscale: argooperator.ArgoCDServerAutoscaleSpec{

internal/controller/defaults.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,17 @@ const (
8787
// Currently none
8888

8989
var DefaultPatternOperatorConfig = map[string]string{
90-
"gitops.catalogSource": GitOpsDefaultCatalogSource,
91-
"gitops.channel": GitOpsDefaultChannel,
92-
"gitops.sourceNamespace": GitOpsDefaultCatalogSourceNamespace,
93-
"gitops.installApprovalPlan": GitOpsDefaultApprovalPlan,
94-
"gitops.csv": GitOpsDefaultCSV,
95-
"gitea.chartName": GiteaChartName,
96-
"gitea.helmRepoUrl": GiteaHelmRepoUrl,
97-
"gitea.chartVersion": GiteaDefaultChartVersion,
98-
"analytics.enabled": "true",
99-
"catalog.image": "",
90+
"gitops.catalogSource": GitOpsDefaultCatalogSource,
91+
"gitops.channel": GitOpsDefaultChannel,
92+
"gitops.sourceNamespace": GitOpsDefaultCatalogSourceNamespace,
93+
"gitops.installApprovalPlan": GitOpsDefaultApprovalPlan,
94+
"gitops.csv": GitOpsDefaultCSV,
95+
"gitops.applicationHealthCheckEnabled": "false",
96+
"gitea.chartName": GiteaChartName,
97+
"gitea.helmRepoUrl": GiteaHelmRepoUrl,
98+
"gitea.chartVersion": GiteaDefaultChartVersion,
99+
"analytics.enabled": "true",
100+
"catalog.image": "",
100101
}
101102

102103
type GitOpsConfig map[string]string

internal/controller/defaults_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ var _ = Describe("GitOpsConfig getValueWithDefault", func() {
3636
Expect(config.getValueWithDefault("gitops.installApprovalPlan")).To(Equal(GitOpsDefaultApprovalPlan))
3737
})
3838

39+
It("should return the default value for gitops.applicationHealthCheckEnabled", func() {
40+
config := GitOpsConfig{}
41+
Expect(config.getValueWithDefault("gitops.applicationHealthCheckEnabled")).To(Equal("true"))
42+
})
43+
3944
It("should return the default value for gitea.chartName", func() {
4045
config := GitOpsConfig{}
4146
Expect(config.getValueWithDefault("gitea.chartName")).To(Equal(GiteaChartName))
@@ -88,6 +93,7 @@ var _ = Describe("DefaultPatternOperatorConfig", func() {
8893
"gitops.channel",
8994
"gitops.sourceNamespace",
9095
"gitops.installApprovalPlan",
96+
"gitops.applicationHealthCheckEnabled",
9197
"gitea.chartName",
9298
"gitea.helmRepoUrl",
9399
"gitea.chartVersion",

0 commit comments

Comments
 (0)