mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
feat(health): additional promoter.argoproj.io health checks (#27170)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
This commit is contained in:
parent
7accd34f64
commit
daadf868db
64 changed files with 1723 additions and 0 deletions
|
|
@ -0,0 +1,57 @@
|
||||||
|
local hs = {}
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Initializing cluster SCM provider"
|
||||||
|
|
||||||
|
-- ClusterScmProvider (gitops-promoter v1alpha1): cluster-scoped SCM provider; same Ready semantics as ScmProvider.
|
||||||
|
|
||||||
|
if obj.metadata.deletionTimestamp then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "ClusterScmProvider is being deleted"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if not obj.status then
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
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 ClusterScmProvider spec update to be observed"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "False" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
local msg = condition.message or "Unknown error"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
if reason == "ReconciliationError" then
|
||||||
|
hs.message = "Cluster SCM provider validation failed: " .. msg
|
||||||
|
else
|
||||||
|
hs.message = "Cluster SCM provider not ready (" .. reason .. "): " .. msg
|
||||||
|
end
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "Unknown" then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
local msg = condition.message or "Unknown"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
hs.message = "Cluster SCM provider readiness unknown (" .. reason .. "): " .. msg
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hasReadyCondition then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "ClusterScmProvider Ready condition is missing"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Cluster SCM provider is ready"
|
||||||
|
return hs
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
tests:
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Initializing cluster SCM provider
|
||||||
|
inputPath: testdata/no-status.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: ClusterScmProvider is being deleted
|
||||||
|
inputPath: testdata/deleting.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for ClusterScmProvider spec update to be observed
|
||||||
|
inputPath: testdata/observed-generation-outdated.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Cluster SCM provider validation failed: Reconciliation failed: invalid credentials"
|
||||||
|
inputPath: testdata/reconcile-error.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: ClusterScmProvider Ready condition is missing
|
||||||
|
inputPath: testdata/no-ready-condition.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Cluster SCM provider readiness unknown (ValidationPending): Probing SCM endpoint"
|
||||||
|
inputPath: testdata/ready-unknown.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Cluster SCM provider is ready
|
||||||
|
inputPath: testdata/healthy.yaml
|
||||||
10
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/deleting.yaml
vendored
Normal file
10
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/deleting.yaml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
deletionTimestamp: "2025-07-04T12:00:00Z"
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/healthy.yaml
vendored
Normal file
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/healthy.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/no-ready-condition.yaml
vendored
Normal file
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/no-ready-condition.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Example
|
||||||
|
status: "True"
|
||||||
|
reason: Example
|
||||||
|
message: ok
|
||||||
|
observedGeneration: 1
|
||||||
9
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/no-status.yaml
vendored
Normal file
9
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/no-status.yaml
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/ready-unknown.yaml
vendored
Normal file
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/ready-unknown.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: Unknown
|
||||||
|
reason: ValidationPending
|
||||||
|
message: Probing SCM endpoint
|
||||||
|
observedGeneration: 1
|
||||||
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/reconcile-error.yaml
vendored
Normal file
17
resource_customizations/promoter.argoproj.io/ClusterScmProvider/testdata/reconcile-error.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ClusterScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-enterprise
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: ReconciliationError
|
||||||
|
message: "Reconciliation failed: invalid credentials"
|
||||||
|
observedGeneration: 1
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
local hs = {}
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Initializing git commit validation"
|
||||||
|
|
||||||
|
-- GitCommitStatus (gitops-promoter) reports per-environment validation in status.environments
|
||||||
|
-- (branch, phase, proposedHydratedSha, targetedSha, expressionResult). It is not CommitStatus:
|
||||||
|
-- there is no top-level status.sha / status.phase.
|
||||||
|
|
||||||
|
if obj.metadata.deletionTimestamp then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "GitCommitStatus is being deleted"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if not obj.status then
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
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 GitCommitStatus spec update to be observed"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "False" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
local msg = condition.message or "Unknown error"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
if reason == "ReconciliationError" then
|
||||||
|
hs.message = "Git commit validation failed: " .. msg
|
||||||
|
else
|
||||||
|
hs.message = "Git commit validation not ready (" .. reason .. "): " .. msg
|
||||||
|
end
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "Unknown" then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
local msg = condition.message or "Unknown"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
hs.message = "Git commit validation status unknown (" .. reason .. "): " .. msg
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hasReadyCondition then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "GitCommitStatus Ready condition is missing"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
local envs = obj.status.environments
|
||||||
|
if not envs or #envs == 0 then
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Git commit validation reconciled"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
local pendingBranches = {}
|
||||||
|
local failureBranches = {}
|
||||||
|
local successCount = 0
|
||||||
|
|
||||||
|
for _, env in ipairs(envs) do
|
||||||
|
local branch = env.branch or "?"
|
||||||
|
local phase = env.phase or "pending"
|
||||||
|
if phase == "failure" then
|
||||||
|
table.insert(failureBranches, branch)
|
||||||
|
elseif phase == "pending" then
|
||||||
|
table.insert(pendingBranches, branch)
|
||||||
|
elseif phase == "success" then
|
||||||
|
successCount = successCount + 1
|
||||||
|
else
|
||||||
|
table.insert(pendingBranches, branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #failureBranches > 0 then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
hs.message = "Git commit validation failed for branch(es): " .. table.concat(failureBranches, ", ")
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if #pendingBranches > 0 then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Git commit validation pending for branch(es): " .. table.concat(pendingBranches, ", ")
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Git commit validation passed for " .. tostring(successCount) .. " environment(s)"
|
||||||
|
return hs
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
tests:
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Initializing git commit validation
|
||||||
|
inputPath: testdata/no-status.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: GitCommitStatus is being deleted
|
||||||
|
inputPath: testdata/deleting.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for GitCommitStatus spec update to be observed
|
||||||
|
inputPath: testdata/observed-generation-outdated.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Git commit validation failed: Something went wrong"
|
||||||
|
inputPath: testdata/reconcile-error.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Git commit validation not ready (CommitStatusesNotReady): CommitStatus \"git-commit-check-env-dev-git-commit-check\" is not Ready because \"ReconciliationError\": Failed to sync to SCM"
|
||||||
|
inputPath: testdata/commit-statuses-not-ready.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: GitCommitStatus Ready condition is missing
|
||||||
|
inputPath: testdata/no-ready-condition.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Git commit validation status unknown (CommitStatusesNotReady): CommitStatus \"child\" Ready condition is missing"
|
||||||
|
inputPath: testdata/ready-unknown.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Git commit validation reconciled
|
||||||
|
inputPath: testdata/empty-environments.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Git commit validation passed for 2 environment(s)
|
||||||
|
inputPath: testdata/all-success.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Git commit validation pending for branch(es): env/dev"
|
||||||
|
inputPath: testdata/environment-pending.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Git commit validation failed for branch(es): env/dev"
|
||||||
|
inputPath: testdata/environment-failure.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Git commit validation failed: Reconciliation failed: failed to process environments: failed to evaluate expression for branch \"env/dev\": failed to compile expression: failed to compile expression: type string has no method startsWith (1:16)\n | Commit.Subject.startsWith(\"chore:\")\n | ...............^"
|
||||||
|
inputPath: testdata/expression-compile-error.yaml
|
||||||
32
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/all-success.yaml
vendored
Normal file
32
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/all-success.yaml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: gitcommitstatus-proposed-mode
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: promotion-strategy-sample
|
||||||
|
key: commit-format-check
|
||||||
|
description: Commit message format validation
|
||||||
|
target: proposed
|
||||||
|
expression: 'Commit.Subject matches "^(feat|fix|docs|chore)(\\(.+\\))?: .+"'
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
activeHydratedSha: "fedcba0987654321fedcba0987654321fedcba09"
|
||||||
|
phase: success
|
||||||
|
expressionResult: true
|
||||||
|
- branch: env/staging
|
||||||
|
proposedHydratedSha: "abcdef0123456789abcdef0123456789abcdef01"
|
||||||
|
targetedSha: "abcdef0123456789abcdef0123456789abcdef01"
|
||||||
|
activeHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
|
expressionResult: true
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: git-commit-check
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: git-commit-check
|
||||||
|
expression: 'Commit.Subject startsWith "chore:"'
|
||||||
|
target: proposed
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: CommitStatusesNotReady
|
||||||
|
message: 'CommitStatus "git-commit-check-env-dev-git-commit-check" is not Ready because "ReconciliationError": Failed to sync to SCM'
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
activeHydratedSha: "fedcba0987654321fedcba0987654321fedcba09"
|
||||||
|
phase: success
|
||||||
|
expressionResult: true
|
||||||
10
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/deleting.yaml
vendored
Normal file
10
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/deleting.yaml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
deletionTimestamp: "2025-07-04T12:00:00Z"
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "true"
|
||||||
18
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/empty-environments.yaml
vendored
Normal file
18
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/empty-environments.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: orphaned-key
|
||||||
|
expression: "true"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments: []
|
||||||
23
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/environment-failure.yaml
vendored
Normal file
23
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/environment-failure.yaml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "false"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: failure
|
||||||
|
expressionResult: false
|
||||||
22
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/environment-pending.yaml
vendored
Normal file
22
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/environment-pending.yaml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "true"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: pending
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: git-commit-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
description: Commit subject must start with 'chore:'
|
||||||
|
expression: Commit.Subject.startsWith("chore:")
|
||||||
|
key: git-commit-check
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
target: proposed
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: '2026-04-05T17:54:11Z'
|
||||||
|
message: >-
|
||||||
|
Reconciliation failed: failed to process environments: failed to
|
||||||
|
evaluate expression for branch "env/dev": failed to compile expression:
|
||||||
|
failed to compile expression: type string has no method startsWith (1:16)
|
||||||
|
| Commit.Subject.startsWith("chore:")
|
||||||
|
| ...............^
|
||||||
|
observedGeneration: 1
|
||||||
|
reason: ReconciliationError
|
||||||
|
status: 'False'
|
||||||
|
type: Ready
|
||||||
23
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/no-ready-condition.yaml
vendored
Normal file
23
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/no-ready-condition.yaml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "true"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: SomeOtherCondition
|
||||||
|
status: "True"
|
||||||
|
reason: Example
|
||||||
|
message: ok
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
|
expressionResult: true
|
||||||
9
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/no-status.yaml
vendored
Normal file
9
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/no-status.yaml
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: 'Commit.Subject startsWith "chore:"'
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "true"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
|
expressionResult: true
|
||||||
23
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/ready-unknown.yaml
vendored
Normal file
23
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/ready-unknown.yaml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "true"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: Unknown
|
||||||
|
reason: CommitStatusesNotReady
|
||||||
|
message: CommitStatus "child" Ready condition is missing
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
proposedHydratedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
targetedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
|
expressionResult: true
|
||||||
17
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/reconcile-error.yaml
vendored
Normal file
17
resource_customizations/promoter.argoproj.io/GitCommitStatus/testdata/reconcile-error.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo-strategy
|
||||||
|
key: my-check
|
||||||
|
expression: "true"
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: ReconciliationError
|
||||||
|
message: Something went wrong
|
||||||
|
observedGeneration: 2
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
local hs = {}
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Initializing Git repository"
|
||||||
|
|
||||||
|
-- GitRepository (gitops-promoter v1alpha1): repo reference validated via standard Ready condition.
|
||||||
|
|
||||||
|
if obj.metadata.deletionTimestamp then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "GitRepository is being deleted"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if not obj.status then
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
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 GitRepository spec update to be observed"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "False" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
local msg = condition.message or "Unknown error"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
if reason == "ReconciliationError" then
|
||||||
|
hs.message = "Git repository validation failed: " .. msg
|
||||||
|
else
|
||||||
|
hs.message = "Git repository not ready (" .. reason .. "): " .. msg
|
||||||
|
end
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "Unknown" then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
local msg = condition.message or "Unknown"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
hs.message = "Git repository readiness unknown (" .. reason .. "): " .. msg
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hasReadyCondition then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "GitRepository Ready condition is missing"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Git repository is ready"
|
||||||
|
return hs
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
tests:
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Initializing Git repository
|
||||||
|
inputPath: testdata/no-status.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: GitRepository is being deleted
|
||||||
|
inputPath: testdata/deleting.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for GitRepository spec update to be observed
|
||||||
|
inputPath: testdata/observed-generation-outdated.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Git repository validation failed: Reconciliation failed: repository not found"
|
||||||
|
inputPath: testdata/reconcile-error.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: GitRepository Ready condition is missing
|
||||||
|
inputPath: testdata/no-ready-condition.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Git repository readiness unknown (ValidationPending): Resolving repository metadata"
|
||||||
|
inputPath: testdata/ready-unknown.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Git repository is ready
|
||||||
|
inputPath: testdata/healthy.yaml
|
||||||
13
resource_customizations/promoter.argoproj.io/GitRepository/testdata/deleting.yaml
vendored
Normal file
13
resource_customizations/promoter.argoproj.io/GitRepository/testdata/deleting.yaml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
deletionTimestamp: "2025-07-04T12:00:00Z"
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/healthy.yaml
vendored
Normal file
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/healthy.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/no-ready-condition.yaml
vendored
Normal file
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/no-ready-condition.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Example
|
||||||
|
status: "True"
|
||||||
|
reason: Example
|
||||||
|
message: ok
|
||||||
|
observedGeneration: 1
|
||||||
12
resource_customizations/promoter.argoproj.io/GitRepository/testdata/no-status.yaml
vendored
Normal file
12
resource_customizations/promoter.argoproj.io/GitRepository/testdata/no-status.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/ready-unknown.yaml
vendored
Normal file
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/ready-unknown.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: Unknown
|
||||||
|
reason: ValidationPending
|
||||||
|
message: Resolving repository metadata
|
||||||
|
observedGeneration: 1
|
||||||
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/reconcile-error.yaml
vendored
Normal file
20
resource_customizations/promoter.argoproj.io/GitRepository/testdata/reconcile-error.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: app-repo
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
scmProviderRef:
|
||||||
|
kind: ScmProvider
|
||||||
|
name: github-provider
|
||||||
|
github:
|
||||||
|
owner: argoproj
|
||||||
|
name: argo-cd
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: ReconciliationError
|
||||||
|
message: "Reconciliation failed: repository not found"
|
||||||
|
observedGeneration: 1
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
local hs = {}
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Initializing SCM provider"
|
||||||
|
|
||||||
|
-- ScmProvider (gitops-promoter v1alpha1): credentials / SCM API reachability via standard Ready condition.
|
||||||
|
|
||||||
|
if obj.metadata.deletionTimestamp then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "ScmProvider is being deleted"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if not obj.status then
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
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 ScmProvider spec update to be observed"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "False" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
local msg = condition.message or "Unknown error"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
if reason == "ReconciliationError" then
|
||||||
|
hs.message = "SCM provider validation failed: " .. msg
|
||||||
|
else
|
||||||
|
hs.message = "SCM provider not ready (" .. reason .. "): " .. msg
|
||||||
|
end
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "Unknown" then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
local msg = condition.message or "Unknown"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
hs.message = "SCM provider readiness unknown (" .. reason .. "): " .. msg
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hasReadyCondition then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "ScmProvider Ready condition is missing"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "SCM provider is ready"
|
||||||
|
return hs
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
tests:
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Initializing SCM provider
|
||||||
|
inputPath: testdata/no-status.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: ScmProvider is being deleted
|
||||||
|
inputPath: testdata/deleting.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for ScmProvider spec update to be observed
|
||||||
|
inputPath: testdata/observed-generation-outdated.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "SCM provider validation failed: Reconciliation failed: secret not found"
|
||||||
|
inputPath: testdata/reconcile-error.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: ScmProvider Ready condition is missing
|
||||||
|
inputPath: testdata/no-ready-condition.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "SCM provider readiness unknown (ValidationPending): Checking GitHub API connectivity"
|
||||||
|
inputPath: testdata/ready-unknown.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: SCM provider is ready
|
||||||
|
inputPath: testdata/healthy.yaml
|
||||||
11
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/deleting.yaml
vendored
Normal file
11
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/deleting.yaml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
deletionTimestamp: "2025-07-04T12:00:00Z"
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/healthy.yaml
vendored
Normal file
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/healthy.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/no-ready-condition.yaml
vendored
Normal file
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/no-ready-condition.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Example
|
||||||
|
status: "True"
|
||||||
|
reason: Example
|
||||||
|
message: ok
|
||||||
|
observedGeneration: 1
|
||||||
10
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/no-status.yaml
vendored
Normal file
10
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/no-status.yaml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/ready-unknown.yaml
vendored
Normal file
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/ready-unknown.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: Unknown
|
||||||
|
reason: ValidationPending
|
||||||
|
message: Checking GitHub API connectivity
|
||||||
|
observedGeneration: 1
|
||||||
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/reconcile-error.yaml
vendored
Normal file
18
resource_customizations/promoter.argoproj.io/ScmProvider/testdata/reconcile-error.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: ScmProvider
|
||||||
|
metadata:
|
||||||
|
name: github-provider
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: github-app-secret
|
||||||
|
github:
|
||||||
|
appID: 1
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: ReconciliationError
|
||||||
|
message: "Reconciliation failed: secret not found"
|
||||||
|
observedGeneration: 1
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
local hs = {}
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Initializing timed commit gate"
|
||||||
|
|
||||||
|
-- TimedCommitStatus (gitops-promoter v1alpha1): per-environment wait before reporting success.
|
||||||
|
-- status.environments[].phase is pending or success (see API).
|
||||||
|
|
||||||
|
if obj.metadata.deletionTimestamp then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "TimedCommitStatus is being deleted"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if not obj.status then
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
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 TimedCommitStatus spec update to be observed"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "False" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
local msg = condition.message or "Unknown error"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
if reason == "ReconciliationError" then
|
||||||
|
hs.message = "Timed commit gate failed: " .. msg
|
||||||
|
else
|
||||||
|
hs.message = "Timed commit gate not ready (" .. reason .. "): " .. msg
|
||||||
|
end
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "Unknown" then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
local msg = condition.message or "Unknown"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
hs.message = "Timed commit gate status unknown (" .. reason .. "): " .. msg
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hasReadyCondition then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "TimedCommitStatus Ready condition is missing"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
local envs = obj.status.environments
|
||||||
|
if not envs or #envs == 0 then
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Timed commit gate reconciled"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
local pendingBranches = {}
|
||||||
|
local failureBranches = {}
|
||||||
|
local successCount = 0
|
||||||
|
|
||||||
|
for _, env in ipairs(envs) do
|
||||||
|
local branch = env.branch or "?"
|
||||||
|
local phase = env.phase or "pending"
|
||||||
|
if phase == "failure" then
|
||||||
|
table.insert(failureBranches, branch)
|
||||||
|
elseif phase == "success" then
|
||||||
|
successCount = successCount + 1
|
||||||
|
else
|
||||||
|
table.insert(pendingBranches, branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #failureBranches > 0 then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
hs.message = "Timed commit gate failed for branch(es): " .. table.concat(failureBranches, ", ")
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if #pendingBranches > 0 then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Timed commit gate pending for branch(es): " .. table.concat(pendingBranches, ", ")
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Timed commit gate satisfied for " .. tostring(successCount) .. " environment(s)"
|
||||||
|
return hs
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
tests:
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Initializing timed commit gate
|
||||||
|
inputPath: testdata/no-status.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: TimedCommitStatus is being deleted
|
||||||
|
inputPath: testdata/deleting.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for TimedCommitStatus spec update to be observed
|
||||||
|
inputPath: testdata/observed-generation-outdated.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Timed commit gate failed: Reconciliation failed: PromotionStrategy not found"
|
||||||
|
inputPath: testdata/reconcile-error.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: TimedCommitStatus Ready condition is missing
|
||||||
|
inputPath: testdata/no-ready-condition.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Timed commit gate reconciled
|
||||||
|
inputPath: testdata/empty-environments.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Timed commit gate pending for branch(es): env/dev"
|
||||||
|
inputPath: testdata/environment-pending.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Timed commit gate satisfied for 2 environment(s)
|
||||||
|
inputPath: testdata/all-success.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Timed commit gate failed for branch(es): env/dev"
|
||||||
|
inputPath: testdata/environment-failure.yaml
|
||||||
34
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/all-success.yaml
vendored
Normal file
34
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/all-success.yaml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 5m
|
||||||
|
- branch: env/staging
|
||||||
|
duration: 5m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
sha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
commitTime: "2026-03-01T12:00:00Z"
|
||||||
|
requiredDuration: 5m
|
||||||
|
phase: success
|
||||||
|
atMostDurationRemaining: 0s
|
||||||
|
- branch: env/staging
|
||||||
|
sha: "abcdef0123456789abcdef0123456789abcdef01"
|
||||||
|
commitTime: "2026-03-01T12:00:00Z"
|
||||||
|
requiredDuration: 5m
|
||||||
|
phase: success
|
||||||
|
atMostDurationRemaining: 0s
|
||||||
12
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/deleting.yaml
vendored
Normal file
12
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/deleting.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
deletionTimestamp: "2025-07-04T12:00:00Z"
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
20
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/empty-environments.yaml
vendored
Normal file
20
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/empty-environments.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments: []
|
||||||
26
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/environment-failure.yaml
vendored
Normal file
26
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/environment-failure.yaml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
sha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
commitTime: "2026-03-01T12:00:00Z"
|
||||||
|
requiredDuration: 10m
|
||||||
|
phase: failure
|
||||||
|
atMostDurationRemaining: 0s
|
||||||
26
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/environment-pending.yaml
vendored
Normal file
26
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/environment-pending.yaml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
sha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
commitTime: "2026-03-01T12:00:00Z"
|
||||||
|
requiredDuration: 10m
|
||||||
|
phase: pending
|
||||||
|
atMostDurationRemaining: 5m
|
||||||
19
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/no-ready-condition.yaml
vendored
Normal file
19
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/no-ready-condition.yaml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Example
|
||||||
|
status: "True"
|
||||||
|
reason: Example
|
||||||
|
message: ok
|
||||||
|
observedGeneration: 1
|
||||||
11
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/no-status.yaml
vendored
Normal file
11
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/no-status.yaml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
sha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
commitTime: "2026-03-01T12:00:00Z"
|
||||||
|
requiredDuration: 10m
|
||||||
|
phase: success
|
||||||
|
atMostDurationRemaining: 0s
|
||||||
19
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/reconcile-error.yaml
vendored
Normal file
19
resource_customizations/promoter.argoproj.io/TimedCommitStatus/testdata/reconcile-error.yaml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: TimedCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: soak-gate
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
duration: 10m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: ReconciliationError
|
||||||
|
message: "Reconciliation failed: PromotionStrategy not found"
|
||||||
|
observedGeneration: 1
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
local hs = {}
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Initializing web request commit validation"
|
||||||
|
|
||||||
|
-- WebRequestCommitStatus (gitops-promoter v1alpha1): HTTP-based gating with per-environment phase
|
||||||
|
-- (pending / success / failure) in status.environments, plus aggregated Ready.
|
||||||
|
|
||||||
|
if obj.metadata.deletionTimestamp then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "WebRequestCommitStatus is being deleted"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if not obj.status then
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
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 WebRequestCommitStatus spec update to be observed"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "False" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
local msg = condition.message or "Unknown error"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
if reason == "ReconciliationError" then
|
||||||
|
hs.message = "Web request commit validation failed: " .. msg
|
||||||
|
else
|
||||||
|
hs.message = "Web request commit validation not ready (" .. reason .. "): " .. msg
|
||||||
|
end
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if condition.status == "Unknown" then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
local msg = condition.message or "Unknown"
|
||||||
|
local reason = condition.reason or "Unknown"
|
||||||
|
hs.message = "Web request commit validation unknown (" .. reason .. "): " .. msg
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not hasReadyCondition then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "WebRequestCommitStatus Ready condition is missing"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
local envs = obj.status.environments
|
||||||
|
if not envs or #envs == 0 then
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Web request commit validation reconciled"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
local pendingBranches = {}
|
||||||
|
local failureBranches = {}
|
||||||
|
local successCount = 0
|
||||||
|
|
||||||
|
for _, env in ipairs(envs) do
|
||||||
|
local branch = env.branch or "?"
|
||||||
|
local phase = env.phase or "pending"
|
||||||
|
if phase == "failure" then
|
||||||
|
table.insert(failureBranches, branch)
|
||||||
|
elseif phase == "pending" then
|
||||||
|
table.insert(pendingBranches, branch)
|
||||||
|
elseif phase == "success" then
|
||||||
|
successCount = successCount + 1
|
||||||
|
else
|
||||||
|
table.insert(pendingBranches, branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #failureBranches > 0 then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
hs.message = "Web request validation failed for branch(es): " .. table.concat(failureBranches, ", ")
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
if #pendingBranches > 0 then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Web request validation pending for branch(es): " .. table.concat(pendingBranches, ", ")
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "Web request validation passed for " .. tostring(successCount) .. " environment(s)"
|
||||||
|
return hs
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
tests:
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Initializing web request commit validation
|
||||||
|
inputPath: testdata/no-status.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: WebRequestCommitStatus is being deleted
|
||||||
|
inputPath: testdata/deleting.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for WebRequestCommitStatus spec update to be observed
|
||||||
|
inputPath: testdata/observed-generation-outdated.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Web request commit validation failed: Reconciliation failed: invalid URL template"
|
||||||
|
inputPath: testdata/reconcile-error.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Web request commit validation not ready (CommitStatusesNotReady): CommitStatus \"external-check-env-dev-ext-approval\" is not Ready because \"ReconciliationError\": SCM error"
|
||||||
|
inputPath: testdata/commit-statuses-not-ready.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: WebRequestCommitStatus Ready condition is missing
|
||||||
|
inputPath: testdata/no-ready-condition.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Web request commit validation reconciled
|
||||||
|
inputPath: testdata/empty-environments.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Web request validation pending for branch(es): env/dev"
|
||||||
|
inputPath: testdata/environment-pending.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: "Web request validation failed for branch(es): env/dev"
|
||||||
|
inputPath: testdata/environment-failure.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: Web request validation passed for 2 environment(s)
|
||||||
|
inputPath: testdata/all-success.yaml
|
||||||
33
resource_customizations/promoter.argoproj.io/WebRequestCommitStatus/testdata/all-success.yaml
vendored
Normal file
33
resource_customizations/promoter.argoproj.io/WebRequestCommitStatus/testdata/all-success.yaml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
reportedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
|
- branch: env/staging
|
||||||
|
reportedSha: "abcdef0123456789abcdef0123456789abcdef01"
|
||||||
|
phase: success
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: CommitStatusesNotReady
|
||||||
|
message: 'CommitStatus "external-check-env-dev-ext-approval" is not Ready because "ReconciliationError": SCM error'
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
reportedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
19
resource_customizations/promoter.argoproj.io/WebRequestCommitStatus/testdata/deleting.yaml
vendored
Normal file
19
resource_customizations/promoter.argoproj.io/WebRequestCommitStatus/testdata/deleting.yaml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
deletionTimestamp: "2025-07-04T12:00:00Z"
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments: []
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
reportedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: failure
|
||||||
|
lastResponseStatusCode: 500
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
reportedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: pending
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Example
|
||||||
|
status: "True"
|
||||||
|
reason: Example
|
||||||
|
message: ok
|
||||||
|
observedGeneration: 1
|
||||||
18
resource_customizations/promoter.argoproj.io/WebRequestCommitStatus/testdata/no-status.yaml
vendored
Normal file
18
resource_customizations/promoter.argoproj.io/WebRequestCommitStatus/testdata/no-status.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 2
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "True"
|
||||||
|
reason: ReconciliationSuccess
|
||||||
|
message: Reconciliation successful
|
||||||
|
observedGeneration: 1
|
||||||
|
environments:
|
||||||
|
- branch: env/dev
|
||||||
|
reportedSha: "0123456789abcdef0123456789abcdef01234567"
|
||||||
|
phase: success
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: promoter.argoproj.io/v1alpha1
|
||||||
|
kind: WebRequestCommitStatus
|
||||||
|
metadata:
|
||||||
|
name: external-check
|
||||||
|
namespace: gitops-promoter
|
||||||
|
generation: 1
|
||||||
|
spec:
|
||||||
|
promotionStrategyRef:
|
||||||
|
name: demo
|
||||||
|
key: ext-approval
|
||||||
|
httpRequest:
|
||||||
|
urlTemplate: "https://example.com/health"
|
||||||
|
method: GET
|
||||||
|
success:
|
||||||
|
when:
|
||||||
|
expression: "Response.StatusCode == 200"
|
||||||
|
mode:
|
||||||
|
polling:
|
||||||
|
interval: 1m
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- type: Ready
|
||||||
|
status: "False"
|
||||||
|
reason: ReconciliationError
|
||||||
|
message: "Reconciliation failed: invalid URL template"
|
||||||
|
observedGeneration: 1
|
||||||
Loading…
Reference in a new issue