Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions api/v2/condition_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const (
// (uninstall/rollback) due to a failure of the last release attempt against the
// latest desired state.
RemediatedCondition string = "Remediated"

// DriftedCondition represents the status of the Helm release drift detection,
// indicating that the deployed release has drifted from the desired state.
DriftedCondition string = "Drifted"
)

const (
Expand Down Expand Up @@ -79,4 +83,12 @@ const (
// DependencyNotReadyReason represents the fact that
// one of the dependencies is not ready.
DependencyNotReadyReason string = "DependencyNotReady"

// DriftDetectedReason represents the fact that drift has been detected in the
// Helm release compared to the expected state.
DriftDetectedReason string = "DriftDetected"

// NoDriftDetectedReason represents the fact that no drift has been detected in
// the Helm release compared to the expected state.
NoDriftDetectedReason string = "NoDriftDetected"
)
28 changes: 26 additions & 2 deletions docs/spec/v2/helmreleases.md
Original file line number Diff line number Diff line change
Expand Up @@ -2020,8 +2020,9 @@ A HelmRelease enters various states during its lifecycle, reflected as
[Kubernetes Conditions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
It can be [reconciling](#reconciling-helmrelease) when it is being processed by
the controller, it can be [ready](#ready-helmrelease) when the Helm release is
installed and up-to-date, or it can [fail](#failed-helmrelease) during
reconciliation.
installed and up-to-date, it can [fail](#failed-helmrelease) during
reconciliation, or it can be [drifted](#drifted-helmrelease) if the
drift detection mode is set to enabled/warn and there is a drift.

The HelmRelease API is compatible with the [kstatus specification](https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus),
and reports `Reconciling` and `Stalled` conditions where applicable to provide
Expand Down Expand Up @@ -2106,6 +2107,29 @@ HelmRelease's `.status.conditions`:
The `TestSuccess` Condition will retain a status value of `"True"` until the
next Helm install or upgrade occurs, or the Helm tests are disabled.

#### Drifted HelmRelease

The helm-controller marks the HelmRelease as _drifted_ when it has the following
characteristics:

- The HelmRelease have drift detection mode set to enabled or warn.
- There is a drift detected against the cluster state.

When the HelmRelease is "drifted", the controller sets a Condition with the
following attributes in the HelmRelease's `.status.conditions`:

- `type: Drifted`
- `status: "True"`
- `reason: DriftDetected`

When the HelmRelease have drift detection mode set to enabled or warn there
and there is no drift, the controller sets a Condition with the following
attributes in the HelmRelease's `.status.conditions`:

- `type: Drifted`
- `status: "False"`
- `reason: NoDriftDetected`

#### Failed HelmRelease

The helm-controller may get stuck trying to determine state or produce a Helm
Expand Down
12 changes: 8 additions & 4 deletions internal/reconcile/atomic_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state
}
}

if req.Object.GetDriftDetection().MustDetectChanges() {
conditions.MarkFalse(req.Object, v2.DriftedCondition, v2.NoDriftDetectedReason, "No drift detected against the cluster state")
}

Comment thread
yozel marked this conversation as resolved.
return nil, nil
case ReleaseStatusLocked:
log.Info(msgWithReason("release locked", state.Reason))
Expand Down Expand Up @@ -436,10 +440,10 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state
}
}

r.eventRecorder.Eventf(req.Object, corev1.EventTypeWarning, "DriftDetected",
"Cluster state of release %s has drifted from the desired state:\n%s",
req.Object.Status.History.Latest().FullReleaseName(), diff.SummarizeDiffSet(state.Diff),
)
msg := fmt.Sprintf("Cluster state of release %s has drifted from the desired state:\n%s",
req.Object.Status.History.Latest().FullReleaseName(), diff.SummarizeDiffSet(state.Diff))
r.eventRecorder.Eventf(req.Object, corev1.EventTypeWarning, v2.DriftDetectedReason, msg)
conditions.MarkTrue(req.Object, v2.DriftedCondition, v2.DriftDetectedReason, "%s", msg)

if req.Object.GetDriftDetection().GetMode() == v2.DriftDetectionEnabled {
return NewCorrectClusterDrift(r.configFactory, r.eventRecorder, state.Diff, kube.ManagedFieldsManager), nil
Expand Down
6 changes: 6 additions & 0 deletions internal/reconcile/atomic_release_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,9 @@ func TestAtomicRelease_actionForState(t *testing.T) {
"Deployment/something/mock removed",
),
},
assertConditions: []metav1.Condition{
*conditions.TrueCondition(v2.DriftedCondition, v2.DriftDetectedReason, "Cluster state of release mock-ns/mock-release.v1 has drifted from the desired state:\nDeployment/something/mock removed"),
},
},
{
name: "drifted release only triggers event if mode is warn",
Expand Down Expand Up @@ -1725,6 +1728,9 @@ func TestAtomicRelease_actionForState(t *testing.T) {
"Deployment/something/mock changed (0 additions, 1 changes, 0 removals)",
),
},
assertConditions: []metav1.Condition{
*conditions.TrueCondition(v2.DriftedCondition, v2.DriftDetectedReason, "Cluster state of release mock-ns/mock-release.v1 has drifted from the desired state:\nDeployment/something/mock changed (0 additions, 1 changes, 0 removals)"),
},
},
{
name: "out-of-sync release triggers upgrade",
Expand Down