Skip to content

eve-k: use stable :latest tag for eve-external-boot-image in VMIRSes#6100

Draft
andrewd-zededa wants to merge 1 commit into
lf-edge:masterfrom
andrewd-zededa:eve-k-external-boot-image-tag
Draft

eve-k: use stable :latest tag for eve-external-boot-image in VMIRSes#6100
andrewd-zededa wants to merge 1 commit into
lf-edge:masterfrom
andrewd-zededa:eve-k-external-boot-image-tag

Conversation

@andrewd-zededa

@andrewd-zededa andrewd-zededa commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Description

Container-as-VM VMIRSes were embedding a versioned eve-external-boot-image
tag (e.g. :1.2.3) that gets deleted by cluster-init.sh cleanup on every
baseOS upgrade, permanently breaking virt-launcher pod scheduling on
upgraded nodes (ErrImageNeverPull, ImagePullPolicy: PullNever).

Fix by decoupling image lifecycle from VMIRS references:

  • cluster-init.sh: tag the imported image as both :VERSION and :latest,
    and exclude :latest from cleanup_old_external_boot_images so it survives
    across upgrades. The :latest re-tag uses ctr image tag --force: the
    image store is persistent (/persist/vault/containerd), so :latest
    already exists from the prior version and a plain tag would fail with
    AlreadyExists — which, combined with the inspecti early-return guard,
    would leave :latest stuck on the old image permanently.

  • kubevirt.go CreateReplicaVMIConfig: use :latest instead of reading
    /run/eve-release, so new VMIRSes never embed a version-specific tag.

  • zedkube bootImgMigrator (new): one-shot state machine
    (WaitReady → Migrate → Done) that migrates existing VMIRSes still
    referencing a versioned tag. WaitReady polls independently for both
    eve-external-boot-image:latest in k3s containerd and the KubeVirt CR
    Available condition, and requires nodeName to be known before advancing
    (an empty nodeName would skip every VMIRS and latch to Done). Scoped to
    VMIRSes whose kubernetes.io/hostname node affinity matches the local
    node, so each node only patches its own apps. Once complete the event
    loop skips it (isDone) to avoid rebuilding a kubevirt client every tick.

    This races the descheduler in a rolling upgrade: an app can be
    rescheduled onto the upgraded node before its VMIRS is patched. step()
    collapses WaitReady → Migrate → Done into a single tick to shrink that
    window, and the case self-heals — checkStuckPendingVMI force-deletes the
    stuck Pending VMI and the ReplicaSet recreates it from the patched
    template.

Also documents the migration in docs/zedkube.md and normalizes a
pre-existing markdown table separator in the same file.

PR dependencies

None

How to test and validate this PR

TODO

Changelog notes

TODO

PR Backports

TODO

  • 16.0-stable: To be backported.
  • 14.5-stable: No, as the feature is not available there.
  • 13.4-stable: No, as the feature is not available there.

Checklist

  • I've provided a proper description
  • I've added the proper documentation
  • I've tested my PR on amd64 device
  • I've tested my PR on arm64 device
  • I've written the test verification instructions
  • I've set the proper labels to this PR

And the last but not least:

  • I've checked the boxes above, or I've provided a good reason why I didn't
    check them.

Please, check the boxes above after submitting the PR in interactive mode.

Comment thread pkg/pillar/cmd/zedkube/zedkube.go Outdated
@@ -763,8 +766,7 @@ func Run(ps *pubsub.PubSub, loggerArg *logrus.Logger, logArg *base.LogObject, ar
zedkubeCtx.reconcileSRIOVDevicePlugin(&aa)
}
}
zedkubeCtx.applyLonghornNodeDrainPolicy()

zedkubeCtx.bootImgMigrator.step(&zedkubeCtx)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we check if this is not done yet then go into this?

patchCtx, patchCancel := context.WithTimeout(context.Background(), kubeAPITimeout)
_, merr := virtClient.ReplicaSet(kubeapi.EVEKubeNameSpace).Update(
patchCtx, vmirs, metav1.UpdateOptions{})
patchCancel()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to see if we can use patch like:

_, merr := virtClient.ReplicaSet(kubeapi.EVEKubeNameSpace).Patch(
    patchCtx, vmirs.Name, k8stypes.MergePatchType, patch, metav1.PatchOptions{})

