convoy docs
Search

Status and conditions

How to read the status of a Release, and which component writes each part.

The status of a Release records what convoy did and what it sees on the cluster. The controller-manager and two loops of the observer write it. Each writer owns its own fields, so one writer never overwrites the fields of another writer.

Who writes the status#

Each writer uses a server-side apply to /status with its own field manager. conditions is a map with type as the key, so each writer owns only its own conditions.

WriterField managerConditionsOther fields
controller-managerconvoy-releaseApplied, RolloutSucceeded, LifecycleSucceededlastAttempted, lastSuccessful, lastHealthy, rollout, pin, waves, lifecycle, resolvedRetentionPolicy, reconciledAt, lastHandledSyncAt, lastHandledForceSyncAt, observedGeneration
observer, drift loopconvoy-observeInSyncnone
observer, health loopconvoy-healthHealthyhealthSummary

lastAttempted and lastSuccessful#

status.lastAttempted records the most recent reconcile. The controller-manager writes it at each reconcile, also when the reconcile waits.

  • attemptedAt is the time of the reconcile.
  • resolvedSource, resolvedCluster, resolvedMutations and renderRef contain the values that the reconcile found before it stopped.
  • phase is Succeeded when convoy applied a render, and Failed when a step failed. When the reconcile waits, phase is empty. The API also defines Rendering and Syncing, but convoy does not set them.
  • failure.stage tells which step failed. See the table below.

status.lastSuccessful is a copy of lastAttempted from the most recent reconcile that applied a render. A reconcile that waits or fails does not change it. After a rollback, it contains the last healthy render, because that render runs on the cluster.

When lastAttempted.renderRef and lastSuccessful.renderRef are different, convoy did not apply the newest render yet. lastAttempted tells why.

failure.stage has these values:

failure.stageStep
SourceResolutionFetch of the source
ClusterResolutionLook-up of the target Cluster
RenderingHelm values, rendering, mutations, and the load of a pinned render
SyncingDry run, store write, apply, sync waves, cluster waves and prune
HookFailedA lifecycle hook with onFailure: Fail. hookName and hookPhase name the hook.

The API also defines MutationResolution. convoy does not set it. A mutation failure has the stage Rendering.

Where a reconcile can stop Select a step and a way it fails. Play or Step walks the reconcile and shows the resulting status.
Stepfailure.stageApplied reasonRetried with backoff
Fetch the source: errorSourceResolutionSourceNotReadyyes
Fetch the source: not supportedSourceResolutionUnsupportedno
Find the cluster: missing or not reachableClusterResolutionClusterNotReadyyes
Read Helm valuesRenderingValuesNotReadyyes
Render or mutate: errorRenderingDeployFailedyes
Render or mutate: not supportedRenderingUnsupportedno
Load the pinned render: not availableRenderingPinnedno
Load the pinned render: read or parse errorRenderingDeployFailedno
Manual sync mode: no /sync requestnoneWaitingForSyncno
Cluster wave: waitnoneWaitingForClusterWaveno
Cluster wave: timeoutSyncingClusterWaveFailedno
Dry run: rejectedSyncingUnappliableno
Write to the render storeSyncingDeployFailedyes
preSync hook: runningnoneWaitingForHookno
preSync hook: failedHookFailedHookFailedno
Apply a wave: errorSyncingDeployFailedyes
Sync wave: wait for the previous wavenoneWaitingForWaveHealthno
Sync wave: timeoutSyncingWaveFailed or RolloutFailedno
PruneSyncingPruneFailedyes
Start postSync hooks: errorSyncingDeployFailedyes
All steps donenoneAppliedno
  • Yes: with spec.syncPolicy.retryBackoff.initial, convoy uses that backoff. Without it, the work queue uses its default rate limit.
  • No: convoy reconciles again after spec.syncPolicy.interval, or when the Release changes. A Progressing rollout is checked again at least every 10 seconds.
  • A sync-wave timeout with failurePolicy.rollback: OnWaveFailure can roll back. Then the reason is RolledBack. See Progressive rollout.

Conditions#

