Skip to content

o/devicemgmtstate: add task to queue response messages - #17254

Merged
st3v3nmw merged 9 commits into
canonical:masterfrom
st3v3nmw:add-queue-mgmt-response-task-2
Jul 14, 2026
Merged

o/devicemgmtstate: add task to queue response messages#17254
st3v3nmw merged 9 commits into
canonical:masterfrom
st3v3nmw:add-queue-mgmt-response-task-2

Conversation

@st3v3nmw

Copy link
Copy Markdown
Member

This PR implements the queue-mgmt-response task. Once a message has been applied (or rejected before reaching the subsystem), this task builds a signed response-message and queues it for transmission on the next store exchange.

For messages with a subsystem change, the task retries until the change completes, then calls the handler's ResultFromChange to produce the response body. For messages that were rejected or not authorized before dispatch, the response is built directly from ResponseStatus & ResponseBody in the RequestMessage.

Split from #16248, replaces #16656.

Copilot AI 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.

Pull request overview

This PR adds support for the queue-mgmt-response task in overlord/devicemgmtstate, completing the message-processing pipeline by generating signed response-message assertions and queueing them for transmission on the next Store message exchange.

Changes:

  • Implemented queue-mgmt-response task logic to build response bodies (from completed subsystem changes or pre-set rejection/authorization outcomes), sign them, and store them in ReadyResponses.
  • Added state helper to remove processed request messages from a sequence while preserving sequence progress.
  • Expanded unit tests to cover successful response queuing, idempotency, retry behavior while subsystem changes are incomplete, and signing/error scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
overlord/devicemgmtstate/devicemgmtmgr.go Implements response queuing/signing, adds request-message removal helper, and wires the new task into dispatch chains.
overlord/devicemgmtstate/devicemgmtmgr_test.go Adds coverage for queue-mgmt-response and updates existing tests to account for the new task in message chains.

Comment thread overlord/devicemgmtstate/devicemgmtmgr.go
Comment thread overlord/devicemgmtstate/devicemgmtmgr.go
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.54217% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.85%. Comparing base (5f9a899) to head (cff4064).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
overlord/devicemgmtstate/devicemgmtmgr.go 85.54% 8 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #17254      +/-   ##
==========================================
- Coverage   78.86%   78.85%   -0.02%     
==========================================
  Files        1401     1401              
  Lines      195954   196081     +127     
  Branches     2466     2466              
==========================================
+ Hits       154543   154620      +77     
- Misses      32138    32174      +36     
- Partials     9273     9287      +14     
Flag Coverage Δ
unittests 78.85% <85.54%> (-0.02%) ⬇️

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

☔ 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.

}, nil
})

s.runner.AddHandler("queue-mgmt-response", func(*state.Task, *tomb.Tomb) error { return nil }, nil)

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.

The no-op s.runner.AddHandler("queue-mgmt-response", ...) overrides in the pre-existing tests are there because those tests assert on intermediate state (that a message was dispatched, validated, or applied correctly) before doQueueResponse runs and removes it from the sequence. Now that doQueueResponse is implemented and registered by the manager, .settle runs it to completion, which removes the RequestMessage from state and breaks the assertions on ms.Sequences[...].Messages.

The no-op handler suppresses the real task keeping tests focused & their assertions meaningful.

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.

Just pinging @pedronis to bring it to his attention. I think this makes sense, it enables testing intermediate state and those tests still run most of the change, just not the response/removal bit.

@st3v3nmw
st3v3nmw force-pushed the add-queue-mgmt-response-task-2 branch from 420d0ee to 21896f3 Compare June 24, 2026 09:09
@st3v3nmw
st3v3nmw requested a review from miguelpires June 24, 2026 09:09
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Wed Jul 1 13:53:01 UTC 2026
The following results are from: https://github.com/canonical/snapd/actions/runs/28521330602

No spread failures reported

