Skip to content

Status Subresource in Gardener APIs ​

Overview ​

Kubernetes resources are divided into two conceptually separate parts: spec (the desired state) and status (the observed state). The status subresource is a dedicated API endpoint (/apis/<group>/<version>/<resource>/<name>/status) that allows controllers to update the observed state independently of the spec.

As a rule, only controllers that observe real-world state should write to status.

Statuses in Gardener APIs ​

Most Gardener API types carry a Status struct. It reports what a controller has observed about the resource. While the exact fields vary by resource, several fields appear across many types:

FieldDescription
ConditionsHealth check results (see Conditions)
ConstraintsOperational signals and informational state (see Constraints)
LastOperationThe most recent reconciliation operation (type, state, progress, description)
ObservedGenerationThe .metadata.generation last reconciled by the controller

Conditions ​

Conditions represent the results of health checks — discrete observations about whether a specific aspect of the system is working correctly.

FieldTypeRequiredDescription
TypeConditionTypeYesThe identifier for this condition (e.g. APIServerAvailable)
StatusConditionStatusYesWhether the condition is True, False, or Unknown
LastTransitionTimemetav1.TimeYesWhen Status last changed
LastUpdateTimemetav1.TimeYesWhen any field (reason, message, codes) last changed
ReasonstringYesA short, machine-readable word describing why the condition has its current status
MessagestringYesA human-readable description of the condition
Codes[]ErrorCodeNoStructured error codes (e.g. ERR_INFRA_QUOTA_EXCEEDED, ERR_UNAUTHORIZED) used for alerting and user-facing diagnostics

You can find the full type definition in pkg/apis/core/v1beta1/types_utils.go.

Examples ​

Shoot conditions:

TypeWhat it checks
APIServerAvailableWhether the shoot's kube-apiserver is reachable
ControlPlaneHealthyHealth of control plane components (etcd, apiserver, controller-manager, scheduler)
ObservabilityComponentsHealthyHealth of monitoring and logging components
EveryNodeReadyWhether all worker nodes are Ready
SystemComponentsHealthyHealth of system components deployed to the shoot (coredns, vpn, etc.)

Seed conditions:

TypeWhat it checks
BackupBucketsReadyWhether all backup buckets for the seed are healthy
ExtensionsReadyWhether all extensions installed in the seed are ready
SeedSystemComponentsHealthyHealth of system components running in the seed

Introducing New Condition Types ​

Condition types are deliberately stable. Before introducing a new ConditionType, check whether an existing type can be reused — many health checks naturally fit under SystemComponentsHealthy or ControlPlaneHealthy. Fragmenting health checks into many narrow types makes the overall status harder to read. Additionally, since the Gardener Dashboard always displays all conditions, having too many narrow types would clutter the cluster overview.

Constraints ​

Constraints use the same Condition type and struct type and struct as health check conditions but serve a different purpose: they carry operational signals and informational state that may gate or influence controller behavior, or that surface relevant cluster information to operators and users.

Unlike conditions, constraints are not limited to binary health checks. They can represent:

  • Prerequisites for operations — HibernationPossible blocks hibernation when Status: False; MaintenancePreconditionsSatisfied gates maintenance
  • Certificate and credential hygiene — CACertificateValiditiesAcceptable signals when CA certificates are nearing expiry
  • Migration readiness — ReadyForMigration must be True before a control-plane migration can proceed
  • Pending user actions — ManualInPlaceWorkersUpdated signals that a worker pool awaits manual intervention
  • Informational signals — HasIgnoredManagedResources or CRDsWithProblematicConversionWebhooks inform operators of configuration issues

See the shoot status documentation for more information.

Introducing New Constraint Types ​

Introducing new constraint types is more common and generally more acceptable than introducing new condition types. Constraints are often specific to an operation or a lifecycle phase, and there is less risk of conflict with existing semantics. If you are implementing a new controller behavior that depends on cluster state, a new constraint type is the right place to surface that signal.

Conditions and Constraints Status Convention ​

The Gardener Dashboard and other consumers display conditions and constraints generically. To ensure a consistent visual representation, the following convention applies:

  • Positive results (everything is fine, the operation is possible) → Status: True
  • Negative results (something is wrong, an operation is blocked) → Status: False

Even with this polarity rule, avoid negating the Type name. A type like HibernationNotPossible set to Status: False means "hibernation is not not possible" — a double negation that is hard to parse. Instead, use HibernationPossible with Status: True to express the same state clearly.

NOTE

The Gardener Dashboard always displays all conditions, whereas constraints are only shown when relevant (Status: False).

Using the Helper Functions ​

Never construct or compare Condition values by hand. Use the helpers in pkg/api/core/v1beta1/helper/ to ensure timestamps are set correctly and updates are idempotent.

Functional Helpers (condition.go) ​

GetOrInitConditionWithClock, UpdatedConditionWithClock, and MergeConditions are the most direct way to update a single condition. UpdatedConditionWithClock only advances LastTransitionTime when Status changes and LastUpdateTime when Reason, Message, or Codes change — stable inputs produce stable outputs.

See pkg/gardenlet/operation/botanist/dualstackmigration.go for a constraint being read, updated, and merged back.

Builder API (condition_builder.go) ​

NewConditionBuilder provides a fluent API useful when the status, reason, and message come from different code paths. Build() returns a boolean updated that is true only when the resulting condition differs from the old one — use it to skip unnecessary status patch calls.

See pkg/controllermanager/controller/shoot/migration/reconciler.go for the builder being used to set a constraint.

Atomically Rebuilding Both Slices ​

BuildConditions removes a set of managed types from the existing slice and appends the freshly computed values, leaving any types owned by other controllers untouched. Use it when a reconciler refreshes all its conditions and constraints in one pass.

See pkg/gardenlet/controller/shoot/care/reconciler.go for both slices being rebuilt atomically.