From b642ea606b043ce4b2d396ce307825933af7203f Mon Sep 17 00:00:00 2001 From: CMGS Date: Mon, 17 Aug 2026 18:08:09 +0800 Subject: [PATCH] sandboxd: add Client.Info for GET /v1/info The read-only twin of SetPools, so a node publisher can read warm-pool capacity through the operator's client instead of its own decoder. --- pkg/scale/sandboxd/client.go | 10 ++++++++++ pkg/scale/sandboxd/client_test.go | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/pkg/scale/sandboxd/client.go b/pkg/scale/sandboxd/client.go index 3a06e22..bc1eeed 100644 --- a/pkg/scale/sandboxd/client.go +++ b/pkg/scale/sandboxd/client.go @@ -189,6 +189,16 @@ func (c *Client) SetPools(ctx context.Context, pools []PoolSpec) (*NodeInfo, err return &info, nil } +// Info performs GET /v1/info: the node's live per-pool warm state and lifecycle +// counters, read without touching its pool config. +func (c *Client) Info(ctx context.Context) (*NodeInfo, error) { + var info NodeInfo + if err := c.getJSON(ctx, "/v1/info", &info); err != nil { + return nil, err + } + return &info, nil +} + // Release performs POST /v1/sandboxes/{id}/release, which DESTROYS the VM. It // authenticates with the sandbox's own token. A 404 (unknown id or already gone) // is treated as success, matching the SDK. Callers must only reach this on diff --git a/pkg/scale/sandboxd/client_test.go b/pkg/scale/sandboxd/client_test.go index 6b6698f..1780f94 100644 --- a/pkg/scale/sandboxd/client_test.go +++ b/pkg/scale/sandboxd/client_test.go @@ -102,3 +102,23 @@ func TestReleaseSuccessAndAlreadyGone(t *testing.T) { require.NoError(t, c.Release(t.Context(), "sb_abc", "sbtok")) require.Equal(t, int64(2), releases.Load()) } + +func TestInfo(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + assert.Equal(t, "/v1/info", r.URL.Path) + assert.Equal(t, "Bearer root-token", r.Header.Get("Authorization"), "info is a root-token operator surface") + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"pools":[{"key":{"template":"base:24.04","net":"none","size":"small"},"warm":3,"refilling":1,"target":4}],"claimed":2,"hibernated":1,"archived":0,"peers":["10.0.0.6:7777"]}`)) + })) + defer srv.Close() + + info, err := New(srv.URL, "root-token").Info(t.Context()) + require.NoError(t, err) + require.Len(t, info.Pools, 1) + assert.Equal(t, PoolKey{Template: "base:24.04", Net: "none", Size: "small"}, info.Pools[0].Key) + assert.Equal(t, 3, info.Pools[0].Warm) + assert.Equal(t, 4, info.Pools[0].Target) + assert.Equal(t, 2, info.Claimed) + assert.Equal(t, 1, info.Hibernated) +}