Skip to content

o/devicemgmtstate,o/assertstate: add task to validate request messages - #17273

Merged
st3v3nmw merged 15 commits into
canonical:masterfrom
st3v3nmw:add-validate-mgmt-response-task
Jul 29, 2026
Merged

o/devicemgmtstate,o/assertstate: add task to validate request messages#17273
st3v3nmw merged 15 commits into
canonical:masterfrom
st3v3nmw:add-validate-mgmt-response-task

Conversation

@st3v3nmw

Copy link
Copy Markdown
Member

This PR implements the validate-mgmt-message task. Before a request message is handed off to a subsystem handler for processing, this task runs four checks: the message must target this specific device, the current time must fall within the message's validity window, any assumes constraints must be satisfied (TODO), and the subsystem handler's Validate method must pass.

Split from #16248.

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 snapd-side validation for incoming remote device-management request-message assertions, ensuring messages are targeted to the current device, fall within their validity window, and pass subsystem-specific validation before any subsystem Apply() work begins. This fits into the new device management manager pipeline by tightening the pre-dispatch gatekeeping step.

Changes:

  • Implement validate-mgmt-message task logic (device targeting, time window checks, handler lookup, and handler Validate() including unauthorized vs rejected outcomes).
  • Extend the persisted/request message model to include assumes, and introduce a device-identity provider dependency for obtaining the current device ID.
  • Add unit tests covering successful validation and the main rejection/unauthorized paths, plus idempotence for repeated validation failures.

Reviewed changes

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

File Description
overlord/overlord.go Updates manager construction to pass identity + signer dependencies into device management manager.
overlord/devicemgmtstate/export_test.go Exposes identity provider type and adds a test helper to mock the identity provider.
overlord/devicemgmtstate/devicemgmtmgr.go Implements doValidateMessage, adds Assumes to RequestMessage, and adds helpers for targeting/time validation plus an UnauthorizedError type.
overlord/devicemgmtstate/devicemgmtmgr_test.go Adds tests for the new validation task behavior and adjusts handler mocks accordingly.

Comment thread overlord/devicemgmtstate/devicemgmtmgr.go Outdated
Comment thread overlord/devicemgmtstate/devicemgmtmgr.go Outdated

s.mgr.MockIdentity(s.makeSerial(c, "other-serial"))

s.runner.AddHandler("queue-mgmt-response", noopTask, 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.

Copying in similar comment from #17254 (comment):

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.

@st3v3nmw
st3v3nmw force-pushed the add-validate-mgmt-response-task branch from 49fc933 to e88d9e9 Compare June 25, 2026 11:41
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Wed Jul 29 07:23:11 UTC 2026
The following results are from: https://github.com/canonical/snapd/actions/runs/30246231450

Failures:

Preparing:

  • openstack:ubuntu-26.10-64:tests/main/lxd-postrm-purge
  • openstack:ubuntu-26.10-64:tests/main/interfaces-posix-mq

Executing:

  • openstack:opensuse-tumbleweed-selinux-64:tests/main/manpages
  • openstack:opensuse-tumbleweed-64:tests/main/manpages
  • openstack:opensuse-tumbleweed-64:tests/main/services-watchdog
  • openstack:ubuntu-26.10-64:tests/main/upgrade-from-release
  • openstack:ubuntu-26.10-64:tests/main/xdg-settings
  • openstack:ubuntu-26.10-64:tests/main/interfaces-location-control
  • openstack:ubuntu-26.10-64:tests/main/bad-interfaces-warn
  • openstack:ubuntu-26.10-64:tests/main/broken-seeding
  • openstack:ubuntu-26.10-64:tests/main/lxd
  • openstack:ubuntu-26.10-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_deny_allow
  • openstack:ubuntu-26.10-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_allow_deny

Restoring:

  • openstack:ubuntu-26.10-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_deny_allow
  • openstack:ubuntu-26.10-64:tests/main/
  • openstack:ubuntu-26.10-64:
  • openstack:ubuntu-26.10-64:tests/main/apparmor-prompting-integration-tests:write_read_multiple_actioned_by_other_pid_allow_deny
  • openstack:ubuntu-26.10-64:tests/main/
  • openstack:ubuntu-26.10-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)

  • 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/apparmor-prompting-flag-restart
  • openstack:ubuntu-24.04-64:tests/main/apparmor-prompting-integration-tests
  • openstack:ubuntu-24.04-64:tests/main/apparmor-prompting-prompt-restoration
  • openstack:ubuntu-24.04-64:tests/main/apparmor-prompting-smoke
  • openstack:ubuntu-24.04-64:tests/main/apparmor-prompting-snapd-startup
  • openstack:ubuntu-24.04-64:tests/main/apparmor-prompting-support
  • openstack:ubuntu-24.04-64:tests/main/i18n
  • openstack:ubuntu-24.04-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

