Files
Mrunal Patel c502be9fd7 feat(api): return typed deletion outcomes with explicit missing-target semantics (#3317)
* 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>
2026-09-16 22:11:19 +00:00

59 lines
2.3 KiB
Go

// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package fake
import (
"context"
"fmt"
"io"
v1 "github.com/NVIDIA/OpenShell/sdk/go/openshell/v1"
"github.com/NVIDIA/OpenShell/sdk/go/openshell/v1/types"
)
// fakeSSHClient implements v1.SSHInterface. All methods return
// Unimplemented because SSH session management requires a real gateway.
type fakeSSHClient struct {
closedFunc func() bool
}
// newFakeSSHClient creates a new fakeSSHClient.
func newFakeSSHClient(closedFunc func() bool) *fakeSSHClient {
return &fakeSSHClient{closedFunc: closedFunc}
}
// CreateSession returns Unimplemented.
func (c *fakeSSHClient) CreateSession(_ context.Context, _, _ string) (*types.SSHSession, error) {
if c.closedFunc() {
return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"}
}
return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "CreateSession is not supported by the fake client"}
}
// RevokeSession returns Unimplemented.
func (c *fakeSSHClient) RevokeSession(_ context.Context, _, _ string, _ ...v1.DeleteOptions) (*types.DeletionResult, error) {
if c.closedFunc() {
return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"}
}
return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "RevokeSession is not supported by the fake client"}
}
// Tunnel returns Unimplemented. Ports outside 1-65535 and empty sandbox names
// are rejected with InvalidArgument to match the real client's behavior.
func (c *fakeSSHClient) Tunnel(_ context.Context, _, sandboxName string, port uint32, _ ...v1.TunnelOption) (io.ReadWriteCloser, error) {
if c.closedFunc() {
return nil, &types.StatusError{Code: types.ErrorUnavailable, Message: "client is closed"}
}
if sandboxName == "" {
return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: "sandbox name must not be empty"}
}
if port == 0 || port > 65535 {
return nil, &types.StatusError{Code: types.ErrorInvalidArgument, Message: fmt.Sprintf("port must be in range 1-65535, got %d", port)}
}
return nil, &types.StatusError{Code: types.ErrorUnimplemented, Message: "Tunnel is not supported by the fake client"}
}
// Compile-time check that fakeSSHClient implements v1.SSHInterface.
var _ v1.SSHInterface = (*fakeSSHClient)(nil)