feat(health): add health checks for GitOps Promoter (#23663)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
This commit is contained in:
Michael Crenshaw 2025-07-22 08:04:57 -07:00 committed by GitHub
parent d5383de5c5
commit 36f1a59c09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 1138 additions and 1 deletions

View file

@ -0,0 +1,50 @@
local hs = {}
hs.status = "Progressing"
hs.message = "Initializing Argo CD commit status"
-- Check for deletion timestamp
if obj.metadata.deletionTimestamp then
hs.status = "Progressing"
hs.message = "Argo CD commit status is being deleted"
return hs
end
-- Check if status exists
if not obj.status then
return hs
end
-- Check for reconciliation conditions
local hasReadyCondition = false
if obj.status.conditions then
for _, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" then
hasReadyCondition = true
if condition.observedGeneration and obj.metadata.generation and condition.observedGeneration ~= obj.metadata.generation then
hs.status = "Progressing"
hs.message = "Waiting for Argo CD commit status spec update to be observed"
return hs
end
if condition.status == "False" and condition.reason == "ReconciliationError" then
hs.status = "Degraded"
hs.message = "Argo CD commit status reconciliation failed: " .. (condition.message or "Unknown error")
return hs
end
end
end
end
if not hasReadyCondition then
hs.status = "Progressing"
hs.message = "Argo CD commit status is not ready yet"
return hs
end
if not obj.status.applicationsSelected or #obj.status.applicationsSelected == 0 then
hs.status = "Degraded"
hs.message = "Argo CD commit status has no applications configured"
return hs
end
hs.status = "Healthy"
hs.message = "Argo CD commit status is healthy and is tracking " .. #obj.status.applicationsSelected .. " applications"
return hs

View file

@ -0,0 +1,29 @@
tests:
- healthStatus:
status: Progressing
message: Initializing Argo CD commit status
inputPath: testdata/no-status.yaml
- healthStatus:
status: Progressing
message: Argo CD commit status is being deleted
inputPath: testdata/deleting.yaml
- healthStatus:
status: Progressing
message: Waiting for Argo CD commit status spec update to be observed
inputPath: testdata/observed-generation-outdated.yaml
- healthStatus:
status: Degraded
message: "Argo CD commit status reconciliation failed: Something went wrong"
inputPath: testdata/reconcile-error.yaml
- healthStatus:
status: Progressing
message: Argo CD commit status is not ready yet
inputPath: testdata/no-ready-condition.yaml
- healthStatus:
status: Degraded
message: Argo CD commit status has no applications configured
inputPath: testdata/no-applications.yaml
- healthStatus:
status: Healthy
message: Argo CD commit status is healthy and is tracking 2 applications
inputPath: testdata/all-healthy.yaml

View file

@ -0,0 +1,20 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
applicationsSelected:
- name: app1
namespace: default
phase: success
sha: abc1234
- name: app2
namespace: default
phase: success
sha: def5678

View file

@ -0,0 +1,5 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test
deletionTimestamp: "2025-07-04T12:00:00Z"

View file

@ -0,0 +1,12 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
applicationsSelected: []

View file

@ -0,0 +1,11 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: SomeOtherCondition
status: True
observedGeneration: 2

View file

@ -0,0 +1,4 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test

View file

@ -0,0 +1,11 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 1

View file

@ -0,0 +1,12 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ArgoCDCommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: "False"
reason: ReconciliationError
message: Something went wrong
observedGeneration: 2

View file

@ -0,0 +1,121 @@
local hs = {}
hs.status = "Progressing"
hs.message = "Initializing change transfer policy"
-- Check for deletion timestamp
if obj.metadata.deletionTimestamp then
hs.status = "Progressing"
hs.message = "Change transfer policy is being deleted"
return hs
end
-- Check if status exists
if not obj.status then
return hs
end
-- Check for reconciliation conditions
local hasReadyCondition = false
if obj.status.conditions then
for _, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" then
hasReadyCondition = true
-- Check observedGeneration vs metadata.generation within the reconciliation condition
if condition.observedGeneration and obj.metadata.generation and condition.observedGeneration ~= obj.metadata.generation then
hs.status = "Progressing"
hs.message = "Waiting for change transfer policy spec update to be observed"
return hs
end
if condition.status == "False" and condition.reason == "ReconciliationError" then
hs.status = "Degraded"
hs.message = "Change transfer policy reconciliation failed: " .. (condition.message or "Unknown error")
return hs
end
end
end
end
if not hasReadyCondition then
hs.status = "Progressing"
hs.message = "Change transfer policy is not ready yet"
return hs
end
if not obj.status.active or not obj.status.active.dry or not obj.status.active.dry.sha or obj.status.active.dry.sha == "" then
hs.status = "Progressing"
hs.message = "The active commit DRY SHA is missing or empty."
return hs
end
if not obj.status.proposed or not obj.status.proposed.dry or not obj.status.proposed.dry.sha or obj.status.proposed.dry.sha == "" then
hs.status = "Progressing"
hs.message = "The proposed commit DRY SHA is missing or empty."
return hs
end
-- Helper function to get short SHA
local function getShortSha(sha)
if not sha or sha == "" then
return ""
end
if string.len(sha) > 7 then
return string.sub(sha, 1, 7)
end
return sha
end
if obj.status.proposed.dry.sha ~= obj.status.active.dry.sha then
local pendingCount = 0
local successCount = 0
local failureCount = 0
local pendingKeys = {}
local failedKeys = {}
for _, status in ipairs(obj.status.proposed.commitStatuses or {}) do
if status.phase == "pending" then
pendingCount = pendingCount + 1
table.insert(pendingKeys, status.key)
elseif status.phase == "success" then
successCount = successCount + 1
elseif status.phase == "failure" then
failureCount = failureCount + 1
table.insert(failedKeys, status.key)
end
end
hs.status = "Progressing"
hs.message =
"Promotion in progress from '" .. getShortSha(obj.status.active.dry.sha) ..
"' to '" .. getShortSha(obj.status.proposed.dry.sha) .. "': " ..
pendingCount .. " pending, " .. successCount .. " successful, " .. failureCount .. " failed. "
if pendingCount > 0 then
hs.message = hs.message .. "Pending commit statuses: " .. table.concat(pendingKeys, ", ") .. ". "
end
if failureCount > 0 then
hs.message = hs.message .. "Failed commit statuses: " .. table.concat(failedKeys, ", ") .. ". "
end
return hs
end
local pendingCount = 0
local failureCount = 0
for _, status in ipairs(obj.status.active.commitStatuses or {}) do
if status.phase == "pending" then
pendingCount = pendingCount + 1
elseif status.phase == "failure" then
failureCount = failureCount + 1
end
end
if pendingCount > 0 or failureCount > 0 then
local envMessages = {}
for branch, counts in pairs(nonSuccessfulEnvironments) do
local msg = branch .. " (" .. counts.failure .. " failed, " .. counts.pending .. " pending)"
table.insert(envMessages, msg)
end
hs.status = "Healthy"
hs.message = "Environment is up-to-date, but there are non-successful commit statuses: " .. table.concat(envMessages, ", ") .. "."
return hs
end
hs.status = "Healthy"
hs.message = "Environment is up-to-date on commit " .. getShortSha(obj.status.active.dry.sha) .. "."
return hs

View file

@ -0,0 +1,37 @@
tests:
- healthStatus:
status: Progressing
message: Initializing change transfer policy
inputPath: testdata/no-status.yaml
- healthStatus:
status: Progressing
message: Change transfer policy is being deleted
inputPath: testdata/deleting.yaml
- healthStatus:
status: Progressing
message: Waiting for change transfer policy spec update to be observed
inputPath: testdata/observed-generation-outdated.yaml
- healthStatus:
status: Degraded
message: "Change transfer policy reconciliation failed: Something went wrong"
inputPath: testdata/reconcile-error.yaml
- healthStatus:
status: Progressing
message: Change transfer policy is not ready yet
inputPath: testdata/no-ready-condition.yaml
- healthStatus:
status: Progressing
message: The active commit DRY SHA is missing or empty.
inputPath: testdata/active-dry-sha-missing.yaml
- healthStatus:
status: Progressing
message: The proposed commit DRY SHA is missing or empty.
inputPath: testdata/proposed-dry-sha-missing.yaml
- healthStatus:
status: Progressing
message: "Promotion in progress from 'abc1234' to 'def5678': 1 pending, 0 successful, 1 failed. Pending commit statuses: test-pending. Failed commit statuses: test-failed. "
inputPath: testdata/progressing-failed-pending.yaml
- healthStatus:
status: Healthy
message: "Environment is up-to-date on commit abc1234."
inputPath: testdata/all-healthy.yaml

View file

@ -0,0 +1,18 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
active:
dry: {}
commitStatuses: []
proposed:
dry:
sha: abc1234
commitStatuses: []

View file

@ -0,0 +1,19 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry:
sha: abc1234
commitStatuses: []

View file

@ -0,0 +1,5 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
deletionTimestamp: "2025-07-04T12:00:00Z"

View file

@ -0,0 +1,10 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
status:
conditions:
- type: SomeOtherCondition
status: True
observedGeneration: 2

View file

@ -0,0 +1,4 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
spec: {}
status:
branch: main
conditions:
- type: Ready
status: True
observedGeneration: 1

View file

@ -0,0 +1,22 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry:
sha: def5678
commitStatuses:
- key: test-pending
phase: pending
- key: test-failed
phase: failure

View file

@ -0,0 +1,18 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry: {}
commitStatuses: []

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: ChangeTransferPolicy
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: "False"
reason: ReconciliationError
message: Something went wrong
observedGeneration: 2

View file

@ -0,0 +1,50 @@
local hs = {}
hs.status = "Progressing"
hs.message = "Initializing commit status"
-- Check for deletion timestamp
if obj.metadata.deletionTimestamp then
hs.status = "Progressing"
hs.message = "Commit status is being deleted"
return hs
end
-- Check if status exists
if not obj.status then
return hs
end
-- Check for reconciliation conditions
local hasReadyCondition = false
if obj.status.conditions then
for _, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" then
hasReadyCondition = true
if condition.observedGeneration and obj.metadata.generation and condition.observedGeneration ~= obj.metadata.generation then
hs.status = "Progressing"
hs.message = "Waiting for commit status spec update to be observed"
return hs
end
if condition.status == "False" and condition.reason == "ReconciliationError" then
hs.status = "Degraded"
hs.message = "Commit status reconciliation failed: " .. (condition.message or "Unknown error")
return hs
end
end
end
end
if not hasReadyCondition then
hs.status = "Progressing"
hs.message = "Commit status is not ready yet"
return hs
end
if not obj.status.sha or not obj.status.phase then
hs.status = "Healthy"
hs.message = "Commit status is healthy"
return hs
end
hs.status = "Healthy"
hs.message = "Commit status for commit " .. obj.status.sha .. " reports " .. obj.status.phase
return hs

View file

@ -0,0 +1,33 @@
tests:
- healthStatus:
status: Progressing
message: Initializing commit status
inputPath: testdata/no-status.yaml
- healthStatus:
status: Progressing
message: Commit status is being deleted
inputPath: testdata/deleting.yaml
- healthStatus:
status: Progressing
message: Waiting for commit status spec update to be observed
inputPath: testdata/observed-generation-outdated.yaml
- healthStatus:
status: Degraded
message: "Commit status reconciliation failed: Something went wrong"
inputPath: testdata/reconcile-error.yaml
- healthStatus:
status: Progressing
message: Commit status is not ready yet
inputPath: testdata/no-ready-condition.yaml
- healthStatus:
status: Healthy
message: "Commit status for commit abc1234 reports success"
inputPath: testdata/success.yaml
- healthStatus:
status: Healthy
message: "Commit status for commit abc1234 reports pending"
inputPath: testdata/pending.yaml
- healthStatus:
status: Healthy
message: "Commit status for commit abc1234 reports failure"
inputPath: testdata/failure.yaml

View file

@ -0,0 +1,5 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
deletionTimestamp: "2025-07-04T12:00:00Z"

View file

@ -0,0 +1,14 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
id: test-2
sha: abc1234
phase: failure

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: SomeOtherCondition
status: True
observedGeneration: 2
id: test-2
sha: abc1234

View file

@ -0,0 +1,4 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test

View file

@ -0,0 +1,12 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 1
id: test-2
sha: abc1234

View file

@ -0,0 +1,14 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
id: test-2
sha: abc1234
phase: pending

View file

@ -0,0 +1,14 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: "False"
reason: ReconciliationError
message: Something went wrong
observedGeneration: 2
id: test-2
sha: abc1234

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: CommitStatus
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
id: test-2
sha: abc1234
phase: success

View file

@ -0,0 +1,168 @@
local hs = {}
hs.status = "Progressing"
hs.message = "Initializing promotion strategy"
-- Check for deletion timestamp
if obj.metadata.deletionTimestamp then
hs.status = "Progressing"
hs.message = "Promotion strategy is being deleted"
return hs
end
-- Check if status exists
if not obj.status then
return hs
end
-- Check for reconciliation conditions
local hasReadyCondition = false
if obj.status.conditions then
for _, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" then
hasReadyCondition = true
-- Check observedGeneration vs metadata.generation within the reconciliation condition
if condition.observedGeneration and obj.metadata.generation and condition.observedGeneration ~= obj.metadata.generation then
hs.status = "Progressing"
hs.message = "Waiting for promotion strategy spec update to be observed"
return hs
end
if condition.status == "False" and condition.reason == "ReconciliationError" then
hs.status = "Degraded"
hs.message = "Promotion strategy reconciliation failed: " .. (condition.message or "Unknown error")
return hs
end
end
end
end
-- If no Ready condition is found, return Progressing status
if not hasReadyCondition then
hs.status = "Progressing"
hs.message = "Promotion strategy is not ready yet"
return hs
end
if not obj.status.environments or #obj.status.environments == 0 then
hs.status = "Degraded"
hs.message = "Promotion strategy has no environments configured"
return hs
end
-- Make sure there's a fully-populated status for both active and proposed commits in all environments. If anything is
-- missing or empty, return a Progressing status.
for _, env in ipairs(obj.status.environments) do
if not env.active or not env.active.dry or not env.active.dry.sha or env.active.dry.sha == "" then
hs.status = "Progressing"
hs.message = "The active commit DRY SHA is missing or empty in environment '" .. env.branch .. "'."
return hs
end
if not env.proposed or not env.proposed.dry or not env.proposed.dry.sha or env.proposed.dry.sha == "" then
hs.status = "Progressing"
hs.message = "The proposed commit DRY SHA is missing or empty in environment '" .. env.branch .. "'."
return hs
end
end
-- Check if all the proposed environments have the same proposed commit dry sha. If not, return a Progressing status.
local proposedSha = obj.status.environments[1].proposed.dry.sha -- Don't panic, Lua is 1-indexed.
for _, env in ipairs(obj.status.environments) do
if env.proposed.dry.sha ~= proposedSha then
hs.status = "Progressing"
hs.status = "Not all environments have the same proposed commit SHA. This likely means the hydrator has not run for all environments yet."
return hs
end
end
-- Helper function to get short SHA
local function getShortSha(sha)
if not sha or sha == "" then
return ""
end
if string.len(sha) > 7 then
return string.sub(sha, 1, 7)
end
return sha
end
-- Find the first environment with a proposed commit dry sha that doesn't match the active one. Loop over its commit
-- statuses and build a summary about how many are pending, successful, or failed. Return a Progressing status for this
-- in-progress environment.
for _, env in ipairs(obj.status.environments) do
if env.proposed.dry.sha ~= env.active.dry.sha then
local pendingCount = 0
local successCount = 0
local failureCount = 0
local pendingKeys = {}
local failedKeys = {}
-- pending, success, and failure are the only possible values
-- https://github.com/argoproj-labs/gitops-promoter/blob/c58d55ef52f86ff84e4f8fa35d2edba520886e3b/api/v1alpha1/commitstatus_types.go#L44
for _, status in ipairs(env.proposed.commitStatuses or {}) do
if status.phase == "pending" then
pendingCount = pendingCount + 1
table.insert(pendingKeys, status.key)
elseif status.phase == "success" then
successCount = successCount + 1
elseif status.phase == "failure" then
failureCount = failureCount + 1
table.insert(failedKeys, status.key)
end
end
hs.status = "Progressing"
hs.message =
"Promotion in progress for environment '" .. env.branch ..
"' from '" .. getShortSha(env.active.dry.sha) ..
"' to '" .. getShortSha(env.proposed.dry.sha) .. "': " ..
pendingCount .. " pending, " .. successCount .. " successful, " .. failureCount .. " failed. "
if pendingCount > 0 then
hs.message = hs.message .. "Pending commit statuses: " .. table.concat(pendingKeys, ", ") .. ". "
end
if failureCount > 0 then
hs.message = hs.message .. "Failed commit statuses: " .. table.concat(failedKeys, ", ") .. ". "
end
return hs
end
end
-- Check all environments for active commit statuses that aren't successful. For each environment with a non-successful
-- commit status, get a count of how many aren't successful. Write a summary of non-successful environments.
local nonSuccessfulEnvironments = {}
for _, env in ipairs(obj.status.environments) do
local pendingCount = 0
local failureCount = 0
for _, status in ipairs(env.active.commitStatuses or {}) do
if status.phase == "pending" then
pendingCount = pendingCount + 1
elseif status.phase == "failure" then
failureCount = failureCount + 1
end
end
if pendingCount > 0 or failureCount > 0 then
nonSuccessfulEnvironments[env.branch] = {
pending = pendingCount,
failure = failureCount,
}
end
end
if next(nonSuccessfulEnvironments) then
local envMessages = {}
for branch, counts in pairs(nonSuccessfulEnvironments) do
local msg = branch .. " (" .. counts.failure .. " failed, " .. counts.pending .. " pending)"
table.insert(envMessages, msg)
end
hs.status = "Healthy"
hs.message = "Environments are up-to-date, but some environments have non-successful commit statuses: " .. table.concat(envMessages, ", ") .. "."
return hs
end
-- If all environments have the same proposed commit dry sha as the active one, we can consider the promotion strategy
-- healthy. This means all environments are in sync and no further action is needed.
hs.status = "Healthy"
hs.message = "All environments are up-to-date on commit '" .. getShortSha(obj.status.environments[1].proposed.dry.sha) .. "'."
return hs

View file

@ -0,0 +1,41 @@
tests:
- healthStatus:
status: Progressing
message: Initializing promotion strategy
inputPath: testdata/no-status.yaml
- healthStatus:
status: Progressing
message: Promotion strategy is being deleted
inputPath: testdata/deleting.yaml
- healthStatus:
status: Progressing
message: Promotion strategy is not ready yet
inputPath: testdata/no-ready-condition.yaml
- healthStatus:
status: Progressing
message: Waiting for promotion strategy spec update to be observed
inputPath: testdata/observed-generation-outdated.yaml
- healthStatus:
status: Degraded
message: "Promotion strategy reconciliation failed: Something went wrong"
inputPath: testdata/reconciliation-error.yaml
- healthStatus:
status: Degraded
message: Promotion strategy has no environments configured
inputPath: testdata/no-environments.yaml
- healthStatus:
status: Progressing
message: The active commit DRY SHA is missing or empty in environment 'dev'.
inputPath: testdata/active-dry-sha-missing.yaml
- healthStatus:
status: Progressing
message: "The proposed commit DRY SHA is missing or empty in environment 'dev'."
inputPath: testdata/proposed-dry-sha-missing.yaml
- healthStatus:
status: Progressing
message: "Promotion in progress for environment 'dev' from 'abc1234' to 'def5678': 1 pending, 0 successful, 1 failed. Pending commit statuses: test-pending. Failed commit statuses: test-failed. "
inputPath: testdata/progressing-failed-pending.yaml
- healthStatus:
status: Healthy
message: All environments are up-to-date on commit 'abc1234'.
inputPath: testdata/all-healthy.yaml

View file

@ -0,0 +1,20 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
environments:
- branch: dev
active:
dry: {}
commitStatuses: []
proposed:
dry:
sha: abc1234
commitStatuses: []

View file

@ -0,0 +1,31 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
environments:
- branch: dev
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry:
sha: abc1234
commitStatuses: []
- branch: prod
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry:
sha: abc1234
commitStatuses: []

View file

@ -0,0 +1,22 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
deletionTimestamp: "2025-07-04T12:00:00Z"
spec: {}
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
environments:
- branch: dev
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry:
sha: abc1234
commitStatuses: []

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
environments: []

View file

@ -0,0 +1,7 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status: {}

View file

@ -0,0 +1,7 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}

