Skip to content

Commit 7bf7205

Browse files
committed
feat(api): add stat endpoint and PathInfo error field
Cherry-picked from stainless-sdks/hypeman-go preview/feature/hypeman-cp. - Add InstanceService.Stat() method - Add PathInfo response type with Error field for stat failures - Add InstanceStatParams query parameters
1 parent b16f378 commit 7bf7205

File tree

4 files changed

+106
-4
lines changed

4 files changed

+106
-4
lines changed

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 24
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fhypeman-8fded10e90df28c07b64a92d12d665d54749b9fc13c35520667637fc596957d9.yml
3-
openapi_spec_hash: 7374a732372bddf7f2c0b532b56ae3fb
4-
config_hash: 510018ffa6ad6a17875954f66fe69598
1+
configured_endpoints: 30
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fhypeman-28e78b73c796f9ee866671ed946402b5d569e683c3207d57c9143eb7d6f83fb6.yml
3+
openapi_spec_hash: fce0ac8713369a5f048bac684ed34fc8
4+
config_hash: f65a6a2bcef49a9f623212f9de6d6f6f

api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Params Types:
3030
Response Types:
3131

3232
- <a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#Instance">Instance</a>
33+
- <a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#PathInfo">PathInfo</a>
3334
- <a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#VolumeMount">VolumeMount</a>
3435

3536
Methods:
@@ -42,6 +43,7 @@ Methods:
4243
- <code title="post /instances/{id}/restore">client.Instances.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#InstanceService.Restore">Restore</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4344
- <code title="post /instances/{id}/standby">client.Instances.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#InstanceService.Standby">Standby</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4445
- <code title="post /instances/{id}/start">client.Instances.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#InstanceService.Start">Start</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
46+
- <code title="get /instances/{id}/stat">client.Instances.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#InstanceService.Stat">Stat</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, query <a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#InstanceStatParams">InstanceStatParams</a>) (<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#PathInfo">PathInfo</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4547
- <code title="post /instances/{id}/stop">client.Instances.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#InstanceService.Stop">Stop</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/onkernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4648

4749
## Volumes

instance.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ func (r *InstanceService) Start(ctx context.Context, id string, opts ...option.R
144144
return
145145
}
146146

147+
// Returns information about a path in the guest filesystem. Useful for checking if
148+
// a path exists, its type, and permissions before performing file operations.
149+
func (r *InstanceService) Stat(ctx context.Context, id string, query InstanceStatParams, opts ...option.RequestOption) (res *PathInfo, err error) {
150+
opts = slices.Concat(r.Options, opts)
151+
if id == "" {
152+
err = errors.New("missing required id parameter")
153+
return
154+
}
155+
path := fmt.Sprintf("instances/%s/stat", id)
156+
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, query, &res, opts...)
157+
return
158+
}
159+
147160
// Stop instance (graceful shutdown)
148161
func (r *InstanceService) Stop(ctx context.Context, id string, opts ...option.RequestOption) (res *Instance, err error) {
149162
opts = slices.Concat(r.Options, opts)
@@ -277,6 +290,45 @@ func (r *InstanceNetwork) UnmarshalJSON(data []byte) error {
277290
return apijson.UnmarshalRoot(data, r)
278291
}
279292

293+
type PathInfo struct {
294+
// Whether the path exists
295+
Exists bool `json:"exists,required"`
296+
// Error message if stat failed (e.g., permission denied). Only set when exists is
297+
// false due to an error rather than the path not existing.
298+
Error string `json:"error,nullable"`
299+
// True if this is a directory
300+
IsDir bool `json:"is_dir"`
301+
// True if this is a regular file
302+
IsFile bool `json:"is_file"`
303+
// True if this is a symbolic link (only set when follow_links=false)
304+
IsSymlink bool `json:"is_symlink"`
305+
// Symlink target path (only set when is_symlink=true)
306+
LinkTarget string `json:"link_target,nullable"`
307+
// File mode (Unix permissions)
308+
Mode int64 `json:"mode"`
309+
// File size in bytes
310+
Size int64 `json:"size"`
311+
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
312+
JSON struct {
313+
Exists respjson.Field
314+
Error respjson.Field
315+
IsDir respjson.Field
316+
IsFile respjson.Field
317+
IsSymlink respjson.Field
318+
LinkTarget respjson.Field
319+
Mode respjson.Field
320+
Size respjson.Field
321+
ExtraFields map[string]respjson.Field
322+
raw string
323+
} `json:"-"`
324+
}
325+
326+
// Returns the unmodified JSON received from the API
327+
func (r PathInfo) RawJSON() string { return r.JSON.raw }
328+
func (r *PathInfo) UnmarshalJSON(data []byte) error {
329+
return apijson.UnmarshalRoot(data, r)
330+
}
331+
280332
type VolumeMount struct {
281333
// Path where volume is mounted in the guest
282334
MountPath string `json:"mount_path,required"`
@@ -354,6 +406,8 @@ type InstanceNewParams struct {
354406
Size param.Opt[string] `json:"size,omitzero"`
355407
// Number of virtual CPUs
356408
Vcpus param.Opt[int64] `json:"vcpus,omitzero"`
409+
// Device IDs or names to attach for GPU/PCI passthrough
410+
Devices []string `json:"devices,omitzero"`
357411
// Environment variables
358412
Env map[string]string `json:"env,omitzero"`
359413
// Network configuration for the instance
@@ -422,3 +476,19 @@ const (
422476
InstanceLogsParamsSourceVmm InstanceLogsParamsSource = "vmm"
423477
InstanceLogsParamsSourceHypeman InstanceLogsParamsSource = "hypeman"
424478
)
479+
480+
type InstanceStatParams struct {
481+
// Path to stat in the guest filesystem
482+
Path string `query:"path,required" json:"-"`
483+
// Follow symbolic links (like stat vs lstat)
484+
FollowLinks param.Opt[bool] `query:"follow_links,omitzero" json:"-"`
485+
paramObj
486+
}
487+
488+
// URLQuery serializes [InstanceStatParams]'s query parameters as `url.Values`.
489+
func (r InstanceStatParams) URLQuery() (v url.Values, err error) {
490+
return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{
491+
ArrayFormat: apiquery.ArrayQueryFormatComma,
492+
NestedFormat: apiquery.NestedQueryFormatBrackets,
493+
})
494+
}

instance_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,36 @@ func TestInstanceStart(t *testing.T) {
195195
}
196196
}
197197

198+
func TestInstanceStatWithOptionalParams(t *testing.T) {
199+
t.Skip("Prism tests are disabled")
200+
baseURL := "http://localhost:4010"
201+
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
202+
baseURL = envURL
203+
}
204+
if !testutil.CheckTestServer(t, baseURL) {
205+
return
206+
}
207+
client := hypeman.NewClient(
208+
option.WithBaseURL(baseURL),
209+
option.WithAPIKey("My API Key"),
210+
)
211+
_, err := client.Instances.Stat(
212+
context.TODO(),
213+
"id",
214+
hypeman.InstanceStatParams{
215+
Path: "path",
216+
FollowLinks: hypeman.Bool(true),
217+
},
218+
)
219+
if err != nil {
220+
var apierr *hypeman.Error
221+
if errors.As(err, &apierr) {
222+
t.Log(string(apierr.DumpRequest(true)))
223+
}
224+
t.Fatalf("err should be nil: %s", err.Error())
225+
}
226+
}
227+
198228
func TestInstanceStop(t *testing.T) {
199229
t.Skip("Prism tests are disabled")
200230
baseURL := "http://localhost:4010"

0 commit comments

Comments
 (0)