Skip to content

client, test/suites: forward session timeout cause over websocket - #1519

Open
shardool-patil wants to merge 2 commits into
canonical:mainfrom
shardool-patil:fix/session-timeout-forwarding
Open

client, test/suites: forward session timeout cause over websocket#1519
shardool-patil wants to merge 2 commits into
canonical:mainfrom
shardool-patil:fix/session-timeout-forwarding

Conversation

@shardool-patil

Copy link
Copy Markdown

Description

When a trust establishment session expires, the client previously received a generic connection error (websocket: close 1006: unexpected EOF) instead of the actual cause (Session timeout exceeded)[cite: 5].

Changes

  • client/websocket.go:
    • Updated WriteClose to write ControlClose directly over the WebSocket under writeLock so close frames are sent even when gwCtx is canceled on timeout[cite: 5].
    • Updated ReceiveWithContext to check channel closure and return context.Cause(w.ctx)[cite: 5].
  • test/suites/basic.sh:
    • Added a test case in test_interactive with SESSION_TIMEOUT="5" verifying that both the initiator and joiner receive "Session timeout exceeded"[cite: 2].

Checklist

Related issues

@roosterfish i have taken into consideration the insights you provided and resolved the issue, so take a look at it when you get time.

@roosterfish roosterfish 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 for taking a look at this, please see my comments on the test.

Comment thread test/suites/basic.sh Outdated
Comment thread test/suites/basic.sh Outdated
Comment thread test/suites/basic.sh Outdated

Copilot AI left a comment

Copy link
Copy Markdown

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 improves MicroCloud’s WebSocket session handling so that when a trust establishment session expires, the client receives the real cancellation cause (e.g., “Session timeout exceeded”) rather than a generic abnormal WebSocket EOF error, aligning behavior with issue #1245.

Changes:

  • Adjusts the WebSocket gateway close-path to reliably send a ControlClose message even when the gateway context is canceled.
  • Improves receive behavior to surface the gateway context cause when the receive channel closes.
  • Adds a system test intended to validate that both initiator and joiner surface the timeout error.

Reviewed changes

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

File Description
client/websocket.go Ensures close-cause forwarding works reliably and improves receive-side error reporting when the socket closes.
test/suites/basic.sh Adds an interactive test case intended to validate session-timeout error propagation to initiator/joiner.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/suites/basic.sh Outdated
Comment thread test/suites/basic.sh Outdated
Comment thread client/websocket.go Outdated
@shardool-patil

Copy link
Copy Markdown
Author

@roosterfish i have addressed all the requested changes:

  • test/suites/basic.sh: Set EXPECT_PEERS=2, clarified the test comment and echo message to verify both initiator and joiner, and added the missing EOF newline[cite: 2, 3].
  • test/includes/microcloud.sh: Updated microcloud_interactive to pass ${SESSION_TIMEOUT:-60} dynamically and added SESSION_TIMEOUT to unset_interactive_vars[cite: 2].

take a look whenever possible

@shardool-patil
shardool-patil force-pushed the fix/session-timeout-forwarding branch from b287951 to 4ffe082 Compare August 21, 2026 11:28
@roosterfish

Copy link
Copy Markdown
Contributor

See the test failure https://github.com/canonical/microcloud/actions/runs/32477357735/job/96766897819?pr=1519#step:3:45500.
I suspect what is happening is that the session expires too early now. Maybe let's leave the 60s default (no need to make it configurable in this case).

@roosterfish
roosterfish marked this pull request as draft August 21, 2026 15:41
@shardool-patil
shardool-patil force-pushed the fix/session-timeout-forwarding branch from 4ffe082 to 14a4c0b Compare August 21, 2026 16:41
@shardool-patil

Copy link
Copy Markdown
Author

See the test failure https://github.com/canonical/microcloud/actions/runs/32477357735/job/96766897819?pr=1519#step:3:45500. I suspect what is happening is that the session expires too early now. Maybe let's leave the 60s default (no need to make it configurable in this case).

@roosterfish I have worked and handled the requested changes so take a look at the code.

@roosterfish
roosterfish marked this pull request as ready for review August 24, 2026 11:42
@shardool-patil
shardool-patil force-pushed the fix/session-timeout-forwarding branch from 14a4c0b to c3f95c6 Compare August 25, 2026 02:27
@shardool-patil

Copy link
Copy Markdown
Author

@roosterfish I switched the test to reset_systems 3 0 0 because with reset_systems 2 0 0, micro03 is paused by default. Since EXPECT_PEERS=2 triggers table:expect 2, micro01 hangs during mDNS peer discovery waiting for a nonexistent second peer before the session ever starts. Running reset_systems 3 0 0 allows micro01 to discover both peers, select them, connect with micro02, and wait for micro03 until the session timeout triggers. Does reset_systems 3 0 0 work for you here?