View file

@ -0,0 +1,12 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: Unknown
observedGeneration: 1

View file

@ -0,0 +1,24 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
environments:
- branch: dev
active:
dry:
sha: abc1234
proposed:
dry:
sha: def5678
commitStatuses:
- key: test-failed
phase: failure
- key: test-pending
phase: pending

View file

@ -0,0 +1,20 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
environments:
- branch: dev
active:
dry:
sha: abc1234
commitStatuses: []
proposed:
dry: {}
commitStatuses: []

View file

@ -0,0 +1,14 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PromotionStrategy
metadata:
name: test
generation: 2
spec: {}
status:
conditions:
- type: Ready
status: "False"
reason: ReconciliationError
message: Something went wrong
observedGeneration: 2

View file

@ -0,0 +1,52 @@
local hs = {}
hs.status = "Progressing"
hs.message = "Initializing pull request"
-- Check for deletion timestamp
if obj.metadata.deletionTimestamp then
hs.status = "Progressing"
hs.message = "Pull request is being deleted"
return hs
end
-- Check if status exists
if not obj.status then
return hs
end
-- Check for reconciliation conditions
local hasReadyCondition = false
if obj.status.conditions then
for _, condition in ipairs(obj.status.conditions) do
if condition.type == "Reconciled" or condition.type == "Ready" then
hasReadyCondition = true
-- Check observedGeneration vs metadata.generation within the reconciliation condition
if condition.observedGeneration and obj.metadata.generation and condition.observedGeneration ~= obj.metadata.generation then
hs.status = "Progressing"
hs.message = "Waiting for pull request spec update to be observed"
return hs
end
if condition.status == "False" and (condition.reason == "ReconcileError" or condition.reason == "Failed") then
hs.status = "Degraded"
hs.message = "Pull request reconciliation failed: " .. (condition.message or "Unknown error")
return hs
end
end
end
end
if not hasReadyCondition then
hs.status = "Progressing"
hs.message = "Pull request is not ready yet"
return hs
end
-- This shouldn't happen, but if the condition says reconciliation succeeded, just trust it.
if not obj.status.id or not obj.status.state then
hs.status = "Healthy"
hs.message = "Pull request is healthy"
return hs
end
hs.status = "Healthy"
hs.message = "Pull request is " .. obj.status.state .. " as PR " .. obj.status.id
return hs