@st3v3nmw
st3v3nmw force-pushed the add-queue-mgmt-response-task-2 branch 2 times, most recently from 4ffb3fe to b66865c Compare June 24, 2026 10:44
@st3v3nmw
st3v3nmw force-pushed the add-queue-mgmt-response-task-2 branch from b66865c to d7e0cf0 Compare June 24, 2026 10:50
@st3v3nmw
st3v3nmw marked this pull request as draft June 24, 2026 14:42
@st3v3nmw
st3v3nmw marked this pull request as ready for review June 24, 2026 15:41
@st3v3nmw
st3v3nmw marked this pull request as draft June 25, 2026 08:39

@miguelpires miguelpires 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

@st3v3nmw
st3v3nmw requested a review from pedronis June 28, 2026 11:24
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Mon Jul 13 17:54:51 UTC 2026
The following results are from: https://github.com/canonical/snapd/actions/runs/29240595071

Failures:

Preparing:

  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack-arm:ubuntu-24.04-arm-64:tests/main/interfaces-gpio-control

Restoring:

  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-64:
  • openstack:opensuse-tumbleweed-selinux-64:
  • openstack:opensuse-tumbleweed-selinux-64:

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 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:debian-sid-64:tests/main/interfaces-network-status-classic
  • openstack:debian-sid-64:tests/main/interfaces-xdg-portal-permission-store
  • 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
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-prompt-restoration
  • openstack:ubuntu-25.10-64:tests/main/apparmor-prompting-smoke
  • 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
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-prompt-restoration
  • openstack:ubuntu-26.04-64:tests/main/apparmor-prompting-smoke
  • 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

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

qq about some error handling code paths

s.st.Unlock()
err := s.mgr.DoQueueResponse(t, &tomb.Tomb{})
s.st.Lock()
c.Assert(err, ErrorMatches, `cannot find subsystem change "16384"`)

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.

Just to clarify, if this happens we're basically stuck right? This sequence will just stick around until it gets pushed out of the state due to a global limit

@st3v3nmw st3v3nmw Jul 3, 2026

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.

@miguelpires, yes. I have a TODO in doQueueResponse to handle this after this PR & maybe #17273 land:

	// TODO: rejecting sequences currently happens in 2 ways:
	// 1. doDispatchMessage can evict the sequence immediately if it's rejected early.
	// 2. If it errors elsewhere (in validate, apply, or queue-response), we end
	//    up not advancing Applied, which means we accumulate messages until we
	//    hit the sequence cap.
	// Refactor sequence rejection to always evict immediately and determine the
	// right behavior for internal errors.

I can do it in this PR but I think it makes sense to land this first because I'll have to refactor the dispatching code that already does rejection and make sure all rejection/error paths in all tasks/handlers reject the sequences & messages immediately instead of waiting for the cap.

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.

Yes, definitely lets land this first. We can deal with that in its own PR. I was just thinking about the devicemgmtmgr's behaviour on error and wanted to check my understanding. Thanks

queueTask := ti.queue["mesg-1"]
c.Assert(queueTask, NotNil)
c.Check(queueTask.Status(), Equals, state.ErrorStatus)
c.Check(strings.Join(queueTask.Log(), "\n"), testutil.Contains, "cannot sign response message: device key not found")

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.

Same with this; if we get this error, the change fails and the sequence is stuck?

@st3v3nmw

st3v3nmw commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Hoping we land this first, then #17273, and finally #17039.

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

thanks, some comments

// up not advancing Applied, which means we accumulate messages until we
// hit the sequence cap.
// Refactor sequence rejection to always evict immediately and determine the
// right behavior for internal errors.

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.

we definitely need to see what to do for internal errors?

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.

also the comment makes it sound like it applies only to sequences, but reasonable behavior for internal error is also important for not-sequenced messages

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.

we definitely need to see what to do for internal errors?

Yes. Since tasks are idempotent, a failed message will not be re-dispatched or re-applied on the next change, but the request message remains in state until doQueueResponse completes, so such failures leave it hanging indefinitely.

also the comment makes it sound like it applies only to sequences, but reasonable behavior for internal error is also important for not-sequenced messages

I've fixed it to be clearer.


