Skip to content

feat(ui): add drift detection for Crossplane managed resources#262

Open
claudiocosta wants to merge 2 commits into
crossplane-contrib:pre-releasefrom
claudiocosta:feat/drift-detection
Open

feat(ui): add drift detection for Crossplane managed resources#262
claudiocosta wants to merge 2 commits into
crossplane-contrib:pre-releasefrom
claudiocosta:feat/drift-detection

Conversation

@claudiocosta

@claudiocosta claudiocosta commented May 7, 2026

Copy link
Copy Markdown

Summary

Adds a Drift tab to the resource detail panel for Crossplane managed resources, showing a side-by-side split diff between spec.forProvider (desired state) and status.atProvider (observed state in the provider). The diff uses @codemirror/merge — the official CodeMirror 6 diff package — reusing the same YAML editor already present in the app.

Motivation

When Crossplane manages cloud resources, the provider state can drift from the desired configuration due to out-of-band changes (manual edits in AWS/GCP/Azure console, policy enforcement, etc.). Previously, users had to open both the spec and status tabs and mentally compare the YAML. This feature surfaces that comparison automatically.

Changes

New files

  • src/presentation/utils/driftUtils.js — recursive deep-diff between forProvider and atProvider (computeDrift), and helpers hasDrift / getDriftEntries
  • src/presentation/components/common/ResourceDrift.jsx — Drift tab UI using @codemirror/merge MergeView

Modified files

  • src/presentation/components/common/ResourceTabs.jsx — adds the Drift tab button (only shown when both spec.forProvider and status.atProvider are present)
  • src/presentation/components/common/ResourceDetails.jsx — wires up drift computation and passes props to tabs and content
  • package.json / package-lock.json — adds @codemirror/merge dependency

How it works

  • The Drift tab only appears on Crossplane managed resources (resources with both spec.forProvider and status.atProvider). XR composites and non-managed resources are unaffected.
  • A side-by-side split diff is rendered via @codemirror/merge, consistent with the YAML editor already used in ResourceYAML.jsx:
    • Left pane: status.atProvider (current state in the provider)
    • Right pane: spec.forProvider (desired state)
    • Character-level highlighting of exact changes within each line
    • Unchanged sections are collapsed automatically
    • Long lines (>300 chars) wrap within each pane
  • Provider-only fields (arn, id, tagsAll, etc.) are filtered from atProvider before diffing to avoid false positives
  • Keys are sorted alphabetically on both sides to eliminate ordering false positives
  • A summary bar shows field-level counts (changed, to add, provider-managed)

Testing

  • All existing Go tests pass: go test ./...
  • go vet ./... passes with no issues ✅
  • Manually validated against a live EKS cluster with Crossplane managed resources

Screenshots

image

The Drift tab appears in orange when drift is detected, showing the count of differing fields. The plan view highlights red (atProvider) and green(forProvider) lines with collapsible context.

Related

  • Closes #

@MoeidHeidari

Copy link
Copy Markdown
Collaborator

Hi @claudiocosta thanks for the great contribution to Crossview.This already means a lot for us. There are some tiny issues that need to be fixed before I can merge the changes.

  • First of all, please signoff your commits so that it passes the DCO
  • Please change the target branch from main to pre-release so that we can collect all the changes and be able to have a release candidate before we ship the changes to a new version.
  • The codebase already has a shared ResourceYaml component using @uiw/react-codemirror with the @codemirror/lang-yaml extension. The drift view in ResourceDrift.jsx duplicated this with a plain <Box as"pre"> block and manual inline styles, which leads to inconsistent look & feel between the YAML tab and the Drift tab.

Suggestion: Replace the custom renderer in PlanView with two side-by-side CodeMirror panes (one for status.atProvider and one for spec.forProvider), reusing the same setup as ResourceYAML.jsx.

Something like this

import CodeMirror from '@uiw/react-codemirror';
import { yaml } from '@codemirror/lang-yaml';
import { crossviewMirrorTheme } from '../../utils/crossviewMirrorTheme.js';

<CodeMirror
  value={yamlContent}
  extensions={[yaml()]}
  theme={colorMode === 'dark' ? crossviewMirrorTheme : undefined}
  readOnly
  style={{ fontSize: '0.75rem', lineHeight: '1.5' }}
/>

This keeps the UI consistent and avoids maintaining two separate code/YAML rendering paths.

suggestion

Adds a Drift tab to the resource detail panel for Crossplane managed
resources that have both spec.forProvider (desired state) and
status.atProvider (observed state).

The tab shows a plan-style unified YAML diff:
- Red lines: current state in the provider (atProvider) that differs
- Blue lines: desired state declared in the spec (forProvider)
- Gray lines: fields only present in atProvider (e.g. arn, id, tagsAll)

The diff uses LCS-based line comparison with context collapse (2 lines
around each change) and supports expand-all and copy-to-clipboard.
Provider-only fields are filtered from the diff to avoid false positives.

Signed-off-by: claudiocosta <claudio_costaa@hotmail.com>
@claudiocosta claudiocosta force-pushed the feat/drift-detection branch from 9efc7dd to 2f9503b Compare May 11, 2026 12:36
@claudiocosta claudiocosta changed the base branch from main to pre-release May 11, 2026 12:37
Replace the custom LCS plan view in ResourceDrift with a side-by-side
split diff using the @codemirror/merge package, which is part of the
official CodeMirror 6 ecosystem already used in ResourceYAML.

Changes:
- Use MergeView from @codemirror/merge for character-level diff highlight
- Apply filterToSchema() to atProvider before diffing to exclude
  provider-only fields (arn, id, tagsAll) and avoid false positives
- Apply sortKeys() to both sides to eliminate key-ordering false positives
- Enable EditorView.lineWrapping to handle long lines (>300 chars)
- collapseUnchanged to fold identical sections automatically
- Centered panel headers for atProvider/forProvider labels
- Add @codemirror/merge to package.json dependencies

Signed-off-by: claudiocosta <claudio_costaa@hotmail.com>
@claudiocosta

claudiocosta commented May 11, 2026

Copy link
Copy Markdown
Author

Hi @MoeidHeidari, thanks for the detailed review!

All three points have been addressed:

  1. DCO sign-off — both commits are now signed off (Signed-off-by: claudiocosta <claudio_costaa@hotmail.com>).
  2. Target branch — changed from main to pre-release as requested.
  3. CodeMirror consistency — replaced the custom <Box as="pre"> renderer with @codemirror/merge MergeView, which is part of the same CodeMirror 6 ecosystem already used in ResourceYAML.jsx. The split diff shows status.atProvider on the left and spec.forProvider on the right, with character-level highlighting, automatic collapse of unchanged sections, and line wrapping for long values.

A few additional improvements were included to ensure correctness:

  • Provider-only fields (arn, id, tagsAll, etc.) are filtered from atProvider before diffing to avoid false positives
  • Keys are sorted alphabetically on both sides to eliminate ordering false positives

Full details of the changes are in the updated PR description.

Let me know if there is anything else to adjust!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants