Skip to content

o/snapstate: only create link-component tasks when needed - #16882

Merged
andrewphelpsj merged 1 commit into
canonical:masterfrom
andrewphelpsj:no-link-if-linked
May 7, 2026
Merged

o/snapstate: only create link-component tasks when needed#16882
andrewphelpsj merged 1 commit into
canonical:masterfrom
andrewphelpsj:no-link-if-linked

Conversation

@andrewphelpsj

@andrewphelpsj andrewphelpsj commented Apr 8, 2026

Copy link
Copy Markdown
Member

This resolves an issue that happens on the undo path of a refresh to an already existing snap revision that happened to have some components.

Assume this sequence in the state:

snap-a+one+two at revision 1 <- previous
snap-a+one+two at revision 2 <- current

Then, the user refreshes snap-a+one+two back to the still-local revision 1:

$ snap refresh snap-a --revision=1

In that scenario, we were creating link-component tasks for the already linked components one and two. The undo of this refresh would result in the sequence:

snap-a         at revision 1 <- previous, missing components because of undone link-component tasks
snap-a+one+two at revision 2 <- current

@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown

Thu May 7 14:26:45 UTC 2026
The following results are from: https://github.com/canonical/snapd/actions/runs/25388989716

Failures:

Preparing:

  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:

Executing:

  • openstack:debian-sid-64:tests/unit/go:static
  • openstack:opensuse-tumbleweed-selinux-64:tests/main/selinux-classic-confinement
  • openstack-arm:ubuntu-24.04-arm-64:tests/main/apparmor-prompting-flag-restart

Restoring:

  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack:opensuse-15.6-64:
  • openstack-arm:ubuntu-24.04-arm-64:tests/main/apparmor-prompting-flag-restart

Skipped tests from snapd-testing-skip

If you wish to have any of the below tests run in your PR, in your PR description, add 'unskip:' followed by a copy-and-pasted list (without variants) of the below tests you wish to run (unskip plus test list must be valid yaml)

  • garden:ubuntu-25.10-64:tests/main/apparmor-prompting-support
  • openstack-arm:ubuntu-24.04-arm-64:tests/main/i18n
  • openstack-arm:ubuntu-core-24-arm-64:tests/main/i18n
  • openstack:centos-9-64:tests/main/selinux-clean
  • openstack:debian-sid-64:tests/main/interfaces-network-status-classic
  • openstack:fedora-42-64:tests/main/selinux-clean
  • openstack:ubuntu-24.04-64:tests/main/i18n
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-flag-restart
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_multiple_actioned_by_other_pid_always_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_multiple_actioned_by_other_pid_always_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_multiple_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_multiple_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_multiple_not_actioned_by_other_pid_single_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_multiple_not_actioned_by_other_pid_single_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_write_chmod_same_fd_single_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_write_chmod_same_path_single_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:create_write_write_same_path_single_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:download_file_conflict
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:download_file_defaults
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:download_file_safer
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:read_single_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:read_single_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:special_characters
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:timespan_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:timespan_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_allow_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_deny_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:write_single_allow
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-integration-tests:write_single_deny
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-prompt-restoration
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_forever
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_session
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_single
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_timespan
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_forever
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_session
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_single
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_timespan
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_allow_forever
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_allow_session
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_allow_single
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_allow_timespan
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_deny_forever
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_deny_session
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_deny_single
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:camera_deny_timespan
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_allow_forever
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_allow_session
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_allow_single
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_allow_timespan
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_deny_forever
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_deny_session
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_deny_single
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke:home_deny_timespan
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-snapd-startup
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-support
  • openstack:ubuntu-25.10-64:tests/main/interfaces-requests-activates-handlers
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-flag-restart
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_actioned_by_other_pid_always_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_actioned_by_other_pid_always_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_not_actioned_by_other_pid_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_multiple_not_actioned_by_other_pid_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_write_chmod_same_fd_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_write_chmod_same_path_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:create_write_write_same_path_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:download_file_conflict
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:download_file_defaults
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:download_file_safer
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:read_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:read_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:special_characters
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:timespan_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:timespan_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_allow_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_deny_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_single_allow
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-integration-tests:write_single_deny
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-prompt-restoration
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_allow_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:audiorecord_deny_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_allow_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:camera_deny_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_allow_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_forever
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_session
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_single
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke:home_deny_timespan
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-snapd-startup
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-support
  • openstack:ubuntu-26.04-64:tests/main/i18n
  • openstack:ubuntu-26.04-64:tests/main/interfaces-requests-activates-handlers

