Skip to content

WorkApplier cache does not detect external label/annotation changes #223

Description

@mkolesnik

Summary

WorkApplier.Apply caches the desired work hash and the existing work's generation to skip redundant updates via safeToSkipApply. However, metadata-only changes (labels, annotations) to a ManifestWork do not bump the generation field — only resourceVersion changes. This means safeToSkipApply returns true even when an external actor has modified labels/annotations that the applier owns, and ManifestWorkEqual is never called.

Steps to reproduce

  1. Use WorkApplier.Apply to create a ManifestWork with labels (e.g. app.kubernetes.io/managed-by: my-addon)
  2. Externally modify one of those labels (e.g. change the value to someone-else)
  3. Call WorkApplier.Apply again with the same desired work

Expected: ManifestWorkEqual detects the label diff and patches the MW back to the desired state.

Actual: safeToSkipApply returns true (same desired hash, same generation), the comparison is skipped entirely, and the tampered label persists.

Analysis

The cache in workcache.go stores:

type cachedResource struct {
    resourceHash string  // hash of desired work
    generation   int64   // generation of existing work
}

safeToSkipApply matches when both the desired hash and generation are unchanged. Since label changes don't bump generation (only spec changes do), the cache never invalidates for metadata-only modifications.

This breaks the reconciliation contract — a controller using WorkApplier cannot restore its owned labels/annotations if an external actor tampers with them.

Environment

  • sdk-go v1.3.0
  • Discovered while integrating WorkApplier into an OCM addon controller

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions