Skip to content

Commit dcbfaa1

Browse files
authored
Merge pull request #12 from onkernel/feature/hypeman-cp
feat: add CpToInstance and CpFromInstance functions
2 parents b16f378 + 00c6817 commit dcbfaa1

File tree

9 files changed

+1283
-4
lines changed

9 files changed

+1283
-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

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ go 1.24.0
44

55
require (
66
github.com/google/go-containerregistry v0.20.7
7+
github.com/gorilla/websocket v1.5.3
8+
github.com/stretchr/testify v1.11.1
79
github.com/tidwall/gjson v1.18.0
810
github.com/tidwall/sjson v1.2.5
911
)
@@ -15,6 +17,7 @@ require (
1517
github.com/containerd/errdefs v1.0.0 // indirect
1618
github.com/containerd/errdefs/pkg v0.3.0 // indirect
1719
github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect
20+
github.com/davecgh/go-spew v1.1.1 // indirect
1821
github.com/distribution/reference v0.6.0 // indirect
1922
github.com/docker/cli v29.0.3+incompatible // indirect
2023
github.com/docker/distribution v2.8.3+incompatible // indirect
@@ -32,6 +35,7 @@ require (
3235
github.com/opencontainers/go-digest v1.0.0 // indirect
3336
github.com/opencontainers/image-spec v1.1.1 // indirect
3437
github.com/pkg/errors v0.9.1 // indirect
38+
github.com/pmezard/go-difflib v1.0.0 // indirect
3539
github.com/sirupsen/logrus v1.9.3 // indirect
3640
github.com/tidwall/match v1.1.1 // indirect
3741
github.com/tidwall/pretty v1.2.1 // indirect
@@ -49,4 +53,5 @@ require (
4953
golang.org/x/sync v0.18.0 // indirect
5054
golang.org/x/sys v0.38.0 // indirect
5155
google.golang.org/protobuf v1.36.10 // indirect
56+
gopkg.in/yaml.v3 v3.0.1 // indirect
5257
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,16 @@ github.com/google/go-containerregistry v0.20.7 h1:24VGNpS0IwrOZ2ms2P1QE3Xa5X9p4p
4242
github.com/google/go-containerregistry v0.20.7/go.mod h1:Lx5LCZQjLH1QBaMPeGwsME9biPeo1lPx6lbGj/UmzgM=
4343
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4444
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
45+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
46+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
4547
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
4648
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
4749
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
4850
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
51+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
52+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
53+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
54+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
4955
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
5056
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
5157
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
@@ -66,6 +72,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
6672
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
6773
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6874
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
75+
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
76+
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
6977
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
7078
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
7179
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -125,6 +133,8 @@ google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr
125133
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
126134
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
127135
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
136+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
137+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
128138
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
129139
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
130140
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

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)