Repository navigation
client, test/suites: forward session timeout cause over websocket - #1519
shardool-patil wants to merge 2 commits into
Conversation
roosterfish
left a comment
There was a problem hiding this comment.
Thanks for taking a look at this, please see my comments on the test.
There was a problem hiding this comment.
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
ControlClosemessage 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.
|
@roosterfish i have addressed all the requested changes:
take a look whenever possible |
b287951 to
4ffe082
Compare
|
See the test failure https://github.com/canonical/microcloud/actions/runs/32477357735/job/96766897819?pr=1519#step:3:45500. |
4ffe082 to
14a4c0b
Compare
@roosterfish I have worked and handled the requested changes so take a look at the code. |
14a4c0b to
c3f95c6
Compare
|
@roosterfish I switched the test to |
c3f95c6 to
e98fe8e
Compare
|
@roosterfish I've updated
Please take a look at this setup before re-running the tests. |
e0eafdf to
e5601b6
Compare
There was a problem hiding this comment.
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_sessionrunsmicrocloud init/addwith a hard-coded--session-timeout=60(seetest/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"
| 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) |
There was a problem hiding this comment.
I've restored the cancellation guard in Write()
e5601b6 to
b1cfb10
Compare
| return fmt.Errorf("Failed to write control message: %w", writeErr) | ||
| } | ||
|
|
||
| // Wait on the other end to hang up. |
There was a problem hiding this comment.
Why don't we have to wait anymore for the other site to hang up in case we signal close of the websocket?
There was a problem hiding this comment.
While debugging earlier test failures, I removed that trying to simplify WriteClose, but realized in hindsight that was a mistake. We definitely still need to wait for the other end to hang up so the connection isn't severed before the close frame is processed. I've restored the wait logic, so take a look at this when you get time @roosterfish
b1cfb10 to
c6fb5c0
Compare
|
@roosterfish just a ping, to check the status |
| // Cancel the inner context with the respective error. | ||
| defer gwCancel(errors.New(controlClose.ControlMessage)) | ||
| var closeErr error | ||
| if controlClose.ControlMessage != "" { |
There was a problem hiding this comment.
Can you please add a comment why we are overriding the received control message here if it is set?
| 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) |
| // Unlike the actual websocket control close message this supports message longer than 125 bytes | ||
| // Unlike the actual websocket control close message this supports messages longer than 125 bytes | ||
| // as well as special characters. | ||
| // It waits for the other side to hang up or the gateway's context being cancelled. |
There was a problem hiding this comment.
Why dropping this comment?
| err = json.Unmarshal(bytes, v) | ||
| case data, ok := <-w.Receive(): | ||
| if !ok { | ||
| if w.ctx.Err() != nil { |
There was a problem hiding this comment.
Could you please add a comment when this is happening?
There was a problem hiding this comment.
Added an explanatory comment
| writeErr := w.Write(ControlClose{ | ||
| ControlMessage: err.Error(), | ||
| ControlMessage: msg, | ||
| }) |
There was a problem hiding this comment.
Addressed in the latest push. Restored the cancellation guard in Write()
| export SETUP_CEPH="no" | ||
| export OVN_WARNING="no" | ||
|
|
||
| ! join_session init micro01 micro02 || false |
There was a problem hiding this comment.
The test runs against the default 60-second timeout rather than requiring changes to shared testbed helpers in test/includes/microcloud.sh. I have updated the PR description to remove the reference to SESSION_TIMEOUT="5".
Signed-off-by: Shardool Patil <shardoolpatil999@gmail.com>
Signed-off-by: Shardool Patil <shardoolpatil999@gmail.com>
23e0335 to
b0b4e85
Compare

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)Changes
client/websocket.go:WriteCloseto writeControlClosedirectly over the WebSocket underwriteLockso close frames are sent even whengwCtxis canceled on timeoutReceiveWithContextto check channel closure and returncontext.Cause(w.ctx)test/suites/basic.sh:test_interactiveverifying that both the initiator and joiner receive"Session timeout exceeded"Checklist
Related issues