@st3v3nmw
st3v3nmw requested a review from miguelpires June 25, 2026 11:45
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.15301% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.88%. Comparing base (546cb6a) to head (e4db441).

Files with missing lines Patch % Lines
overlord/devicemgmtstate/devicemgmtmgr.go 83.95% 18 Missing and 8 partials ⚠️
overlord/assertstate/assertstate.go 85.71% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #17273      +/-   ##
==========================================
+ Coverage   78.81%   78.88%   +0.07%     
==========================================
  Files        1410     1396      -14     
  Lines      196929   196842      -87     
  Branches     2498     2498              
==========================================
+ Hits       155208   155281      +73     
+ Misses      32367    32210     -157     
+ Partials     9354     9351       -3     
Flag Coverage Δ
unittests 78.88% <84.15%> (+0.07%) ⬆️

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.

@st3v3nmw
st3v3nmw marked this pull request as draft June 25, 2026 12:46
@st3v3nmw
st3v3nmw force-pushed the add-validate-mgmt-response-task branch 4 times, most recently from ac24e24 to a247a29 Compare June 26, 2026 08:16
@st3v3nmw st3v3nmw changed the title o/devicemgmtstate: add task to validate request messages o/devicemgmtstate,o/assertstate: add task to validate request messages Jun 26, 2026
@st3v3nmw
st3v3nmw force-pushed the add-validate-mgmt-response-task branch from 54c350f to afc7389 Compare June 26, 2026 12:26
@st3v3nmw
st3v3nmw marked this pull request as ready for review June 26, 2026 12:26

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

Thanks. This looks ok overall, but the FetchAccountKeys call will panic. I also left a related question about testing

Comment thread overlord/devicemgmtstate/devicemgmtmgr.go
Comment thread overlord/devicemgmtstate/devicemgmtmgr.go Outdated
Comment thread overlord/devicemgmtstate/devicemgmtmgr_test.go Outdated
@st3v3nmw
st3v3nmw force-pushed the add-validate-mgmt-response-task branch 2 times, most recently from fe03449 to 8d23ccc Compare July 1, 2026 09:12
@st3v3nmw
st3v3nmw requested a review from miguelpires July 1, 2026 09:13
@miguelpires
miguelpires requested a review from pedronis July 1, 2026 10:41
@st3v3nmw
st3v3nmw force-pushed the add-validate-mgmt-response-task branch from d571f67 to a45a58d Compare July 1, 2026 13:36

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

Question about possible race when dispatching multiple sequences

return nil
}

defer m.setState(ms)

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.

Since FetchAccountKeys might release the lock, can't another sequence finish processing during that time and save state which this would then overwrite with the stale read?

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.

