use GET for enroll status polling #297 - #301
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Code Review
This pull request refactors the enrollment status polling mechanism in both internal/node/enroll.go and internal/router/router.go by replacing the POST requests (which sent protobuf payloads) with GET requests that pass the peer ID as a query parameter. A review comment points out that the implementation in internal/router/router.go uses client.Get instead of http.NewRequestWithContext, which ignores the request context, and fails to query-escape the peer ID. The reviewer suggested a code block to resolve these issues.
| return r.ctx.Err() | ||
| case <-ticker.C: | ||
| statusResp, err := client.Post(r.config.ControlPlaneURL+"/enroll/status", "application/x-protobuf", bytes.NewReader(statusData)) | ||
| statusResp, err := client.Get(r.config.ControlPlaneURL + "/enroll/status?peer_id=" + peerID.String()) |
There was a problem hiding this comment.
Using client.Get here ignores the request context (r.ctx), meaning that if the context is canceled while the HTTP request is in-flight, the request will not be aborted immediately and will instead wait for the client's 30-second timeout.
Additionally, peerID.String() is appended directly to the URL without query escaping, which is inconsistent with the implementation in internal/node/enroll.go and could lead to issues if the peer ID format contains special characters.
We should use http.NewRequestWithContext with r.ctx and url.QueryEscape instead. Note that you will need to import "net/url" in this file.
| statusResp, err := client.Get(r.config.ControlPlaneURL + "/enroll/status?peer_id=" + peerID.String()) | |
| req, err := http.NewRequestWithContext(r.ctx, "GET", r.config.ControlPlaneURL+"/enroll/status?peer_id="+url.QueryEscape(peerID.String()), nil) | |
| if err != nil { | |
| logger.Warnf("failed to create enrollment status request: %v", err) | |
| continue | |
| } | |
| statusResp, err := client.Do(req) |
|
Things I specifically checked and am happy with:
Three changes before we merge
A verb mismatch between two components belongs at the integration We already have a test that calls the handler is what we already have, and it is what missed this.
The URL is loop-invariant in both files. Build it once above the Please make
|
|
thanks @aojea. I pushed the changes:
i also set the protoc version string to v3.21.12 so the verify passes. |
@IbrahimAhmed8 can you please rebase to fix that? |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request removes the redundant EnrollmentStatusRequest protobuf message and transitions the enrollment status polling mechanism to use standard HTTP GET requests with query parameters instead of POST requests with protobuf bodies. It also includes a new integration test to verify the enrollment status polling flow. The review feedback suggests improving the robustness of URL construction by using url.Parse and JoinPath to avoid potential issues with double slashes, and recommends making the integration test polling loop more resilient to transient network or decoding errors.
| return fmt.Errorf("failed to marshal status request: %w", err) | ||
| } | ||
|
|
||
| statusURL := controlPlaneURL + "/enroll/status?peer_id=" + url.QueryEscape(n.Host.ID().String()) |
There was a problem hiding this comment.
Constructing the status URL via simple string concatenation is fragile and can result in double slashes (e.g., http://example.com//enroll/status) if controlPlaneURL has a trailing slash. This can cause unexpected 404s or redirects depending on the server or reverse proxy configuration. It is safer and more robust to use url.Parse and JoinPath to build the URL defensively.
u, err := url.Parse(controlPlaneURL)
if err != nil {
return fmt.Errorf("invalid control plane URL: %w", err)
}
u = u.JoinPath("enroll", "status")
q := u.Query()
q.Set("peer_id", n.Host.ID().String())
u.RawQuery = q.Encode()
statusURL := u.String()| return fmt.Errorf("failed to marshal status request: %w", err) | ||
| } | ||
|
|
||
| statusURL := r.config.ControlPlaneURL + "/enroll/status?peer_id=" + url.QueryEscape(peerID.String()) |
There was a problem hiding this comment.
Constructing the status URL via simple string concatenation is fragile and can result in double slashes (e.g., http://example.com//enroll/status) if ControlPlaneURL has a trailing slash. This can cause unexpected 404s or redirects depending on the server or reverse proxy configuration. It is safer and more robust to use url.Parse and JoinPath to build the URL defensively.
u, err := url.Parse(r.config.ControlPlaneURL)
if err != nil {
return fmt.Errorf("invalid control plane URL: %w", err)
}
u = u.JoinPath("enroll", "status")
q := u.Query()
q.Set("peer_id", peerID.String())
u.RawQuery = q.Encode()
statusURL := u.String()| listResp, err := client.Do(listReq) | ||
| if err != nil { | ||
| t.Fatalf("failed to list enrollments: %v", err) | ||
| } | ||
| var enrollList []storage.EnrollmentRequest | ||
| if err := json.NewDecoder(listResp.Body).Decode(&enrollList); err != nil { | ||
| _ = listResp.Body.Close() | ||
| t.Fatalf("failed to decode enrollments: %v", err) | ||
| } | ||
| _ = listResp.Body.Close() |
There was a problem hiding this comment.
In the integration test polling loop, calling t.Fatalf immediately on any network error, non-200 HTTP status, or JSON decoding failure can lead to flaky tests. Since the node enrollment request runs asynchronously, the admin client might poll before the server is fully ready or before the enrollment request has been processed. It is much more robust to log the error, sleep, and continue the polling loop until the deadline is reached.
| listResp, err := client.Do(listReq) | |
| if err != nil { | |
| t.Fatalf("failed to list enrollments: %v", err) | |
| } | |
| var enrollList []storage.EnrollmentRequest | |
| if err := json.NewDecoder(listResp.Body).Decode(&enrollList); err != nil { | |
| _ = listResp.Body.Close() | |
| t.Fatalf("failed to decode enrollments: %v", err) | |
| } | |
| _ = listResp.Body.Close() | |
| listResp, err := client.Do(listReq) | |
| if err != nil { | |
| time.Sleep(50 * time.Millisecond) | |
| continue | |
| } | |
| if listResp.StatusCode != http.StatusOK { | |
| _ = listResp.Body.Close() | |
| time.Sleep(50 * time.Millisecond) | |
| continue | |
| } | |
| var enrollList []storage.EnrollmentRequest | |
| if err := json.NewDecoder(listResp.Body).Decode(&enrollList); err != nil { | |
| _ = listResp.Body.Close() | |
| time.Sleep(50 * time.Millisecond) | |
| continue | |
| } | |
| _ = listResp.Body.Close() |
8a6d9c5 to
83cf602
Compare
|
I rebased on main and applied the bot suggestions. actually pushed an empty commit to retrigger the bats job before realizing it forces a full run of the whole suite. sorry for burning the actions minutes especially with the billing limit issue you mentioned |
|
CI fixed in #312 |
aacfdf9 to
de457ca
Compare
|
Two small things and then I'll merge:
|
|
i looked at the raw logs and its failing on test 23 in relay.bats line 91 where it reports 0 peers. looks like a dht flake |
de457ca to
ec22cfc
Compare
yeah, unrelated flake, we need to deflake that, but is not related to this change |
i added the cache header and combined everything into one commit |
|
Thanks |
clients used POST instead of GET for
/enroll/statuscausing 405s.fixes #297