Skip to content

Commit 9008caf

Browse files
committed
feat(sdk): make the unsuffixed client name the v1 surface in python and go
1 parent 7021e98 commit 9008caf

21 files changed

Lines changed: 879 additions & 726 deletions

sdk/go/README.md

Lines changed: 336 additions & 336 deletions
Large diffs are not rendered by default.

sdk/go/dstack/client.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,30 +189,25 @@ const (
189189
// The methods here mirror the unversioned paths (`/GetKey`, equivalently
190190
// `/v0/GetKey`), which the agent serves unchanged for pre-0.6 clients and will
191191
// never extend. New capability lives on DstackClientV1.
192+
//
193+
// Deprecated: legacy surface, frozen at v0.5.11 and never extended. New code
194+
// should use DstackClient, which is DstackClientV1. Reaching for the explicit
195+
// V0 name is the deliberate way to stay on the frozen surface.
192196
type DstackClientV0 struct {
193197
transport
194198
}
195199

196-
// DstackClient is the pre-0.6 name for DstackClientV0.
197-
//
198-
// Deprecated: use DstackClientV0, or DstackClientV1 for the current API.
199-
type DstackClient = DstackClientV0
200-
201200
// Creates a new DstackClientV0 instance based on the provided endpoint.
202201
// If the endpoint is empty, it will use the simulator endpoint if it is
203202
// set in the environment through DSTACK_SIMULATOR_ENDPOINT. Otherwise, it
204203
// will use the default endpoint at /var/run/dstack.sock.
204+
//
205+
// Deprecated: constructs a client for the frozen v0.5.11 surface. New code
206+
// should use NewDstackClient, which returns a v1 client.
205207
func NewDstackClientV0(opts ...DstackClientOption) *DstackClientV0 {
206208
return &DstackClientV0{transport: newTransport(opts)}
207209
}
208210

209-
// NewDstackClient is the pre-0.6 name for NewDstackClientV0.
210-
//
211-
// Deprecated: use NewDstackClientV0, or NewDstackClientV1 for the current API.
212-
func NewDstackClient(opts ...DstackClientOption) *DstackClient {
213-
return NewDstackClientV0(opts...)
214-
}
215-
216211
// TlsKeyOption defines a function type for TLS key options
217212
type TlsKeyOption func(*tlsKeyOptions)
218213

@@ -591,10 +586,14 @@ func (c *DstackClientV0) TdxQuote(ctx context.Context, reportData []byte, hashAl
591586
return c.GetQuote(ctx, reportData)
592587
}
593588

594-
// TappdClient is a deprecated wrapper around DstackClient for backward compatibility.
589+
// TappdClient is a deprecated wrapper around DstackClientV0 for backward
590+
// compatibility. It wraps v0 and not v1 because tappd predates both: its
591+
// callers expect the v0.5.11 method set, and v1 has no equivalent for the
592+
// tappd-era methods overridden below.
593+
//
595594
// Deprecated: Use DstackClient instead.
596595
type TappdClient struct {
597-
*DstackClient
596+
*DstackClientV0
598597
}
599598

600599
// NewTappdClient creates a new deprecated TappdClient.
@@ -618,11 +617,11 @@ func NewTappdClient(opts ...DstackClientOption) *TappdClient {
618617
// Add user-provided options
619618
tappdOpts = append(tappdOpts, opts...)
620619

621-
client := NewDstackClient(tappdOpts...)
620+
client := NewDstackClientV0(tappdOpts...)
622621
client.logger.Warn("TappdClient is deprecated, please use DstackClient instead")
623622

624623
return &TappdClient{
625-
DstackClient: client,
624+
DstackClientV0: client,
626625
}
627626
}
628627

sdk/go/dstack/client_test.go

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
)
2222

