mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-19 05:50:41 +08:00
* feat(api): return typed deletion outcomes with explicit missing-target semantics Implement phase 2 of #3051 across the public gateway API and first-party SDKs. Preserve asynchronous sandbox deletion and observed resource identities, reserve legacy wire fields, and document the coordinated migration. Signed-off-by: Mrunal Patel <mrunalp@gmail.com> * fix(sdk): bind deletion waits to sandbox identity Track accepted deletions by original sandbox identity in Rust and TypeScript. Preserve name-only waits and cover replacement races and lookup failures. Merge the phase-one cleanup fix and adapt its interceptor regressions to typed deletion outcomes. Refs #3051. Signed-off-by: Mrunal Patel <mrunalp@gmail.com> * test(e2e): accept asynchronous stopped sandbox deletion Allow either completed or accepted deletion output, then continue polling for actual sandbox absence. This preserves the lifecycle assertion under the phase-two deletion contract. Refs #3051. Signed-off-by: Mrunal Patel <mrunalp@gmail.com> --------- Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
185 lines
4.7 KiB
Go
185 lines
4.7 KiB
Go
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package v1_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1"
|
|
"github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/fake"
|
|
)
|
|
|
|
// ExampleClient_Sandboxes demonstrates the sandbox lifecycle: create a sandbox,
|
|
// wait for it to become ready, and then clean up.
|
|
func ExampleClient_Sandboxes() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a sandbox
|
|
sb, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}, nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Phase after create:", sb.Status.Phase)
|
|
|
|
// Wait for the sandbox to become ready
|
|
sb, err = client.Sandboxes().WaitReady(ctx, "default", "my-sandbox")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Phase after wait:", sb.Status.Phase)
|
|
|
|
// Clean up
|
|
if _, err := client.Sandboxes().Delete(ctx, "default", "my-sandbox"); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Deleted")
|
|
// Output:
|
|
// Phase after create: Provisioning
|
|
// Phase after wait: Ready
|
|
// Deleted
|
|
}
|
|
|
|
// ExampleClient_Providers demonstrates registering and listing providers.
|
|
func ExampleClient_Providers() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
// Register a provider
|
|
_, err := client.Providers().Create(ctx, "default", &v1.Provider{
|
|
Name: "my-openai",
|
|
Type: "openai",
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// List all providers
|
|
providers, err := client.Providers().ListAll(ctx, "default")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Count:", len(providers))
|
|
fmt.Println("Name:", providers[0].Name)
|
|
// Output:
|
|
// Count: 1
|
|
// Name: my-openai
|
|
}
|
|
|
|
// ExampleClient_Health demonstrates checking gateway health.
|
|
func ExampleClient_Health() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
result, err := client.Health().Check(ctx)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Healthy:", result.Healthy)
|
|
// Output:
|
|
// Healthy: true
|
|
}
|
|
|
|
// ExampleClient_Exec demonstrates running a command in a sandbox.
|
|
// The fake client returns Unimplemented for exec operations, so this
|
|
// example shows the call pattern and error handling.
|
|
func ExampleClient_Exec() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := client.Exec().Run(ctx, "default", "my-sandbox", []string{"echo", "hello"})
|
|
if v1.IsUnimplemented(err) {
|
|
fmt.Println("Exec requires a real gateway")
|
|
}
|
|
// Output:
|
|
// Exec requires a real gateway
|
|
}
|
|
|
|
// ExampleClient_TCP demonstrates binding a local port to a sandbox port.
|
|
// The returned handle accepts and tunnels connections internally.
|
|
//
|
|
// The fake client returns Unimplemented for Listen, so this example shows
|
|
// the call pattern and error handling rather than a live tunnel.
|
|
func ExampleClient_TCP() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
// Bind local port 0 (OS-assigned) to sandbox port 8080.
|
|
ln, err := client.TCP().Listen(ctx, "default", "my-sandbox", 8080, 0)
|
|
if v1.IsUnimplemented(err) {
|
|
fmt.Println("Listen requires a real gateway")
|
|
}
|
|
if ln != nil {
|
|
// In production, dial ln.Addr() with the protocol client that should
|
|
// connect to the sandbox service. No Accept loop is required.
|
|
defer ln.Close() //nolint:errcheck
|
|
}
|
|
// Output:
|
|
// Listen requires a real gateway
|
|
}
|
|
|
|
// ExampleIsNotFound demonstrates handling a not-found error.
|
|
func ExampleIsNotFound() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := client.Sandboxes().Get(ctx, "default", "nonexistent")
|
|
if v1.IsNotFound(err) {
|
|
fmt.Println("Sandbox not found")
|
|
}
|
|
// Output:
|
|
// Sandbox not found
|
|
}
|
|
|
|
// ExampleIsAlreadyExists demonstrates handling a duplicate-creation error.
|
|
func ExampleIsAlreadyExists() {
|
|
client := fake.NewClient()
|
|
defer client.Close() //nolint:errcheck
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a sandbox
|
|
_, err := client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}, nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Try to create the same sandbox again
|
|
_, err = client.Sandboxes().Create(ctx, "default", "my-sandbox", &v1.SandboxSpec{}, nil)
|
|
if v1.IsAlreadyExists(err) {
|
|
fmt.Println("Sandbox already exists")
|
|
}
|
|
// Output:
|
|
// Sandbox already exists
|
|
}
|
|
|
|
// ExampleIsUnavailable demonstrates detecting a closed client.
|
|
func ExampleIsUnavailable() {
|
|
client := fake.NewClient()
|
|
_ = client.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := client.Sandboxes().Get(ctx, "default", "any")
|
|
if v1.IsUnavailable(err) {
|
|
fmt.Println("Client is closed")
|
|
}
|
|
// Output:
|
|
// Client is closed
|
|
}
|