change := m.state.Change(msg.ApplyChangeID)
if change == nil {
return fmt.Errorf("cannot find subsystem change %q", msg.ApplyChangeID)

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 seems also an internal error, in which scenario would this happen?

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've marked it as internal error: .... The branch is a defensive guard in the rare case the change is not in state anymore. For instance, if a ready subsystem change is pruned before this task fires. I'm looking at the pruning code to see if this is even possible.. say if the computer is shut down mid-device-management-change and then booted after a week.

s.st.Unlock()
err := s.mgr.DoQueueResponse(t, &tomb.Tomb{})
s.st.Lock()
c.Assert(err, FitsTypeOf, &state.Retry{})

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.

would be probably be good to then set the change to Done and check run again in this one test

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.

Done.

@st3v3nmw
st3v3nmw force-pushed the add-queue-mgmt-response-task-2 branch from ae71431 to cff4064 Compare July 13, 2026 09:50
@st3v3nmw
st3v3nmw requested a review from pedronis July 13, 2026 11:26

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

thanks, a local LLM review made this comment, seems more material possibly for a follow up?

Missing test coverage for completed/replayed messages after removal. The branch now removes processed requests from Sequences, but duplicate coverage only exercises duplicates while the request is still pending: devicemgmtmgr_test.go:544-567. I’d add tests for a duplicate sequenced message with Applied already past it, and for an unsequenced duplicate after its response was queued/sent. If the store contract guarantees those cannot happen after LastReceivedToken, the test can document that assumption at the exchange boundary.

@st3v3nmw st3v3nmw added the Auto rerun spread Auto reruns spread up to 4 times in non-draft PRs w/ >=1 approval and <20 fails in any fund. system label Jul 13, 2026
@st3v3nmw

Copy link
Copy Markdown
Member Author

a local LLM review made this comment, seems more material possibly for a follow up?

I'll follow up with some tests to improve the coverage. Thanks!

@st3v3nmw

Copy link
Copy Markdown
Member Author

Spread failures are unrelated to these changes, Copilot summary:

https://github.com/canonical/snapd/actions/runs/29240595071/job/86889028970:

The job ran spread tests on openstack:opensuse-tumbleweed-selinux-64 and recorded widespread failures across smoke tests (install, find-info, remove, sandbox, versioning) and numerous regression tests — far too broad to be caused by the two changed files (devicemgmtmgr.go and its test). This points to a transient infrastructure or environment issue on that system. The job then failed hard (exit code 1) in the post-test reporting step: the gh CLI returned an HTTP 502 when fetching the job ID, resulting in a malformed value ({) being sent to the Test Predictor API, which rejected it with HTTP 400. The actual spread test failures are likely noise from an unstable OpenSUSE Tumbleweed SELinux image, and the exit-code-1 failure is purely a reporting-step issue with no safeguard against a bad gh API response.

https://github.com/canonical/snapd/actions/runs/29240595071/job/86889028802:

The failing job runs the spread test tests/main/interfaces-gpio-control on an ARM runner (openstack-arm:ubuntu-24.04-arm-64). The test's prepare step tries to load the gpio-sim kernel module via modprobe after installing linux-modules-extra, but the module is simply not present in the ARM kernel (6.8.0-134-generic), causing a fatal error and aborting the test. This is a known gap — the task.yaml already has TODO comments noting that ARM support isn't ready yet — but the systems filter only excludes Ubuntu Core ARM, not the regular ubuntu-24.04-arm-64 system the job ran on. The fix is straightforward: change systems: [ubuntu-24.04-*] to systems: [ubuntu-24.04-64] to exclude ARM runners until the gpio-sim module is available on that platform.

@st3v3nmw
st3v3nmw merged commit ec4b245 into canonical:master Jul 14, 2026
469 of 496 checks passed
@st3v3nmw
st3v3nmw deleted the add-queue-mgmt-response-task-2 branch July 14, 2026 07:07
ZeyadYasser pushed a commit to ZeyadYasser/snapd that referenced this pull request Jul 28, 2026
* o/devicemgmtstate: add task to queue response messages

* o/devicemgmtstate: test that a task chain error does not affect other messages

* o/devicemgmtstate: set error message for unknown message kind in setMessageResponseFromChange

* o/devicemgmtstate: do not advance Applied on non-success response

* o/devicemgmtstate: add reuseable noopTask in tests

* o/devicemgmtstate: improve error messages

* o/devicemgmtstate: fixes after review
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants