mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
191 lines
7.8 KiB
Lua
191 lines
7.8 KiB
Lua
-- CRD spec: https://gitops-promoter.readthedocs.io/en/latest/crd-specs/#promotionstrategy
|
|
|
|
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
|
|
-- Check for any False condition status
|
|
if condition.status == "False" then
|
|
hs.status = "Degraded"
|
|
local msg = condition.message or "Unknown error"
|
|
local reason = condition.reason or "Unknown"
|
|
-- Don't include ReconciliationError in the message since it's redundant
|
|
if reason == "ReconciliationError" then
|
|
hs.message = "Promotion strategy reconciliation failed: " .. msg
|
|
else
|
|
hs.message = "Promotion strategy reconciliation failed (" .. reason .. "): " .. msg
|
|
end
|
|
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
|
|
|
|
-- Use note.drySha as canonical proposed SHA when present; fallback to proposed.dry.sha.
|
|
local function getProposedDrySha(env)
|
|
if env and env.proposed and env.proposed.note and env.proposed.note.drySha and env.proposed.note.drySha ~= "" then
|
|
return env.proposed.note.drySha
|
|
end
|
|
if env and env.proposed and env.proposed.dry and env.proposed.dry.sha and env.proposed.dry.sha ~= "" then
|
|
return env.proposed.dry.sha
|
|
end
|
|
return nil
|
|
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 getProposedDrySha(env) 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 = getProposedDrySha(obj.status.environments[1]) -- Don't panic, Lua is 1-indexed.
|
|
for _, env in ipairs(obj.status.environments) do
|
|
if getProposedDrySha(env) ~= proposedSha then
|
|
hs.status = "Progressing"
|
|
hs.message = "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 genuinely promotable diff: proposed metadata dry sha differs from active. When only
|
|
-- the hydrator note advanced (no-op re-hydration), proposed.dry.sha still matches active.dry.sha and no promotion is
|
|
-- needed even if getProposedDrySha (note-based) differs from active.dry.sha.
|
|
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
|
|
hs.message = hs.message .. "Hydrator has processed dry commit '" .. getShortSha(getProposedDrySha(env)) .. "'. "
|
|
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].active.dry.sha) .. "'. Hydrator has processed dry commit '" .. getShortSha(getProposedDrySha(obj.status.environments[1])) .. "'."
|
|
return hs
|