2323
func TestGetKey(t *testing.T) {
24-
client := dstack.NewDstackClient()
24+
client := dstack.NewDstackClientV0()
2525
resp, err := client.GetKey(context.Background(), "/", "test", "ed25519")
2626
if err != nil {
2727
t.Fatal(err)
@@ -37,7 +37,7 @@ func TestGetKey(t *testing.T) {
3737
}
3838

3939
func TestGetQuote(t *testing.T) {
40-
client := dstack.NewDstackClient()
40+
client := dstack.NewDstackClientV0()
4141
resp, err := client.GetQuote(context.Background(), []byte("test"))
4242
if err != nil {
4343
t.Fatal(err)
@@ -59,7 +59,7 @@ func TestGetQuote(t *testing.T) {
5959
}
6060

6161
func TestAttest(t *testing.T) {
62-
client := dstack.NewDstackClient()
62+
client := dstack.NewDstackClientV0()
6363
resp, err := client.Attest(context.Background(), []byte("test"))
6464
if err != nil {
6565
t.Fatal(err)
@@ -111,7 +111,7 @@ func TestAttestRequestIsFrozenAtV0(t *testing.T) {
111111
}
112112

113113
func TestGetTlsKey(t *testing.T) {
114-
client := dstack.NewDstackClient()
114+
client := dstack.NewDstackClientV0()
115115
altNames := []string{"localhost"}
116116
resp, err := client.GetTlsKey(
117117
context.Background(),
@@ -177,7 +177,7 @@ func TestGetTlsKey(t *testing.T) {
177177
}
178178

179179
func TestGetTlsKeyMinimalOptions(t *testing.T) {
180-
client := dstack.NewDstackClient()
180+
client := dstack.NewDstackClientV0()
181181
// Test with minimal options (just subject)
182182
resp, err := client.GetTlsKey(
183183
context.Background(),
@@ -217,7 +217,7 @@ func TestGetTlsKeyMinimalOptions(t *testing.T) {
217217
}
218218

219219
func TestGetTlsKeyServerOnly(t *testing.T) {
220-
client := dstack.NewDstackClient()
220+
client := dstack.NewDstackClientV0()
221221
// Test with server auth only
222222
resp, err := client.GetTlsKey(
223223
context.Background(),
@@ -269,7 +269,7 @@ func TestGetTlsKeyServerOnly(t *testing.T) {
269269
}
270270

271271
func TestGetTlsKeyClientOnly(t *testing.T) {
272-
client := dstack.NewDstackClient()
272+
client := dstack.NewDstackClientV0()
273273
// Test with client auth only
274274
resp, err := client.GetTlsKey(
275275
context.Background(),
@@ -321,7 +321,7 @@ func TestGetTlsKeyClientOnly(t *testing.T) {
321321
}
322322

323323
func TestGetTlsKeyWithMultipleAltNames(t *testing.T) {
324-
client := dstack.NewDstackClient()
324+
client := dstack.NewDstackClientV0()
325325
// Test with multiple alternative names
326326
altNames := []string{"example.com", "test.example.com"}
327327
resp, err := client.GetTlsKey(
@@ -385,7 +385,7 @@ func parseCertificate(pemCert string) (*x509.Certificate, error) {
385385
}
386386

387387
func TestInfo(t *testing.T) {
388-
client := dstack.NewDstackClient()
388+
client := dstack.NewDstackClientV0()
389389
resp, err := client.Info(context.Background())
390390
if err != nil {
391391
t.Fatal(err)
@@ -443,7 +443,7 @@ func TestInfo(t *testing.T) {
443443
}
444444

445445
func TestSignAndVerifyEd25519(t *testing.T) {
446-
client := dstack.NewDstackClient()
446+
client := dstack.NewDstackClientV0()
447447
dataToSign := []byte("test message for ed25519")
448448
algorithm := "ed25519"
449449

@@ -486,7 +486,7 @@ func TestSignAndVerifyEd25519(t *testing.T) {
486486
}
487487

488488
func TestSignAndVerifySecp256k1(t *testing.T) {
489-
client := dstack.NewDstackClient()
489+
client := dstack.NewDstackClientV0()
490490
dataToSign := []byte("test message for secp256k1")
491491
algorithm := "secp256k1"
492492

@@ -522,7 +522,7 @@ func TestSignAndVerifySecp256k1(t *testing.T) {
522522
}
523523

524524
func TestSignAndVerifySecp256k1Prehashed(t *testing.T) {
525-
client := dstack.NewDstackClient()
525+
client := dstack.NewDstackClientV0()
526526
dataToSign := []byte("test message for secp256k1 prehashed")
527527
digest := sha256.Sum256(dataToSign)
528528
algorithm := "secp256k1_prehashed"
@@ -567,7 +567,7 @@ func TestSignAndVerifySecp256k1Prehashed(t *testing.T) {
567567
}
568568

569569
func TestGetVersion(t *testing.T) {
570-
client := dstack.NewDstackClient()
570+
client := dstack.NewDstackClientV0()
571571
resp, err := client.GetVersion(context.Background())
572572
if err != nil {
573573
t.Fatal(err)
@@ -579,7 +579,7 @@ func TestGetVersion(t *testing.T) {
579579
}
580580

581581
func TestGetKeyK256Alias(t *testing.T) {
582-
client := dstack.NewDstackClient()
582+
client := dstack.NewDstackClientV0()
583583

584584
respK256, err := client.GetKey(context.Background(), "/test", "purpose", "k256")
585585
if err != nil {
@@ -598,23 +598,23 @@ func TestGetKeyK256Alias(t *testing.T) {
598598
}
599599

600600
func TestGetKeyUnsupportedAlgorithm(t *testing.T) {
601-
client := dstack.NewDstackClient()
601+
client := dstack.NewDstackClientV0()
602602
_, err := client.GetKey(context.Background(), "/test", "purpose", "rsa")
603603
if err == nil {
604604
t.Fatal("expected error for unsupported algorithm")
605605
}
606606
}
607607

608608
func TestGetKeySecp256k1PrehashedRejected(t *testing.T) {
609-
client := dstack.NewDstackClient()
609+
client := dstack.NewDstackClientV0()
610610
_, err := client.GetKey(context.Background(), "/test", "purpose", "secp256k1_prehashed")
611611
if err == nil {
612612
t.Fatal("expected error for secp256k1_prehashed in GetKey")
613613
}
614614
}
615615

616616
func TestGetKeyAlgorithmValidation(t *testing.T) {
617-
client := dstack.NewDstackClient()
617+
client := dstack.NewDstackClientV0()
618618

619619
// ed25519 should succeed (Version RPC is available on the simulator)
620620
resp, err := client.GetKey(context.Background(), "/test", "purpose", "ed25519")
@@ -646,12 +646,11 @@ func TestEmitEventSurfacesTheRemovalMessage(t *testing.T) {
646646
}
647647
}
648648

649-
// NewDstackClient is kept as a deprecated alias for NewDstackClientV0, so code
650-
// written against the pre-0.6 SDK keeps compiling and keeps talking to the same
651-
// frozen surface.
652-
func TestDeprecatedAliasIsTheV0Client(t *testing.T) {
653-
var client *dstack.DstackClientV0 = dstack.NewDstackClient()
649+
// The frozen surface stays reachable, but only under its explicit name now that
650+
// the unsuffixed client means v1.
651+
func TestV0RemainsAvailableUnderItsExplicitName(t *testing.T) {
652+
var client *dstack.DstackClientV0 = dstack.NewDstackClientV0()
654653
if !client.IsReachable(context.Background()) {
655-
t.Error("expected the aliased client to reach the simulator")
654+
t.Error("expected the v0 client to reach the simulator")
656655
}
657656
}

sdk/go/dstack/client_v1.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,27 @@ type DstackClientV1 struct {
132132
transport
133133
}
134134

135+
// DstackClient is the recommended client, and it is v1: the unsuffixed name
136+
// tracks the current API rather than pinning the surface a caller happened to
137+
// start on. Code that used it for v0 fails to compile after the upgrade,
138+
// because the v1 signatures differ -- which is the point. A silent switch would
139+
// hand back different key material under the same call.
140+
type DstackClient = DstackClientV1
141+
135142
// Creates a new DstackClientV1 instance based on the provided endpoint.
136143
// Endpoint resolution is identical to NewDstackClientV0 -- the two surfaces
137144
// share one socket and differ only in the URL path.
138145
func NewDstackClientV1(opts ...DstackClientOption) *DstackClientV1 {
139146
return &DstackClientV1{transport: newTransport(opts)}
140147
}
141148

149+
// NewDstackClient creates a client for the current API, which is v1. Use it
150+
// unless you specifically need the frozen v0.5.11 surface, in which case name
151+
// NewDstackClientV0 explicitly.
152+
func NewDstackClient(opts ...DstackClientOption) *DstackClient {
153+
return NewDstackClientV1(opts...)
154+
}
155+
142156
// decodeHexField decodes one hex-encoded protobuf `bytes` field, naming the
143157
// field so a malformed response says which one was wrong.
144158
func decodeHexField(name string, value string) ([]byte, error) {

sdk/go/dstack/client_v1_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,29 @@ func TestV1MethodsPostUnderTheV1Prefix(t *testing.T) {
445445
}
446446
}
447447

448+
// The unsuffixed names are v1. The assignments below are half the assertion --
449+
// they do not compile if DstackClient is anything else -- and the round trip is
450+
// the other half: the default constructor must actually post under /v1.
451+
func TestUnsuffixedClientIsV1(t *testing.T) {
452+
var _ *dstack.DstackClientV1 = (*dstack.DstackClient)(nil)
453+
454+
path := make(chan string, 1)
455+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
456+
path <- r.URL.Path
457+
w.Header().Set("Content-Type", "application/json")
458+
_, _ = w.Write([]byte(`{"version":"0.6.0","rev":"test"}`))
459+
}))
460+
defer server.Close()
461+
462+
client := dstack.NewDstackClient(dstack.WithEndpoint(server.URL))
463+
if _, err := client.Version(context.Background()); err != nil {
464+
t.Fatal(err)
465+
}
466+
if got := <-path; got != "/v1/Version" {
467+
t.Errorf("expected the default client to speak v1, got %s", got)
468+
}
469+
}
470+
448471
// An agent that predates v1 has no /v1 mount, so it answers with a plain 404
449472
// rather than a prpc error. The client surfaces that rather than masking it.
450473
func TestV1AgainstAnAgentWithoutV1(t *testing.T) {

sdk/go/dstack/client_web3_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestGetKeySignatureVerification(t *testing.T) {
2424
expectedAppPubkey, _ := hex.DecodeString("02818494263695e8839122dbd88e281d7380622999df4e60a14befa0f2d096fc7c")
2525
expectedKmsPubkey, _ := hex.DecodeString("0321529e458424ab1f710a3a57ec4dad2fb195ddca572f7469242ba6c7563085b6")
2626

27-
client := dstack.NewDstackClient()
27+
client := dstack.NewDstackClientV0()
2828
path := "/test/path"
2929
purpose := "test-purpose"
3030
resp, err := client.GetKey(context.Background(), path, purpose, "secp256k1")

sdk/go/dstack/transport.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ const sdkVersion = "0.6.0"
3030

3131
// clientOptions holds what a caller may set at construction.
3232
//
33-
// Both NewDstackClientV0 and NewDstackClientV1 take the same options: a caller
34-
// moving to v1 changes the constructor and nothing else.
33+
// Every constructor -- NewDstackClient, NewDstackClientV1 and NewDstackClientV0
34+
// -- takes the same options, so a caller moving between surfaces changes the
35+
// constructor and nothing else.
3536
type clientOptions struct {
3637
endpoint string
3738
logger *slog.Logger

0 commit comments

Comments
 (0)