Skip to content

Commit 982799d

Browse files
committed
fix(sdk/go): address principal engineer review findings
- Remove dead boolCount function that would fail golangci-lint (#1) - Emit EventAdded for the first watch event instead of EventModified, matching k8s watch semantics (#7) - Add mutex locking to all mock server methods that access the shared sandboxes map, fixing latent race conditions (#12) - Skip HealthCheck integration test that calls an unimplemented stub (#13) - Scope doc.go examples: mark sections for sub-clients not yet available in this PR with "available in a future release" (#4) - Document Config.Timeout/RetryPolicy/Logger and WatchOptions fields as reserved for future use (#2, #6) Signed-off-by: Roland Huß <rhuss@redhat.com>
1 parent 20a4a48 commit 982799d

7 files changed

Lines changed: 43 additions & 31 deletions

File tree

‎sdk/go/openshell/v1/doc.go‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
// log.Fatal(err)
3535
// }
3636
//
37-
// # Command Execution
37+
// # Command Execution (available in a future release)
3838
//
3939
// result, err := client.Exec().Run(ctx, sandbox.Name, []string{"echo", "hello"}, v1.ExecOptions{})
4040
// if err != nil {
@@ -76,7 +76,7 @@
7676
// }
7777
// // channel closes automatically after Ready or Error
7878
//
79-
// # Service Exposure
79+
// # Service Exposure (available in a future release)
8080
//
8181
// Expose an HTTP service running inside a sandbox and retrieve its public URL:
8282
//
@@ -94,7 +94,7 @@
9494
// fmt.Printf(" %s → port %d (URL: %s)\n", ep.ServiceName, ep.TargetPort, ep.URL)
9595
// }
9696
//
97-
// # Provider Profiles
97+
// # Provider Profiles (available in a future release)
9898
//
9999
// List available provider profiles and import new ones:
100100
//
@@ -119,7 +119,7 @@
119119
// fmt.Printf("[%s] %s: %s\n", d.Severity, d.Field, d.Message)
120120
// }
121121
//
122-
// # Credential Refresh
122+
// # Credential Refresh (available in a future release)
123123
//
124124
// Configure gateway-owned credential refresh for a provider:
125125
//
@@ -192,7 +192,7 @@
192192
// "x-proxy-key": "proxy-secret",
193193
// })
194194
//
195-
// # SSH Session Management
195+
// # SSH Session Management (available in a future release)
196196
//
197197
// Create an SSH session for a sandbox and use the returned connection details.
198198
// Note: CreateSession accepts a sandbox ID, not a name. For name-based access
@@ -213,7 +213,7 @@
213213
// }
214214
// fmt.Printf("Session revoked: %v\n", revoked)
215215
//
216-
// # TCP Port Forwarding
216+
// # TCP Port Forwarding (available in a future release)
217217
//
218218
// Forward a local connection to a port inside a sandbox:
219219
//
@@ -242,7 +242,7 @@
242242
// v1.WithForwardServiceID("billing-db"),
243243
// )
244244
//
245-
// # SSH Tunneling
245+
// # SSH Tunneling (available in a future release)
246246
//
247247
// Create an SSH tunnel to a sandbox port in a single call. Tunnel combines
248248
// session creation, TCP forwarding with an SSH relay target, and automatic
@@ -300,7 +300,7 @@
300300
// },
301301
// }, nil)
302302
//
303-
// Replace the full policy at runtime via configuration update:
303+
// Replace the full policy at runtime via configuration update (available in a future release):
304304
//
305305
// result, err := client.Config().Update(ctx, &v1.ConfigUpdate{
306306
// Name: "secure-sandbox",
@@ -312,7 +312,7 @@
312312
// },
313313
// })
314314
//
315-
// Read a policy back from revision history:
315+
// Read a policy back from revision history (available in a future release):
316316
//
317317
// revisions, err := client.Policy().List(ctx, "secure-sandbox")
318318
// if err != nil {
@@ -324,7 +324,7 @@
324324
// }
325325
// }
326326
//
327-
// # Configuration Management
327+
// # Configuration Management (available in a future release)
328328
//
329329
// Read sandbox and gateway configuration, and update settings:
330330
//

‎sdk/go/openshell/v1/integration_test.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ func TestIntegration_HealthCheck(t *testing.T) {
2929
require.NoError(t, err)
3030
defer client.Close()
3131

32+
t.Skip("TODO: Health().Check() is a stub until PR B lands")
33+
3234
_, err = client.Health().Check(context.Background())
3335
require.NoError(t, err)
3436
}

‎sdk/go/openshell/v1/internal/converter/copy.go‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,3 @@ func mapToStruct(m map[string]any) *structpb.Struct {
6565
return s
6666
}
6767

68-
func boolCount(flags ...bool) int {
69-
n := 0
70-
for _, f := range flags {
71-
if f {
72-
n++
73-
}
74-
}
75-
return n
76-
}

‎sdk/go/openshell/v1/sandbox_client.go‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,15 @@ func (s *sandboxClient) Watch(ctx context.Context, name string, opts ...WatchOpt
205205
go func() {
206206
defer close(ch)
207207
ev := first
208+
isFirst := true
208209
for {
209210
if sbPayload, ok := ev.Payload.(*pb.SandboxStreamEvent_Sandbox); ok && sbPayload.Sandbox != nil {
210211
sandbox := converter.SandboxFromProto(sbPayload.Sandbox)
211212
eventType := EventModified
212-
if sandbox.Status.Phase == SandboxDeleting {
213+
if isFirst {
214+
eventType = EventAdded
215+
isFirst = false
216+
} else if sandbox.Status.Phase == SandboxDeleting {
213217
eventType = EventDeleted
214218
}
215219
select {

‎sdk/go/openshell/v1/sandbox_client_test.go‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ func newMockSandboxServer() *mockSandboxServer {
5555
}
5656

5757
func (s *mockSandboxServer) CreateSandbox(_ context.Context, req *pb.CreateSandboxRequest) (*pb.SandboxResponse, error) {
58+
s.mu.Lock()
59+
defer s.mu.Unlock()
5860
if s.createErr != nil {
5961
return nil, s.createErr
6062
}
@@ -96,6 +98,8 @@ func (s *mockSandboxServer) setPhase(name string, phase pb.SandboxPhase) {
9698
}
9799

98100
func (s *mockSandboxServer) ListSandboxes(_ context.Context, _ *pb.ListSandboxesRequest) (*pb.ListSandboxesResponse, error) {
101+
s.mu.Lock()
102+
defer s.mu.Unlock()
99103
if s.listErr != nil {
100104
return nil, s.listErr
101105
}
@@ -107,6 +111,8 @@ func (s *mockSandboxServer) ListSandboxes(_ context.Context, _ *pb.ListSandboxes
107111
}
108112

109113
func (s *mockSandboxServer) DeleteSandbox(_ context.Context, req *pb.DeleteSandboxRequest) (*pb.DeleteSandboxResponse, error) {
114+
s.mu.Lock()
115+
defer s.mu.Unlock()
110116
if s.deleteErr != nil {
111117
return nil, s.deleteErr
112118
}
@@ -119,6 +125,8 @@ func (s *mockSandboxServer) DeleteSandbox(_ context.Context, req *pb.DeleteSandb
119125
}
120126

121127
func (s *mockSandboxServer) AttachSandboxProvider(_ context.Context, req *pb.AttachSandboxProviderRequest) (*pb.AttachSandboxProviderResponse, error) {
128+
s.mu.Lock()
129+
defer s.mu.Unlock()
122130
if s.attachErr != nil {
123131
return nil, s.attachErr
124132
}
@@ -131,6 +139,8 @@ func (s *mockSandboxServer) AttachSandboxProvider(_ context.Context, req *pb.Att
131139
}
132140

133141
func (s *mockSandboxServer) DetachSandboxProvider(_ context.Context, req *pb.DetachSandboxProviderRequest) (*pb.DetachSandboxProviderResponse, error) {
142+
s.mu.Lock()
143+
defer s.mu.Unlock()
134144
if s.detachErr != nil {
135145
return nil, s.detachErr
136146
}
@@ -567,7 +577,7 @@ func TestSandboxWatch_ReceivesEvents(t *testing.T) {
567577
defer w.Stop()
568578

569579
ev1 := <-w.ResultChan()
570-
assert.Equal(t, EventModified, ev1.Type)
580+
assert.Equal(t, EventAdded, ev1.Type)
571581
require.NotNil(t, ev1.Object)
572582
assert.Equal(t, "sb-1", ev1.Object.Name)
573583
assert.Equal(t, SandboxProvisioning, ev1.Object.Status.Phase)
@@ -599,7 +609,7 @@ func TestSandboxWatch_FiltersSandboxEventsOnly(t *testing.T) {
599609
defer w.Stop()
600610

601611
ev := <-w.ResultChan()
602-
assert.Equal(t, EventModified, ev.Type)
612+
assert.Equal(t, EventAdded, ev.Type)
603613
assert.Equal(t, "sb-1", ev.Object.Name)
604614

605615
// Stream ends after server sends all events; channel should close
@@ -738,7 +748,7 @@ func TestSandboxWatch_StopOnTerminal_Ready(t *testing.T) {
738748

739749
// Should receive the Provisioning event
740750
ev1 := <-w.ResultChan()
741-
assert.Equal(t, EventModified, ev1.Type)
751+
assert.Equal(t, EventAdded, ev1.Type)
742752
assert.Equal(t, SandboxProvisioning, ev1.Object.Status.Phase)
743753

744754
// Should receive the Ready event (terminal)
@@ -782,7 +792,7 @@ func TestSandboxWatch_StopOnTerminal_Error(t *testing.T) {
782792

783793
// Should receive the Provisioning event
784794
ev1 := <-w.ResultChan()
785-
assert.Equal(t, EventModified, ev1.Type)
795+
assert.Equal(t, EventAdded, ev1.Type)
786796
assert.Equal(t, SandboxProvisioning, ev1.Object.Status.Phase)
787797

788798
// Should receive the Error event (terminal)
@@ -820,7 +830,7 @@ func TestSandboxWatch_StopOnTerminal_False_DoesNotClose(t *testing.T) {
820830
defer w.Stop()
821831

822832
ev := <-w.ResultChan()
823-
assert.Equal(t, EventModified, ev.Type)
833+
assert.Equal(t, EventAdded, ev.Type)
824834
assert.Equal(t, SandboxReady, ev.Object.Status.Phase)
825835

826836
// Channel closes because mock stream ends (not because of StopOnTerminal)

‎sdk/go/openshell/v1/types/config.go‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import "time"
77

88
// Config holds all settings needed to create a Client.
99
type Config struct {
10-
Address string
11-
TLS *TLSConfig
12-
Auth AuthProvider
13-
Timeout time.Duration
10+
Address string
11+
TLS *TLSConfig
12+
Auth AuthProvider
13+
// Timeout is reserved for future use. It is not yet applied.
14+
Timeout time.Duration
15+
// RetryPolicy is reserved for future use. It is not yet applied.
1416
RetryPolicy *RetryPolicy
15-
Logger Logger
17+
// Logger is reserved for future use. It is not yet applied.
18+
Logger Logger
1619
}

‎sdk/go/openshell/v1/types/options.go‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ type UpdateOptions struct{}
2626

2727
// WatchOptions configures watch behavior.
2828
type WatchOptions struct {
29+
// TimeoutSeconds is reserved for future use. Use context for timeout control.
2930
TimeoutSeconds int64
30-
LabelSelector string
31+
// LabelSelector is reserved for future use.
32+
LabelSelector string
3133
// StopOnTerminal causes the watch to close automatically when the sandbox
3234
// reaches a terminal phase (Ready or Error).
3335
StopOnTerminal bool

0 commit comments

Comments
 (0)