which should give a limited change than the update one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this will avoid resource version issues between the list and update, will incorporate the change.

@andrewd-zededa
andrewd-zededa force-pushed the eve-k-external-boot-image-tag branch from 25ddcc8 to 96b156d Compare July 1, 2026 18:10
@github-actions
github-actions Bot requested a review from naiming-zededa July 1, 2026 18:10

@naiming-zededa naiming-zededa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andrewd-zededa
andrewd-zededa force-pushed the eve-k-external-boot-image-tag branch from 96b156d to c615ef2 Compare July 6, 2026 23:10
@github-actions
github-actions Bot requested a review from naiming-zededa July 6, 2026 23:11
@andrewd-zededa
andrewd-zededa force-pushed the eve-k-external-boot-image-tag branch from c615ef2 to 28ba07c Compare July 6, 2026 23:13
@andrewd-zededa

Copy link
Copy Markdown
Contributor Author

Fixed timing race with descheduler, and issue with bumping the latest->latest tag forward. Need to test locally before moving to ready to review.

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.81818% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 22.78%. Comparing base (111a305) to head (28ba07c).

Files with missing lines Patch % Lines
pkg/pillar/cmd/zedkube/bootimgmigrate.go 86.53% 13 Missing and 1 partial ⚠️
pkg/pillar/cmd/zedkube/zedkube.go 0.00% 5 Missing ⚠️
pkg/pillar/hypervisor/kubevirt.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6100      +/-   ##
==========================================
+ Coverage   22.33%   22.78%   +0.45%     
==========================================
  Files         491      502      +11     
  Lines       91901    93704    +1803     
==========================================
+ Hits        20529    21354     +825     
- Misses      69699    70495     +796     
- Partials     1673     1855     +182     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Container-as-VM VMIRSes were embedding a versioned eve-external-boot-image
tag (e.g. :1.2.3) that gets deleted by cluster-init.sh cleanup on every
baseOS upgrade, permanently breaking virt-launcher pod scheduling on
upgraded nodes (ErrImageNeverPull, ImagePullPolicy: PullNever).

Fix by decoupling image lifecycle from VMIRS references:

- cluster-init.sh: tag the imported image as both :VERSION and :latest,
  and exclude :latest from cleanup_old_external_boot_images so it survives
  across upgrades. The :latest re-tag uses `ctr image tag --force`: the
  image store is persistent (/persist/vault/containerd), so :latest
  already exists from the prior version and a plain tag would fail with
  AlreadyExists — which, combined with the inspecti early-return guard,
  would leave :latest stuck on the old image permanently.

- kubevirt.go CreateReplicaVMIConfig: use :latest instead of reading
  /run/eve-release, so new VMIRSes never embed a version-specific tag.

- zedkube bootImgMigrator (new): one-shot state machine
  (WaitReady → Migrate → Done) that migrates existing VMIRSes still
  referencing a versioned tag. WaitReady polls independently for both
  eve-external-boot-image:latest in k3s containerd and the KubeVirt CR
  Available condition, and requires nodeName to be known before advancing
  (an empty nodeName would skip every VMIRS and latch to Done). Scoped to
  VMIRSes whose kubernetes.io/hostname node affinity matches the local
  node, so each node only patches its own apps. Once complete the event
  loop skips it (isDone) to avoid rebuilding a kubevirt client every tick.

  This races the descheduler in a rolling upgrade: an app can be
  rescheduled onto the upgraded node before its VMIRS is patched. step()
  collapses WaitReady → Migrate → Done into a single tick to shrink that
  window, and the case self-heals — checkStuckPendingVMI force-deletes the
  stuck Pending VMI and the ReplicaSet recreates it from the patched
  template.

Also documents the migration in docs/zedkube.md and normalizes a
pre-existing markdown table separator in the same file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrew Durbin <andrewd@zededa.com>
@andrewd-zededa
andrewd-zededa force-pushed the eve-k-external-boot-image-tag branch from 28ba07c to 7eff58a Compare July 23, 2026 18:25
@andrewd-zededa

Copy link
Copy Markdown
Contributor Author

rebased on latest master, moving to test next

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