View file

@ -0,0 +1,29 @@
tests:
- healthStatus:
status: Progressing
message: Initializing pull request
inputPath: testdata/no-status.yaml
- healthStatus:
status: Progressing
message: Pull request is being deleted
inputPath: testdata/deleting.yaml
- healthStatus:
status: Progressing
message: Waiting for pull request spec update to be observed
inputPath: testdata/observed-generation-outdated.yaml
- healthStatus:
status: Degraded
message: "Pull request reconciliation failed: Something went wrong"
inputPath: testdata/reconcile-error.yaml
- healthStatus:
status: Progressing
message: Pull request is not ready yet
inputPath: testdata/no-ready-condition.yaml
- healthStatus:
status: Healthy
message: Pull request is healthy
inputPath: testdata/healthy-no-id-or-state.yaml
- healthStatus:
status: Healthy
message: Pull request is open as PR 123
inputPath: testdata/healthy-with-id-and-state.yaml

View file

@ -0,0 +1,7 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test
deletionTimestamp: "2025-07-04T12:00:00Z"
status: {}

View file

@ -0,0 +1,11 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 2
id: "123"
state: open

View file

@ -0,0 +1,11 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test
generation: 2
status:
conditions:
- type: SomeOtherCondition
status: True
observedGeneration: 2

View file

@ -0,0 +1,5 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test

View file

@ -0,0 +1,11 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: True
observedGeneration: 1

View file

@ -0,0 +1,13 @@
apiVersion: promoter.argoproj.io/v1alpha1
kind: PullRequest
metadata:
name: test
generation: 2
status:
conditions:
- type: Ready
status: "False"
reason: ReconcileError
message: Something went wrong
observedGeneration: 2

View file

@ -47,7 +47,7 @@ func TestLuaHealthScript(t *testing.T) {
require.NoError(t, err)
for i := range resourceTest.Tests {
test := resourceTest.Tests[i]
t.Run(test.InputPath, func(t *testing.T) {
t.Run(filepath.Join(strings.TrimPrefix(dir, "../../resource_customizations/"), test.InputPath), func(t *testing.T) {
vm := VM{
UseOpenLibs: true,
}