Skip to content
GitHub stars

LynqNode API Reference

Kind: LynqNode
API Version: operator.lynq.sh/v1
Group: operator.lynq.sh

LynqNode represents one active database row × one LynqForm combination. Created and deleted automatically by the LynqHub controller — users typically don't create or edit LynqNodes directly.

Resource lifecycle guide · API index

Spec

LynqNode specs are generated by the hub controller and mirror the LynqForm structure with template variables resolved into the metadata.annotations. Direct editing is unusual.

yaml
apiVersion: operator.lynq.sh/v1
kind: LynqNode
metadata:
  name: acme-corp-web-app            # format: {uid}-{form-name}
  namespace: lynq-system
  annotations:
    lynq.sh/uid: "acme-corp"         # resolved .uid variable
    lynq.sh/activate: "true"         # resolved .activate variable
    lynq.sh/planId: "enterprise"     # from extraValueMappings
    lynq.sh/nodeUrl: "https://..."   # from extraValueMappings
  labels:
    lynq.sh/hub: production-nodes    # source hub
    lynq.sh/uid: acme-corp
spec:
  uid: string                        # Node unique identifier
  templateRef: string                # LynqForm name that generated this node

  # Rendered resource arrays — same structure as LynqForm
  deployments: []
  services: []
  # ... (all resource types)

Status

yaml
status:
  observedGeneration: int64
  desiredResources: int32            # Total resources in the form
  readyResources: int32              # Resources with ready condition met
  failedResources: int32             # Resources that failed to apply or timed out
  skippedResources: int32            # Resources skipped due to dependency failures
  skippedResourceIds: []string       # IDs of skipped resources

  appliedResources: []string         # Current set of tracked resources
                                     # Format: "Kind/namespace/name@id"
                                     # Example: "Deployment/default/acme-app@app"

  lastFullReconcileAt: timestamp     # Baseline used to schedule the next
                                     # periodic force-reapply (drift correction).
                                     # See "lastFullReconcileAt" below.

  conditions:
  - type: Ready
    status: "True" | "False" | "Unknown"
    reason: string
    message: string
    lastTransitionTime: timestamp
  - type: Progressing
    status: "True" | "False"
    reason: string
  - type: Degraded
    status: "True" | "False"
    reason: string
    message: string
  - type: Conflicted
    status: "True" | "False"
    reason: string

Conditions

Ready

True when all desired resources are applied and ready.

ReasonStatusMeaning
ReconciledTrueAll resources ready
ResourcesFailedAndConflictedFalseBoth failed and conflicted resources exist
ResourcesConflictedFalseOne or more resources in ownership conflict
ResourcesFailedFalseOne or more resources failed to apply
NotAllResourcesReadyFalseResources exist but haven't reached ready state

Degraded

True when the node has health issues — even if reconciliation has completed.

ReasonStatusMeaning
HealthyFalseNo issues
ResourceFailuresAndConflictsTrueBoth failed and conflicted resources
ResourceFailuresTrueFailed resources
ResourceConflictsTrueConflicted resources
ResourcesNotReadyTrueResources not yet ready (added v1.1.4)

Progressing

True during active reconciliation.

Conflicted

True when any resource has an SSA field-manager conflict.

appliedResources

Tracks every resource currently under management. Format: Kind/namespace/name@id.

["Deployment/default/acme-app@app", "Service/default/acme-svc@svc"]

Lynq compares this list against the current template on each reconcile. Resources in appliedResources but not in the current template are treated as orphans and handled per their stored deletionPolicy annotation.

lastFullReconcileAt

Timestamp the controller uses to schedule the next periodic force-reapply (drift correction). When time.Since(lastFullReconcileAt) >= ForceReapplyInterval (default 10 minutes), the next reconcile bypasses the per-resource skip check and re-applies every child resource unconditionally. This is the backstop for external mutations that preserve lynq.sh/applied-hash on a child resource and would therefore not be caught by the annotation-only skip path.

A nil value (absent field) means the controller has not yet established a baseline for this node — either the LynqNode is brand-new or the controller just restarted and observed it for the first time. The controller treats nil as "stamp now as the baseline and defer the first force by one full interval". This deferral prevents a re-apply storm across all LynqNodes on every controller restart.

The companion annotations on child resources are:

  • lynq.sh/applied-hash — desired-spec hash from the last successful apply. The skip check compares this against the freshly computed hash; equal ⇒ skip.
  • lynq.sh/apply-start-time — wall-clock timestamp stamped at the most recent apply (preserved across reconciles when the spec is unchanged). Used as the readiness-timeout reference instead of creationTimestamp.

Both annotations are written atomically as part of the SSA / Update payload — no follow-up MergePatch — so the skip check never observes a half-stamped state.

Lifecycle

Creation

LynqHub creates a LynqNode for each activeRow × referencingForm combination. The LynqNode controller then:

  1. Adds finalizer lynqnode.operator.lynq.sh/finalizer
  2. Renders templates with variables from metadata.annotations
  3. Builds dependency graph from dependIds
  4. Applies resources in topological order
  5. Waits for readiness (per waitForReady / timeoutSeconds)
  6. Updates status

Deletion

When the LynqNode is deleted, the finalizer runs cleanup:

  • Resources with deletionPolicy: Delete — removed from cluster
  • Resources with deletionPolicy: Retain — orphan markers added, resource stays

After cleanup, the finalizer is removed and Kubernetes deletes the CR.

Periodic reconciliation

The LynqNode controller requeues every 30 seconds to detect child resource status changes (e.g., a Deployment becoming ready). Combined with event-driven watches on 12 resource types, status reflects reality within ~30 seconds.

kubectl Reference

bash
# List all nodes with status
kubectl get lynqnodes -n lynq-system

# Check ready/desired ratio
kubectl get lynqnode <name> -o jsonpath='{.status.readyResources}/{.status.desiredResources}'

# View all conditions
kubectl get lynqnode <name> -o jsonpath='{range .status.conditions[*]}{.type}: {.status} ({.reason}){"\n"}{end}'

# Find resources managed by this node
kubectl get all -l lynq.sh/node=<name>

# Find skipped resources (dependency failures)
kubectl get lynqnode <name> -o jsonpath='{.status.skippedResourceIds}'

# Force reconciliation
kubectl annotate lynqnode <name> lynq.sh/force-reconcile=$(date +%s) --overwrite

# Watch ready count
watch kubectl get lynqnode <name> -o jsonpath='{.status.readyResources}/{.status.desiredResources}'

See Also