@codecov

codecov Bot commented Apr 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.05%. Comparing base (4620040) to head (60533d3).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #16882      +/-   ##
==========================================
- Coverage   79.06%   79.05%   -0.01%     
==========================================
  Files        1374     1381       +7     
  Lines      191076   191188     +112     
  Branches     2465     2465              
==========================================
+ Hits       151065   151148      +83     
- Misses      30918    30941      +23     
- Partials     9093     9099       +6     
Flag Coverage Δ
unittests 79.05% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

@andrewphelpsj

Copy link
Copy Markdown
Member Author

This is rebased off of master now, and ready for review.

@andrewphelpsj
andrewphelpsj requested a review from pedronis April 14, 2026 13:20

@alfonsosanchezbeato alfonsosanchezbeato 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.

Thanks, couple of questions

Comment thread overlord/snapstate/component.go Outdated
return false
}

return cs.CompType == cc.compsup.CompType && *cs.SideInfo == *cc.compsup.CompSideInfo

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.

Is it really necessary to consider here the type as well? It seems that CompSideInfo would be unique already.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, prob don't need it. Removed.

Comment thread overlord/snapstate/component.go Outdated
Comment on lines +461 to +464
// when refreshing to the current snap revision, component refreshes
// rely on unlink-current-component/link-component. thus, undo can
// restore the old component revision cleanly.
return false

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.

Question, but is then necessary to have the link/unlink component task? Or is it just harmless, but unneeded?

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.

this could be installing a new component as well? and then is needed, maybe the comment should also mention the install POV and not just the refresh POV?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I updated the comments here, hopefully better now?

@pedronis pedronis 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.

did a pass, couple of comments and questions

Comment thread overlord/snapstate/component.go
Comment thread overlord/snapstate/component.go
if !cc.snapsup.Revert {
// finalize (sets SnapState). if we're reverting, there isn't anything to
// change in SnapState regarding the component
if !cc.targetSnapAlreadyHasComponentRevision() {

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.

is this equivalent also to !snapup.Revert because for the revert case we have made sure the revision is present already at this stage?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, this condition is essentially a super-set of the original revert condition.

Comment thread overlord/snapstate/component.go Outdated
Comment on lines +461 to +464
// when refreshing to the current snap revision, component refreshes
// rely on unlink-current-component/link-component. thus, undo can
// restore the old component revision cleanly.
return false

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.

this could be installing a new component as well? and then is needed, maybe the comment should also mention the install POV and not just the refresh POV?

@alfonsosanchezbeato alfonsosanchezbeato 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, thanks for the changes

@pedronis pedronis 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.

thank you

@andrewphelpsj andrewphelpsj added Run nested The PR also runs tests inluded in nested suite Auto rerun spread Auto reruns spread up to 4 times in non-draft PRs w/ >=1 approval and <20 fails in any fund. system labels Apr 16, 2026
@andrewphelpsj andrewphelpsj reopened this Apr 16, 2026
@andrewphelpsj
andrewphelpsj force-pushed the no-link-if-linked branch 2 times, most recently from c64f4f8 to 442dc65 Compare May 1, 2026 13:33
This resolves an issue that happens on the undo path of a refresh to an
already existing snap revision that happened to have some components.

Assume this sequence in the state:

snap-a+one+two at revision 1 <- previous
snap-a+one+two at revision 2 <- current

Then, the user refreshes snap-a+one+two back to the still-local revision 1:

$ snap refresh snap-a --revision=1

In that scenario, we were creating link-component tasks for the already
linked components one and two. The undo of this refresh would result in
the sequence:

snap-a at revision 1         <- previous, missing components because of undone link-component tasks
snap-a+one+two at revision 2 <- current
@andrewphelpsj
andrewphelpsj merged commit 077a1e9 into canonical:master May 7, 2026
792 of 826 checks passed
@andrewphelpsj
andrewphelpsj deleted the no-link-if-linked branch May 7, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto rerun spread Auto reruns spread up to 4 times in non-draft PRs w/ >=1 approval and <20 fails in any fund. system Run nested The PR also runs tests inluded in nested suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants