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:
Michael Crenshaw 2026-04-13 17:09:21 -04:00 committed by GitHub
parent 7accd34f64
commit daadf868db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 1723 additions and 0 deletions

View file

@ -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

View file

@ -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

View 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

View 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

View 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

View 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

View file

@ -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

View 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

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View 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"

View 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: []

View 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

View 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

View file

@ -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

View 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

View 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:"'

View file

@ -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

View 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

View 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

View file

@ -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

View file

@ -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

View 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

View 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

View 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

View 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

View file

@ -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

View 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

View 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

View file

@ -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

View file

@ -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

View 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

View 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

View 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

View 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

View file

@ -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

View 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

View 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

View file

@ -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

View file

@ -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

View 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

View 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

View 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: []

View 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

View 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

View 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

View 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

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View 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

View file

@ -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: []

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -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