Skip to content

Commit 5ab52b7

Browse files
feat: Add fork operation to stainless config
1 parent 83363a5 commit 5ab52b7

4 files changed

Lines changed: 166 additions & 2 deletions

File tree

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 37
1+
configured_endpoints: 39
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fhypeman-68bd472fc1704fc7ff7ed01b4213dda068a0865d42693d47ecef90651526febb.yml
33
openapi_spec_hash: 18ec995954b05d8dfb1e9e3254cf579a
4-
config_hash: d452c139da1e46a44a68b91e8a40de72
4+
config_hash: 368f8c9248e41f12124ab83b6f5b2eec

api.md

Lines changed: 3 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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>
33+
- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceStats">InstanceStats</a>
3334
- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PathInfo">PathInfo</a>
3435
- <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#VolumeMount">VolumeMount</a>
3536

@@ -38,12 +39,14 @@ Methods:
3839
- <code title="post /instances">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.New">New</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceNewParams">InstanceNewParams</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
3940
- <code title="get /instances">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceListParams">InstanceListParams</a>) (\*[]<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4041
- <code title="delete /instances/{id}">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.Delete">Delete</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/builtin#error">error</a></code>
42+
- <code title="post /instances/{id}/fork">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.Fork">Fork</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>, body <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceForkParams">InstanceForkParams</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4143
- <code title="get /instances/{id}">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.Get">Get</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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4244
- <code title="get /instances/{id}/logs">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.Logs">Logs</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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceLogsParams">InstanceLogsParams</a>) (\*<a href="https://pkg.go.dev/builtin#string">string</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4345
- <code title="post /instances/{id}/restore">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4446
- <code title="post /instances/{id}/standby">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4547
- <code title="post /instances/{id}/start">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/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>, body <a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceStartParams">InstanceStartParams</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4648
- <code title="get /instances/{id}/stat">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceStatParams">InstanceStatParams</a>) (\*<a href="https://pkg.go.dev/github.com/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#PathInfo">PathInfo</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
49+
- <code title="get /instances/{id}/stats">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceService.Stats">Stats</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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#InstanceStats">InstanceStats</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4750
- <code title="post /instances/{id}/stop">client.Instances.<a href="https://pkg.go.dev/github.com/kernel/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/kernel/hypeman-go">hypeman</a>.<a href="https://pkg.go.dev/github.com/kernel/hypeman-go#Instance">Instance</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
4851

4952
## Volumes

instance.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ func (r *InstanceService) Delete(ctx context.Context, id string, opts ...option.
7171
return
7272
}
7373

74+
// Fork an instance from stopped, standby, or running (with from_running=true)
75+
func (r *InstanceService) Fork(ctx context.Context, id string, body InstanceForkParams, opts ...option.RequestOption) (res *Instance, err error) {
76+
opts = slices.Concat(r.Options, opts)
77+
if id == "" {
78+
err = errors.New("missing required id parameter")
79+
return
80+
}
81+
path := fmt.Sprintf("instances/%s/fork", id)
82+
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
83+
return
84+
}
85+
7486
// Get instance details
7587
func (r *InstanceService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *Instance, err error) {
7688
opts = slices.Concat(r.Options, opts)
@@ -157,6 +169,20 @@ func (r *InstanceService) Stat(ctx context.Context, id string, query InstanceSta
157169
return
158170
}
159171

172+
// Returns real-time resource utilization statistics for a running VM instance.
173+
// Metrics are collected from /proc/<pid>/stat and /proc/<pid>/statm for CPU and
174+
// memory, and from TAP interface statistics for network I/O.
175+
func (r *InstanceService) Stats(ctx context.Context, id string, opts ...option.RequestOption) (res *InstanceStats, err error) {
176+
opts = slices.Concat(r.Options, opts)
177+
if id == "" {
178+
err = errors.New("missing required id parameter")
179+
return
180+
}
181+
path := fmt.Sprintf("instances/%s/stats", id)
182+
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...)
183+
return
184+
}
185+
160186
// Stop instance (graceful shutdown)
161187
func (r *InstanceService) Stop(ctx context.Context, id string, opts ...option.RequestOption) (res *Instance, err error) {
162188
opts = slices.Concat(r.Options, opts)
@@ -348,6 +374,52 @@ func (r *InstanceNetwork) UnmarshalJSON(data []byte) error {
348374
return apijson.UnmarshalRoot(data, r)
349375
}
350376

377+
// Real-time resource utilization statistics for a VM instance
378+
type InstanceStats struct {
379+
// Total memory allocated to the VM (Size + HotplugSize) in bytes
380+
AllocatedMemoryBytes int64 `json:"allocated_memory_bytes" api:"required"`
381+
// Number of vCPUs allocated to the VM
382+
AllocatedVcpus int64 `json:"allocated_vcpus" api:"required"`
383+
// Total CPU time consumed by the VM hypervisor process in seconds
384+
CPUSeconds float64 `json:"cpu_seconds" api:"required"`
385+
// Instance identifier
386+
InstanceID string `json:"instance_id" api:"required"`
387+
// Instance name
388+
InstanceName string `json:"instance_name" api:"required"`
389+
// Resident Set Size - actual physical memory used by the VM in bytes
390+
MemoryRssBytes int64 `json:"memory_rss_bytes" api:"required"`
391+
// Virtual Memory Size - total virtual memory allocated in bytes
392+
MemoryVmsBytes int64 `json:"memory_vms_bytes" api:"required"`
393+
// Total network bytes received by the VM (from TAP interface)
394+
NetworkRxBytes int64 `json:"network_rx_bytes" api:"required"`
395+
// Total network bytes transmitted by the VM (from TAP interface)
396+
NetworkTxBytes int64 `json:"network_tx_bytes" api:"required"`
397+
// Memory utilization ratio (RSS / allocated memory). Only present when
398+
// allocated_memory_bytes > 0.
399+
MemoryUtilizationRatio float64 `json:"memory_utilization_ratio" api:"nullable"`
400+
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
401+
JSON struct {
402+
AllocatedMemoryBytes respjson.Field
403+
AllocatedVcpus respjson.Field
404+
CPUSeconds respjson.Field
405+
InstanceID respjson.Field
406+
InstanceName respjson.Field
407+
MemoryRssBytes respjson.Field
408+
MemoryVmsBytes respjson.Field
409+
NetworkRxBytes respjson.Field
410+
NetworkTxBytes respjson.Field
411+
MemoryUtilizationRatio respjson.Field
412+
ExtraFields map[string]respjson.Field
413+
raw string
414+
} `json:"-"`
415+
}
416+
417+
// Returns the unmodified JSON received from the API
418+
func (r InstanceStats) RawJSON() string { return r.JSON.raw }
419+
func (r *InstanceStats) UnmarshalJSON(data []byte) error {
420+
return apijson.UnmarshalRoot(data, r)
421+
}
422+
351423
type PathInfo struct {
352424
// Whether the path exists
353425
Exists bool `json:"exists" api:"required"`
@@ -590,6 +662,41 @@ const (
590662
InstanceListParamsStateUnknown InstanceListParamsState = "Unknown"
591663
)
592664

665+
type InstanceForkParams struct {
666+
// Name for the forked instance (lowercase letters, digits, and dashes only; cannot
667+
// start or end with a dash)
668+
Name string `json:"name" api:"required"`
669+
// Allow forking from a running source instance. When true and source is Running,
670+
// the source is put into standby, forked, then restored back to Running.
671+
FromRunning param.Opt[bool] `json:"from_running,omitzero"`
672+
// Optional final state for the forked instance. Default is the source instance
673+
// state at fork time. For example, forking from Running defaults the fork result
674+
// to Running.
675+
//
676+
// Any of "Stopped", "Standby", "Running".
677+
TargetState InstanceForkParamsTargetState `json:"target_state,omitzero"`
678+
paramObj
679+
}
680+
681+
func (r InstanceForkParams) MarshalJSON() (data []byte, err error) {
682+
type shadow InstanceForkParams
683+
return param.MarshalObject(r, (*shadow)(&r))
684+
}
685+
func (r *InstanceForkParams) UnmarshalJSON(data []byte) error {
686+
return apijson.UnmarshalRoot(data, r)
687+
}
688+
689+
// Optional final state for the forked instance. Default is the source instance
690+
// state at fork time. For example, forking from Running defaults the fork result
691+
// to Running.
692+
type InstanceForkParamsTargetState string
693+
694+
const (
695+
InstanceForkParamsTargetStateStopped InstanceForkParamsTargetState = "Stopped"
696+
InstanceForkParamsTargetStateStandby InstanceForkParamsTargetState = "Standby"
697+
InstanceForkParamsTargetStateRunning InstanceForkParamsTargetState = "Running"
698+
)
699+
593700
type InstanceLogsParams struct {
594701
// Continue streaming new lines after initial output
595702
Follow param.Opt[bool] `query:"follow,omitzero" json:"-"`

instance_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,37 @@ func TestInstanceDelete(t *testing.T) {
124124
}
125125
}
126126

127+
func TestInstanceForkWithOptionalParams(t *testing.T) {
128+
t.Skip("Mock server tests are disabled")
129+
baseURL := "http://localhost:4010"
130+
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
131+
baseURL = envURL
132+
}
133+
if !testutil.CheckTestServer(t, baseURL) {
134+
return
135+
}
136+
client := hypeman.NewClient(
137+
option.WithBaseURL(baseURL),
138+
option.WithAPIKey("My API Key"),
139+
)
140+
_, err := client.Instances.Fork(
141+
context.TODO(),
142+
"id",
143+
hypeman.InstanceForkParams{
144+
Name: "my-workload-1-fork",
145+
FromRunning: hypeman.Bool(false),
146+
TargetState: hypeman.InstanceForkParamsTargetStateRunning,
147+
},
148+
)
149+
if err != nil {
150+
var apierr *hypeman.Error
151+
if errors.As(err, &apierr) {
152+
t.Log(string(apierr.DumpRequest(true)))
153+
}
154+
t.Fatalf("err should be nil: %s", err.Error())
155+
}
156+
}
157+
127158
func TestInstanceGet(t *testing.T) {
128159
t.Skip("Mock server tests are disabled")
129160
baseURL := "http://localhost:4010"
@@ -253,6 +284,29 @@ func TestInstanceStatWithOptionalParams(t *testing.T) {
253284
}
254285
}
255286

287+
func TestInstanceStats(t *testing.T) {
288+
t.Skip("Mock server tests are disabled")
289+
baseURL := "http://localhost:4010"
290+
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
291+
baseURL = envURL
292+
}
293+
if !testutil.CheckTestServer(t, baseURL) {
294+
return
295+
}
296+
client := hypeman.NewClient(
297+
option.WithBaseURL(baseURL),
298+
option.WithAPIKey("My API Key"),
299+
)
300+
_, err := client.Instances.Stats(context.TODO(), "id")
301+
if err != nil {
302+
var apierr *hypeman.Error
303+
if errors.As(err, &apierr) {
304+
t.Log(string(apierr.DumpRequest(true)))
305+
}
306+
t.Fatalf("err should be nil: %s", err.Error())
307+
}
308+
}
309+
256310
func TestInstanceStop(t *testing.T) {
257311
t.Skip("Mock server tests are disabled")
258312
baseURL := "http://localhost:4010"

0 commit comments

Comments
 (0)