From c07be30e0998f16af1696ae0faf9fdaea49d1c2a Mon Sep 17 00:00:00 2001 From: Michael Guarino Date: Thu, 13 Aug 2026 15:08:50 +0000 Subject: [PATCH 1/4] docs: add CustomHealth Lua authoring guide --- generated/routes.json | 4 + .../deployment-operator/custom-health.md | 93 +++++++++++++++++++ src/routing/docs-structure.ts | 4 + 3 files changed, 101 insertions(+) create mode 100644 pages/plural-features/continuous-deployment/deployment-operator/custom-health.md diff --git a/generated/routes.json b/generated/routes.json index 3f04b181..23824478 100644 --- a/generated/routes.json +++ b/generated/routes.json @@ -151,6 +151,10 @@ "relPath": "/plural-features/continuous-deployment/deployment-operator/agent-configuration.md", "lastmod": "2026-06-26T10:42:27.000Z" }, + "/plural-features/continuous-deployment/deployment-operator/custom-health": { + "relPath": "/plural-features/continuous-deployment/deployment-operator/custom-health.md", + "lastmod": "2026-08-13T15:06:28.330Z" + }, "/plural-features/continuous-deployment/git-service": { "relPath": "/plural-features/continuous-deployment/git-service.md", "lastmod": "2025-12-30T16:00:47.000Z" diff --git a/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md b/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md new file mode 100644 index 00000000..296056d6 --- /dev/null +++ b/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md @@ -0,0 +1,93 @@ +--- +title: CustomHealth Lua authoring +description: Define CustomHealth Lua scripts for a target Kubernetes GVK +--- + +`CustomHealth` is an exact-GVK health override used by the deployment operator. Write one when you need to define the health status for a particular Kubernetes group, version, and kind. + +Start with a complete manifest. This example targets `stable.example.com/v1`, `Widget` resources and safely handles a missing `status` or `status.phase` field. + +```yaml +apiVersion: deployments.plural.sh/v1alpha1 +kind: CustomHealth +metadata: + name: widget-health +spec: + group: stable.example.com + version: v1 + kind: Widget + script: | + -- obj is the target Kubernetes resource. + local metadata = obj.metadata or {} + local status = obj.status or {} + local phase = status.phase + + if phase == "Ready" then + healthStatus = { + status = "Healthy", + message = "Widget is ready", + } + elseif phase == "Failed" then + healthStatus = { + status = "Degraded", + message = "Widget reported a failed phase", + } + elseif metadata.name == nil then + healthStatus = { + status = "Unknown", + message = "Waiting for a resource name", + } + else + healthStatus = { + status = "Unknown", + message = "Waiting for Widget " .. metadata.name .. " to report a health phase", + } + end +``` + +Set `metadata.name` to name the `CustomHealth` resource. Set `spec.group`, `spec.kind`, and, when needed, the optional `spec.version` to identify the target GVK. Put the Lua code in `spec.script`. + +## Script inputs + +The script receives a global `obj`: the unstructured target Kubernetes resource. Read the fields that define health for the target GVK from `obj`, and guard fields that might be absent. + +## Script outputs + +Every script must set global `healthStatus` to an object with these fields: + +| Field | Purpose | +| --- | --- | +| `healthStatus.status` | The resource health status. | +| `healthStatus.message` | An actionable health message. | + +`healthStatus.status` must be one of: `Healthy`, `Progressing`, `Degraded`, `Suspended`, `Unknown`, or `Missing`. + +## Helpers + +The following supported helpers can be used in `spec.script`: + +- `checkResourceStatusConditions(obj)` +- `checkJobStatusConditions(obj)` + +For example, assign a helper result directly to `healthStatus`: + +```yaml +spec: + script: | + healthStatus = checkResourceStatusConditions(obj) +``` + +For a target whose health script uses the job helper: + +```yaml +spec: + script: | + healthStatus = checkJobStatusConditions(obj) +``` + +## Authoring guidance + +- Keep each script focused on the fields relevant to its target GVK. +- Guard optional or missing fields before reading them. +- Always assign a fallback `healthStatus`, such as `Unknown` when the resource does not yet contain the fields your script needs. +- Make `healthStatus.message` specific enough to help a reader understand the reported state. diff --git a/src/routing/docs-structure.ts b/src/routing/docs-structure.ts index 495f603f..fb3a196f 100644 --- a/src/routing/docs-structure.ts +++ b/src/routing/docs-structure.ts @@ -130,6 +130,10 @@ export const docsStructure: DocSection[] = [ path: 'agent-configuration', title: 'AgentConfiguration', }, + { + path: 'custom-health', + title: 'CustomHealth Lua authoring', + }, ], }, { path: 'git-service', title: 'Git-sourced services' }, From ef363081488bdb010e1199792fd1b42c97f90a3c Mon Sep 17 00:00:00 2001 From: Michael Guarino Date: Thu, 13 Aug 2026 15:12:56 +0000 Subject: [PATCH 2/4] ci: label documentation pull requests --- .github/workflows/pr-labels.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/pr-labels.yaml b/.github/workflows/pr-labels.yaml index 3f8c4c01..3ad2a223 100644 --- a/.github/workflows/pr-labels.yaml +++ b/.github/workflows/pr-labels.yaml @@ -8,6 +8,17 @@ jobs: name: Check that PR has required labels runs-on: ubuntu-latest steps: + - name: Classify documentation pull requests + if: startsWith(github.event.pull_request.title, 'docs:') + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['enhancement'], + }) - uses: mheap/github-action-required-labels@v5 with: mode: exactly From 6354be47d79b5848bfc2728490cbaf04f6c03353 Mon Sep 17 00:00:00 2001 From: Michael Guarino Date: Thu, 13 Aug 2026 15:20:03 +0000 Subject: [PATCH 3/4] docs: clarify CustomHealth condition helpers --- .../deployment-operator/custom-health.md | 87 +++++++------------ 1 file changed, 29 insertions(+), 58 deletions(-) diff --git a/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md b/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md index 296056d6..6d5c8dee 100644 --- a/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md +++ b/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md @@ -3,91 +3,62 @@ title: CustomHealth Lua authoring description: Define CustomHealth Lua scripts for a target Kubernetes GVK --- -`CustomHealth` is an exact-GVK health override used by the deployment operator. Write one when you need to define the health status for a particular Kubernetes group, version, and kind. +`CustomHealth` is an exact-GVK health override used by the deployment operator. Write one when you need to define health for a particular Kubernetes group, version, and kind. -Start with a complete manifest. This example targets `stable.example.com/v1`, `Widget` resources and safely handles a missing `status` or `status.phase` field. +`CustomHealth` is a deployment-operator API, so apply it to each cluster where it should be used. To distribute the same resource across a fleet, use a [GlobalService](/plural-features/continuous-deployment/global-service). + +Start with a complete manifest. This example targets `example.io/v1`, `Example` resources and evaluates their `Ready` condition. ```yaml apiVersion: deployments.plural.sh/v1alpha1 kind: CustomHealth metadata: - name: widget-health + name: example-ready-condition + namespace: spec: - group: stable.example.com + group: example.io version: v1 - kind: Widget + kind: Example script: | - -- obj is the target Kubernetes resource. - local metadata = obj.metadata or {} - local status = obj.status or {} - local phase = status.phase - - if phase == "Ready" then - healthStatus = { - status = "Healthy", - message = "Widget is ready", - } - elseif phase == "Failed" then - healthStatus = { - status = "Degraded", - message = "Widget reported a failed phase", - } - elseif metadata.name == nil then - healthStatus = { - status = "Unknown", - message = "Waiting for a resource name", - } - else - healthStatus = { - status = "Unknown", - message = "Waiting for Widget " .. metadata.name .. " to report a health phase", - } + healthStatus = { status = "Unknown" } + + if Obj.status ~= nil and statusConditionExists(Obj.status, "Ready") then + healthStatus = { status = "Progressing" } + if isStatusConditionTrue(Obj.status, "Ready") then + healthStatus = { status = "Healthy" } + end end ``` -Set `metadata.name` to name the `CustomHealth` resource. Set `spec.group`, `spec.kind`, and, when needed, the optional `spec.version` to identify the target GVK. Put the Lua code in `spec.script`. +Set `metadata.name` and `metadata.namespace` for the `CustomHealth` resource. Set `spec.group`, `spec.kind`, and, when needed, the optional `spec.version` to identify the target GVK. Put the Lua code in `spec.script`. ## Script inputs -The script receives a global `obj`: the unstructured target Kubernetes resource. Read the fields that define health for the target GVK from `obj`, and guard fields that might be absent. +The script receives a global `Obj`: the unstructured target Kubernetes resource. Read the fields that define health for the target GVK from `Obj`, and guard fields that might be absent. ## Script outputs -Every script must set global `healthStatus` to an object with these fields: +Every script sets global `healthStatus` to an object with `status` and, when applicable, `message` fields. The allowed status values are: -| Field | Purpose | -| --- | --- | -| `healthStatus.status` | The resource health status. | -| `healthStatus.message` | An actionable health message. | - -`healthStatus.status` must be one of: `Healthy`, `Progressing`, `Degraded`, `Suspended`, `Unknown`, or `Missing`. +- `Healthy` +- `Progressing` +- `Degraded` +- `Suspended` +- `Unknown` +- `Missing` ## Helpers -The following supported helpers can be used in `spec.script`: - -- `checkResourceStatusConditions(obj)` -- `checkJobStatusConditions(obj)` - -For example, assign a helper result directly to `healthStatus`: - -```yaml -spec: - script: | - healthStatus = checkResourceStatusConditions(obj) -``` +Use the supported status-condition helpers in `spec.script`: -For a target whose health script uses the job helper: +- `statusConditionExists(Obj.status, "Ready")` +- `isStatusConditionTrue(Obj.status, "Ready")` -```yaml -spec: - script: | - healthStatus = checkJobStatusConditions(obj) -``` +The example above uses both helpers to evaluate a `Ready` condition. For resources that use another condition type, replace `"Ready"` with the condition type relevant to that resource. ## Authoring guidance - Keep each script focused on the fields relevant to its target GVK. - Guard optional or missing fields before reading them. - Always assign a fallback `healthStatus`, such as `Unknown` when the resource does not yet contain the fields your script needs. -- Make `healthStatus.message` specific enough to help a reader understand the reported state. +- Use the status values consistently and make messages actionable when you set `healthStatus.message`. From e7793c4e1bbd4c2423a19b1d3dbc4423ca4cf4a7 Mon Sep 17 00:00:00 2001 From: Michael Guarino Date: Thu, 13 Aug 2026 15:28:13 +0000 Subject: [PATCH 4/4] docs: simplify CustomHealth manifest example --- .../deployment-operator/custom-health.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md b/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md index 6d5c8dee..34ab0944 100644 --- a/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md +++ b/pages/plural-features/continuous-deployment/deployment-operator/custom-health.md @@ -7,14 +7,13 @@ description: Define CustomHealth Lua scripts for a target Kubernetes GVK `CustomHealth` is a deployment-operator API, so apply it to each cluster where it should be used. To distribute the same resource across a fleet, use a [GlobalService](/plural-features/continuous-deployment/global-service). -Start with a complete manifest. This example targets `example.io/v1`, `Example` resources and evaluates their `Ready` condition. +Here's an example manifest: ```yaml apiVersion: deployments.plural.sh/v1alpha1 kind: CustomHealth metadata: name: example-ready-condition - namespace: spec: group: example.io version: v1 @@ -30,7 +29,7 @@ spec: end ``` -Set `metadata.name` and `metadata.namespace` for the `CustomHealth` resource. Set `spec.group`, `spec.kind`, and, when needed, the optional `spec.version` to identify the target GVK. Put the Lua code in `spec.script`. +Set `spec.group`, `spec.kind`, and, when needed, the optional `spec.version` to identify the target GVK. Put the Lua code in `spec.script`. ## Script inputs