Skip to content

Commit cc8298e

Browse files
zpzjzjclaude
andcommitted
feat(sdks/go): add PlatformSpec to CreateSandboxRequest
The Lifecycle API (specs/sandbox-lifecycle.yml) and the Python SDK both expose a Platform field for selecting the target OS/arch of a sandbox (e.g. `{"os":"windows","arch":"amd64"}` to provision a Windows guest via the dockur/windows profile). The Go SDK has been missing it, so Go callers cannot drive Windows-guest sandboxes. * Add PlatformSpec{OS, Arch} mirroring the Python model. * Add CreateSandboxRequest.Platform (omitempty) and SandboxInfo.Platform. * Plumb SandboxCreateOptions.Platform through CreateSandbox. * Cover the round trip and the nil-omission case with table-style tests. Refs: docs/windows-sandbox.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ad5c364 commit cc8298e

3 files changed

Lines changed: 102 additions & 0 deletions

File tree

sdks/sandbox/go/opensandbox_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,68 @@ func TestCreateSandbox_FromSnapshot(t *testing.T) {
220220
require.NoErrorf(t, err, "CreateSandbox from snapshot")
221221
}
222222

223+
func TestCreateSandbox_Platform(t *testing.T) {
224+
_, client := newLifecycleServer(t, func(w http.ResponseWriter, r *http.Request) {
225+
var req CreateSandboxRequest
226+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
227+
assert.Fail(t, fmt.Sprintf("decode request: %v", err))
228+
return
229+
}
230+
require.NotNil(t, req.Platform, "expected Platform to be sent in the request")
231+
require.Equal(t, OSWindows, req.Platform.OS, "Platform.OS")
232+
require.Equal(t, ArchAMD64, req.Platform.Arch, "Platform.Arch")
233+
234+
jsonResponse(w, http.StatusCreated, SandboxInfo{
235+
ID: "sbx-windows",
236+
Status: SandboxStatus{State: StatePending},
237+
Platform: &PlatformSpec{OS: OSWindows, Arch: ArchAMD64},
238+
CreatedAt: time.Now().UTC().Truncate(time.Second),
239+
})
240+
})
241+
242+
info, err := client.CreateSandbox(context.Background(), CreateSandboxRequest{
243+
Image: &ImageSpec{URI: "dockurr/windows:latest"},
244+
Entrypoint: []string{"cmd", "/c", "echo hi"},
245+
ResourceLimits: ResourceLimits{"cpu": "2", "memory": "4G", "disk": "64G"},
246+
Platform: &PlatformSpec{OS: OSWindows, Arch: ArchAMD64},
247+
})
248+
require.NoErrorf(t, err, "CreateSandbox with Platform")
249+
require.NotNil(t, info.Platform, "response should echo Platform")
250+
require.Equal(t, OSWindows, info.Platform.OS, "echoed Platform.OS")
251+
require.Equal(t, ArchAMD64, info.Platform.Arch, "echoed Platform.Arch")
252+
}
253+
254+
func TestCreateSandbox_PlatformOmittedWhenNil(t *testing.T) {
255+
_, client := newLifecycleServer(t, func(w http.ResponseWriter, r *http.Request) {
256+
body, err := io.ReadAll(r.Body)
257+
if err != nil {
258+
assert.Fail(t, fmt.Sprintf("read request body: %v", err))
259+
return
260+
}
261+
var raw map[string]json.RawMessage
262+
if err := json.Unmarshal(body, &raw); err != nil {
263+
assert.Fail(t, fmt.Sprintf("unmarshal request body: %v", err))
264+
return
265+
}
266+
if _, present := raw["platform"]; present {
267+
assert.Fail(t, "platform should be omitted from JSON when nil")
268+
}
269+
270+
jsonResponse(w, http.StatusCreated, SandboxInfo{
271+
ID: "sbx-no-platform",
272+
Status: SandboxStatus{State: StatePending},
273+
CreatedAt: time.Now().UTC().Truncate(time.Second),
274+
})
275+
})
276+
277+
_, err := client.CreateSandbox(context.Background(), CreateSandboxRequest{
278+
Image: &ImageSpec{URI: "python:3.12"},
279+
Entrypoint: []string{"/bin/sh"},
280+
ResourceLimits: ResourceLimits{"cpu": "500m"},
281+
})
282+
require.NoErrorf(t, err, "CreateSandbox without Platform")
283+
}
284+
223285
func TestGetSandbox(t *testing.T) {
224286
want := SandboxInfo{
225287
ID: "sbx-456",

sdks/sandbox/go/sandbox.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ type SandboxCreateOptions struct {
6666
// Extensions for provider-specific parameters.
6767
Extensions map[string]string
6868

69+
// Platform selects the target OS/arch for the sandbox (e.g. {"os":
70+
// "windows", "arch": "amd64"}). When nil the server applies its default.
71+
Platform *PlatformSpec
72+
6973
// SkipHealthCheck skips the WaitUntilReady call after creation.
7074
SkipHealthCheck bool
7175

@@ -132,6 +136,7 @@ func CreateSandbox(ctx context.Context, config ConnectionConfig, opts SandboxCre
132136
NetworkPolicy: opts.NetworkPolicy,
133137
Volumes: opts.Volumes,
134138
Extensions: opts.Extensions,
139+
Platform: opts.Platform,
135140
}
136141
if opts.Image != "" {
137142
req.Image = &ImageSpec{URI: opts.Image, Auth: opts.ImageAuth}

sdks/sandbox/go/types.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,39 @@ type ImageAuth struct {
5555
Password string `json:"password"`
5656
}
5757

58+
// PlatformOS is the target operating system of a sandbox platform constraint.
59+
// The wire-level enum is enforced server-side; the constants below mirror the
60+
// spec so Go callers can avoid stringly-typed typos.
61+
type PlatformOS string
62+
63+
const (
64+
OSLinux PlatformOS = "linux"
65+
OSWindows PlatformOS = "windows"
66+
)
67+
68+
// PlatformArch is the target CPU architecture of a sandbox platform
69+
// constraint.
70+
type PlatformArch string
71+
72+
const (
73+
ArchAMD64 PlatformArch = "amd64"
74+
ArchARM64 PlatformArch = "arm64"
75+
)
76+
77+
// PlatformSpec is a runtime platform constraint used for scheduling and
78+
// provisioning. It is independent from Image and expresses the expected
79+
// target OS and CPU architecture for sandbox execution.
80+
//
81+
// When omitted, the server applies its own default platform selection
82+
// behavior. When provided, the runtime must satisfy the constraint or the
83+
// request fails.
84+
//
85+
// See specs/sandbox-lifecycle.yml#/components/schemas/PlatformSpec.
86+
type PlatformSpec struct {
87+
OS PlatformOS `json:"os"`
88+
Arch PlatformArch `json:"arch"`
89+
}
90+
5891
// ResourceLimits defines runtime resource constraints as key-value pairs.
5992
// Common keys: "cpu" (e.g. "500m"), "memory" (e.g. "512Mi"), "gpu" (e.g. "1").
6093
type ResourceLimits map[string]string
@@ -120,6 +153,7 @@ type CreateSandboxRequest struct {
120153
NetworkPolicy *NetworkPolicy `json:"networkPolicy,omitempty"`
121154
Volumes []Volume `json:"volumes,omitempty"`
122155
Extensions map[string]string `json:"extensions,omitempty"`
156+
Platform *PlatformSpec `json:"platform,omitempty"`
123157
}
124158

125159
// SandboxInfo represents a runtime execution environment provisioned from a
@@ -133,6 +167,7 @@ type SandboxInfo struct {
133167
Entrypoint []string `json:"entrypoint"`
134168
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
135169
CreatedAt time.Time `json:"createdAt"`
170+
Platform *PlatformSpec `json:"platform,omitempty"`
136171
}
137172

138173
type SnapshotState string

0 commit comments

Comments
 (0)