ConditionWriterWhen it is present
Appliedcontroller-managerAlways, after the first reconcile.
RolloutSucceededcontroller-managerWhen spec.failurePolicy is set. The reason is the rollout state.
LifecycleSucceededcontroller-managerWhen the Release has lifecycle hooks.
InSyncobserverAfter the first drift check.
HealthyobserverAfter the first health check.

Applied is True in three cases:

  • convoy applied the render.
  • convoy holds the last healthy render after a rollback.
  • The Release is in manual sync mode, and the render did not change.

A wait is False, for example WaitingForSync.

Healthy is True with reason NotConfigured when the Release has no spec.healthAssessment. It is Unknown with reason Pending while the current render is not applied and assessed.

Applied · written by convoy-controller-manager (field manager convoy-release)

ReasonStatusMeaning
AppliedTrueconvoy applied the current render. In manual sync mode, the current render is equal to the last applied render.
SourceNotReadyFalseconvoy could not fetch the source. convoy retries.
ValuesNotReadyFalseconvoy could not read a Helm values layer (a ConfigMap, a Secret or a GitRepository path). convoy retries.
ClusterNotReadyFalseThe Cluster does not exist or is not reachable. convoy retries.
UnsupportedFalseThe Release uses a feature that convoy does not implement, for example a Git semver ref, Helm dependencyMode: resolve or jsonnet content. This is a configuration error, so convoy does not retry with backoff.
UnappliableFalseThe target cluster rejected the render in the pre-apply server-side dry run. convoy applies nothing. A forced /sync skips the dry run.
DeployFailedFalseRendering, mutation or the apply failed. The message and status.lastAttempted.failure.stage tell which step failed. convoy retries.
PruneFailedFalseconvoy applied the render, but could not delete an object that the render no longer contains. convoy retries.
PinnedFalseconvoy cannot apply the pinned render, because no render store is configured or the store does not contain that render.
WaitingForSyncFalseManual sync mode. The current render differs from the last applied render. convoy waits for a /sync request.
WaitingForWaveHealthFalseA sync wave waits until the resources of the previous wave are healthy.
WaveFailedFalseA sync wave timed out while it waited for the previous wave. The Release has no failurePolicy.
RolloutFailedFalseA sync wave timed out while it waited for the previous wave, and the failurePolicy did not roll back.
RolledBackTrueThe rollout failed and convoy applied the last healthy render again.
WaitingForClusterWaveFalseThe Release waits for the Releases of a lower cluster wave on the same Cluster.
ClusterWaveFailedFalseThe Releases of a lower cluster wave did not become ready before the cluster-wave timeout.
WaitingForHookFalseconvoy waits for the workloads of a lifecycle hook phase to complete.
HookFailedFalseA lifecycle hook with onFailure: Fail failed. convoy stops the sync.

See Release conditions for all reasons.

Why there is no Ready condition#

Each condition has one writer. A Ready condition needs one writer that reads the conditions of the other writers. It also hides which part failed. Thus convoy does not write Ready. You combine the conditions.

Combine the signals#

To check that a new render runs and works:

  1. lastAttempted.renderRef is equal to lastSuccessful.renderRef, and Applied is True.
  2. InSync is True. The cluster agrees with the desired render.
  3. Healthy is True. A reason Pending means that the observer did not assess this render yet.
  4. With spec.failurePolicy, RolloutSucceeded is True and status.rollout.renderRef is the new render.

Each condition has observedGeneration. When it is lower than metadata.generation, the condition is from an older spec. A pin, a /sync request or a change to mutations or lifecycle does not change metadata.generation.

Timestamps#

  • reconciledAt is the time of the last status write by the controller-manager. lastAttempted.attemptedAt changes at each reconcile, so in practice it is the time of the last reconcile.
  • lastHandledSyncAt is the time of the last /sync request that convoy applied in manual sync mode. convoy sets it only after a successful apply. In automatic mode, convoy does not set it.
  • lastHandledForceSyncAt is the time of the last /sync?force=true request that convoy handled. convoy sets it at the first reconcile after the request, also when that reconcile fails.

Printer columns#

kubectl get releases shows State (the rollout state), and the status of Applied, InSync and Healthy. See Release for all columns.

In the code

convoy v0.9.0