Yep, I've reproduced the issue & added state re-reads at the points where we might release the lock (8621f56). Right now there are 4 spots where that might happen:

  • doValidateMessage > FetchAccountKeys
  • doValidateMessage > handler.Validate (we pass st *state.State to the handler & the handler might unlock)
  • doApplyMessage > handler.Apply (same as above)
  • doExchangeMessages (since we can only have one device management change at a time, we can't have any other management tasks running when the store exchange task is running)

I've added re-reads after we re-acquire the lock to the first 3. The last one is guarded by the "only one device management change can be in flight at any given time" invariant we have. Not sure if there's a better solution short of breaking up the mgmt state by base message id 🤔.

Added 2 tests that reliably reproduce the race pre-fix.

@st3v3nmw
st3v3nmw requested a review from miguelpires July 9, 2026 08:41

@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 initial comments

Comment thread overlord/assertstate/assertstate.go Outdated
err := f.Fetch(ref)
if err != nil {
if errors.Is(err, &asserts.NotFoundError{}) {
logger.Noticef("cannot fetch account-key %q: %v", keyID, err)

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 you explain why are we ignoring this error?

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.

do other Fetch helper ignore errors like this?

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.

For device mgmt purposes, if we don't find the account-key, we fail at the signature check instead. But this doesn't make sense for a general helper, I've refactored this.

var unauthorizedErr *UnauthorizedError
status := asserts.MessageStatusRejected
if errors.As(err, &unauthorizedErr) {
status = asserts.MessageStatusUnauthorized

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.

in this case we control the error and combined with the reason below will produce an awkward error, maybe we should set a reason here directly, and use the generic reason below in a else?


msg := ms.Sequences["msg1"].Messages[0]
c.Check(msg.ResponseStatus, Equals, asserts.MessageStatusUnauthorized)
c.Check(msg.ResponseBody["message"], Equals, `cannot process message: cannot perform action: operator "alice" is not authorized`)

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.

the error is a bit awkward as mentioned

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.

Fixed.

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

sorry, I forgot to submit this review yesterday 🤦‍♂️

Comment on lines +580 to +732
msg.ResponseBody = map[string]any{"message": err.Error()}
msg.ResponseBody = map[string]any{"message": fmt.Sprintf("cannot process message: %v", applyErr)}

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.

maybe it's better to leave this as it was, otherwise we'll get those cannot x: cannot y: ... errors

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.

Fixed.

Comment on lines +1760 to +1769
// Delay whichever lane reaches this call first: it releases the
// lock and sleeps, giving the other lane time to acquire the
// lock, run to completion, and persist its own write. When this
// lane resumes and re-acquires the lock, it must not overwrite
// that write with its own stale snapshot.
if applyCalls.Add(1) == 1 {
st.Unlock()
time.Sleep(100 * time.Millisecond)
st.Lock()
}

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 a bit brittle. There's no guarantee that the other task runs at this point. You can achieve the same with more control by manually mutating the state, or maybe using AddTaskStatusChangedHandler but that seems complicated.

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 refactored this so we're sure that the other lane is done before we continue.

// run to completion, and persist its own write. When this lane
// resumes and re-acquires the lock, it must not overwrite that
// write with its own stale snapshot.
if fetchCalls.Add(1) == 1 {

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 comment

Comment on lines 1661 to +1688
s.mockStore(func(_ context.Context, _ *store.MessageExchangeRequest) (*store.MessageExchangeResponse, error) {
return &store.MessageExchangeResponse{
Messages: []store.MessageWithToken{
s.makeStoreRequestMessage(c, "msg1", "unknown-kind", "token-1"),
},
}, nil
return &store.MessageExchangeResponse{}, nil
})

s.runner.AddHandler("queue-mgmt-response", noopTask, nil)
ms := &devicemgmtstate.DeviceMgmtState{
Sequences: map[string]*devicemgmtstate.SequenceState{
"msg1": {
Messages: []*devicemgmtstate.RequestMessage{
{
AccountID: "my-brand",
AuthorityID: "my-brand",
BaseID: "msg1",
Kind: "unknown-kind",
Devices: []string{"serial-1.my-model.my-brand"},
ValidSince: fixedTestTime,
ValidUntil: fixedTestTime.Add(24 * time.Hour),
Body: `{"action": "get"}`,
},
},
},
},
}
s.mgr.SetState(ms)

chg := s.st.NewChange("test", "test change")
t := s.st.NewTask("apply-mgmt-message", "apply message with unknown kind")
t.Set("message-id", "msg1")
chg.AddTask(t)

@st3v3nmw st3v3nmw Jul 15, 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.

Refactored this to run the task directly. The validate task now rejects messages with unknown kinds, so we wouldn't reach apply's check with .settle.

@st3v3nmw
st3v3nmw force-pushed the add-validate-mgmt-response-task branch from 2ad0445 to ba12532 Compare July 15, 2026 08:55
@st3v3nmw
st3v3nmw requested a review from miguelpires July 15, 2026 09:01

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

Couple of quick comments. There's also a unit test failure in:

FAIL: devicemgmtmgr_test.go:1440: deviceMgmtMgrSuite.TestDoValidateMessageIdempotent

devicemgmtmgr_test.go:1479:
    c.Check(validateCalls, Equals, 1)
... obtained int = 3
... expected int = 1

Comment on lines +616 to +624
setMsgResponse := func(status asserts.MessageStatus, message string) error {
msg.ResponseStatus = status
msg.ResponseBody = map[string]any{"message": message}
m.setState(ms)
return nil
}
rejectMsg := func(reason string) error {
return setMsgResponse(asserts.MessageStatusRejected, reason)
}

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.

I'm a bit lukewarm on passing through the error (always nil) from these calls. Reading the call sites, it looks like the error could be non-nil. It would be more explicit not to return an error here and to return nil at each site. If we keep this, we should leave a comment here flagging this and why it's always 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.

Should be explicit now.


var fetchCalls atomic.Int32
s.AddCleanup(devicemgmtstate.MockFetchAccountKey(func(st *state.State, _ int, _ string) error {
if fetchCalls.Add(1) == 1 {

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 don't need the atomic int anymore I think?

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.

Fixed, switched to a normal bool.

}, nil
})

var validateCalls atomic.Int32

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

@st3v3nmw

Copy link
Copy Markdown
Member Author

There's also a unit test failure in:

FAIL: devicemgmtmgr_test.go:1440: deviceMgmtMgrSuite.TestDoValidateMessageIdempotent

devicemgmtmgr_test.go:1479:
c.Check(validateCalls, Equals, 1)
... obtained int = 3
... expected int = 1

The test was flaky because assertstate.FetchAccountKey drops the state lock internally, which allowed the other concurrent tasks to slip past the idempotency guard. This wouldn't happen in production because the dispatch code ensures only one validate task per message exists at any given time, so the concurrency in this test is artificial.

Since this idempotency test checks that re-runs of the same task aren't doing duplicate work, I've opted to mock assertstate.FetchAccountKey with a no-op rather than adding WaitFor dependencies between the tasks.

@st3v3nmw
st3v3nmw requested a review from miguelpires July 16, 2026 12:42
miguelpires
miguelpires previously approved these changes Jul 17, 2026

@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

Comment on lines +633 to +635
// TODO: need to distinguish between:
// - transient errors (like store unreachable) - retry
// - permanent errors - determine whether to fail the task or reject the message.

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.

If you haven't yet, don't forget to file a ticket to follow up on this

@st3v3nmw st3v3nmw Jul 20, 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.

Yes, I created one 😅.. Thanks for the reviews!!

@miguelpires
miguelpires requested a review from pedronis July 17, 2026 16:13

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

couple of questions

Comment thread overlord/devicemgmtstate/devicemgmtmgr.go
return nil
}

err = assertstateFetchAccountKey(m.state, 0, a.SignKeyID())

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 will always consult the store, should we check somehow first if we have the key already locally?

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 fixed this.

@st3v3nmw
st3v3nmw requested a review from pedronis July 24, 2026 09:01
@pedronis
pedronis requested a review from Copilot July 24, 2026 11:06
@pedronis
pedronis dismissed miguelpires’s stale review July 24, 2026 11:06

needs a re-review because of the latest change

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

+1 but testing question, also I asked @miguelpires to review again

})

// Remove the account key from the local DB so ensureAccountKey reaches the fetch.
db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{

@pedronis pedronis Jul 24, 2026

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.

do we have a test where it is not removed?

@st3v3nmw st3v3nmw Jul 24, 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.

Yes, TestDoValidateMessageOK and the idempotency & concurrency tests work with an account key that exists in the db set up by SetUpTest (success cases).

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines +813 to +819
// handler.ResultFromChange may drop the state lock internally. Concurrent tasks
// in other lanes may have mutated the state in that window, so re-read before mutating.
ms, _, err = m.getMessageAndState(msgID)
if err != nil {
return err
}

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.

Fixed.

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

thanks!

@github-actions github-actions Bot 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 24, 2026
@st3v3nmw

Copy link
Copy Markdown
Member Author

Confirmed that spread test failures are unrelated to these changes:

Copilot summary:

  • The CI job for PR o/devicemgmtstate,o/assertstate: add task to validate request messages #17273 ran spread integration tests on openstack:ubuntu-26.10-64 and recorded 10 test failures spanning unrelated areas (apparmor prompting, lxd, interfaces, seeding, xdg-settings, etc.), which strongly suggests a flaky or broken environment on that specific Ubuntu 26.10 OpenStack image rather than a regression introduced by the PR's changes (which touch only overlord/assertstate and overlord/devicemgmtstate). On top of that, the job itself exited with a hard error because the external Test-Predictor service returned an HTTP 502 Bad Gateway when the workflow tried to upload results, causing the final step to fail with exit code 1. Both issues appear to be infrastructure/environment problems unrelated to the code changes in this PR, and a re-run should resolve them.
  • The failing job ran spread integration tests on openSUSE Tumbleweed (both standard and SELinux variants) and encountered 3 test failures: tests/main/manpages on two openSUSE systems and tests/main/services-watchdog on one. These failures are unrelated to the PR's actual code changes, which only touch Go source files in overlord/assertstate and overlord/devicemgmtstate. The manpages test uses a man -u --where workaround for an openSUSE-specific man cache bug, which appears to be failing on the current Tumbleweed environment. Additionally, a secondary workflow step ("Send results to Test-Predictor") failed with an HTTP 502 from the test predictor service, though this step is continue-on-error and didn't cause the job failure itself. Overall, these look like pre-existing flaky infrastructure/environment issues on openSUSE Tumbleweed, not regressions introduced by this PR — re-running the job is the recommended first step.

@st3v3nmw
st3v3nmw merged commit 23da9ba into canonical:master Jul 29, 2026
901 of 932 checks passed
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