@shardool-patil
shardool-patil force-pushed the fix/session-timeout-forwarding branch from c3f95c6 to e98fe8e Compare August 25, 2026 16:16
@shardool-patil

Copy link
Copy Markdown
Author

@roosterfish I've updated test/suites/basic.sh:

  • Switched to reset_systems 3 0 0 and disabled storage setup (SETUP_ZFS="no", SETUP_CEPH="no").
  • Kept EXPECT_PEERS=2 with join_session init micro01 micro02 so micro01 connects with micro02 and waits for micro03 until the default 60s session timeout triggers.
    With reset_systems 2 0 0, micro03 is paused by default, causing micro01 to hang during mDNS peer discovery (table:expect 2) before the session can start. 3 0 0 keeps micro03 running so discovery succeeds without configuring any unused storage devices.

Please take a look at this setup before re-running the tests.

Comment thread client/websocket.go Outdated
// Send close control message.
// Try to send the cause from the outer context if present.
_ = gw.WriteClose(context.Cause(gwCtx))
cause := context.Cause(gwCtx)

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.

Don't we always have to write the close message? From what I remember it's important that it's of type ControlClose, but we don't have to populate the ControlMessage if there isn't any 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.

Here we will only ever send the control message if context.Cause returns a non-nil error.

Comment thread client/websocket.go Outdated
// It waits for the other side to hang up or the gateway's context being cancelled.
func (w *WebsocketGateway) WriteClose(err error) error {
writeErr := w.Write(ControlClose{
w.writeLock.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.

Why the extra lock? By invoking w.Write, it already obtains the lock for us. I don't see why we have to use w.conn.WriteJSON directly here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

thanks for pointing that out, i have removed the manual lock and reverted to write close

Comment thread test/includes/microcloud.sh Outdated
set -eu
if [ "${SKIP_SETUP_LOG}" = 1 ]; then
exec > /dev/null 2>&1
exec > /dev/null

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.

Why is this necessary?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

this was an accidental edit left from local debugging, reverted microcloud.sh back to main.

Comment thread test/includes/microcloud.sh Outdated
pkill -f \"ceph-osd .* --id \${osd}\"
microceph.ceph osd purge \${osd} --yes-i-really-mean-it --force
microceph.ceph osd destroy \${osd} --yes-i-really-mean-it --force
microceph.ceph osd purge \${osd} --yes-i-really-really-mean-it --force

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.

Why is this necessary? According to ceph osd purge -h it's --yes-i-really-mean-it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

same here.

@shardool-patil
shardool-patil force-pushed the fix/session-timeout-forwarding branch 2 times, most recently from e0eafdf to e5601b6 Compare August 26, 2026 15:48
@roosterfish
roosterfish requested a lite review from Copilot August 26, 2026 15:59
Comment thread test/suites/basic.sh
lxc exec micro01 -- tail -1 out | grep "User aborted" -q
lxc exec micro04 -- tail -1 out | grep "Failed waiting during join: Initiator aborted the setup" -q

# Initiate a MicroCloud cluster with a session timeout and verify both initiator and joiner receive "Session timeout exceeded".

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.

Please make sure the changes are separated into multiple commits (as you did before).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

i have separated the commits so take a look @roosterfish

Copilot AI left a comment

Copy link
Copy Markdown

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 2 out of 2 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

test/suites/basic.sh:447

  • The new test claims a short session timeout, but join_session runs microcloud init/add with a hard-coded --session-timeout=60 (see test/includes/microcloud.sh:200-204). As written, this test will likely wait ~60s for the session to expire, which slows the suite and doesn’t match the PR description (SESSION_TIMEOUT=5). Consider making the timeout configurable (e.g., env var) and setting it to 5 for this case.
  # Initiate a MicroCloud cluster with a session timeout and verify both initiator and joiner receive "Session timeout exceeded".
  reset_systems 3 0 0
  unset_interactive_vars

  echo "Initiate a MicroCloud cluster with a short session timeout and verify both initiator and joiner receive the timeout error"

Comment thread client/websocket.go
Comment on lines 143 to 147
func (w *WebsocketGateway) Write(v any) error {
w.writeLock.Lock()
defer w.writeLock.Unlock()

if w.ctx.Err() != nil {
return context.Cause(w.ctx)
}

return w.conn.WriteJSON(v)
Signed-off-by: Shardool Patil <shardoolpatil999@gmail.com>
Signed-off-by: Shardool Patil <shardoolpatil999@gmail.com>
@shardool-patil
shardool-patil force-pushed the fix/session-timeout-forwarding branch from e5601b6 to b1cfb10 Compare August 26, 2026 17:03
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.

Session expiry isn't forwarded to the client

3 participants