Skip to content

Commit d70ee1d

Browse files
committed
fix: allow defaulted legacy watch-list options
1 parent cf9327b commit d70ee1d

2 files changed

Lines changed: 94 additions & 4 deletions

File tree

internal/aggregated/storage/watch.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@ func validateUnsupportedWatchListOptions(opts *metainternalversion.ListOptions)
4545
return nil
4646
}
4747

48-
if opts.SendInitialEvents != nil {
49-
return fmt.Errorf("sendInitialEvents is not supported for this watch endpoint")
48+
if isDefaultedLegacyWatchListOptions(opts) {
49+
return nil
50+
}
51+
52+
if opts.SendInitialEvents != nil && *opts.SendInitialEvents {
53+
return fmt.Errorf("sendInitialEvents=true is not supported for this watch endpoint")
5054
}
5155
if opts.ResourceVersionMatch != "" {
5256
return fmt.Errorf(
@@ -58,6 +62,20 @@ func validateUnsupportedWatchListOptions(opts *metainternalversion.ListOptions)
5862
return nil
5963
}
6064

65+
func isDefaultedLegacyWatchListOptions(opts *metainternalversion.ListOptions) bool {
66+
if opts == nil {
67+
return false
68+
}
69+
if opts.SendInitialEvents == nil || !*opts.SendInitialEvents {
70+
return false
71+
}
72+
if opts.ResourceVersionMatch != metav1.ResourceVersionMatchNotOlderThan {
73+
return false
74+
}
75+
76+
return opts.ResourceVersion == "" || opts.ResourceVersion == "0"
77+
}
78+
6179
// filterForListOptions builds a watch.FilterFunc that applies namespace, label, and field selector filtering.
6280
// Returns nil if no filtering is needed.
6381
func filterForListOptions(requestNamespace string, opts *metainternalversion.ListOptions) (watch.FilterFunc, error) {

internal/aggregated/storage/watch_test.go

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,45 @@ func TestTemplateWatchRejectsUnsupportedWatchListOptions(t *testing.T) {
339339
})
340340
}
341341

342+
func TestWatchAllowsDefaultedLegacyWatchListOptions(t *testing.T) {
343+
t.Parallel()
344+
345+
server, _ := newMockCoderServer(t)
346+
defer server.Close()
347+
348+
workspaceStorage := NewWorkspaceStorage(newTestClientProvider(t, server.URL))
349+
defer workspaceStorage.Destroy()
350+
templateStorage := NewTemplateStorage(newTestClientProvider(t, server.URL))
351+
defer templateStorage.Destroy()
352+
353+
ctx := namespacedContext("control-plane")
354+
sendInitialEvents := true
355+
legacyWatchOptions := &metainternalversion.ListOptions{
356+
Watch: true,
357+
ResourceVersion: "",
358+
SendInitialEvents: &sendInitialEvents,
359+
ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
360+
}
361+
362+
workspaceWatcher, err := workspaceStorage.Watch(ctx, legacyWatchOptions)
363+
if err != nil {
364+
t.Fatalf("expected workspace watch to accept defaulted legacy watch options: %v", err)
365+
}
366+
if workspaceWatcher == nil {
367+
t.Fatal("assertion failed: workspace watcher must not be nil")
368+
}
369+
workspaceWatcher.Stop()
370+
371+
templateWatcher, err := templateStorage.Watch(ctx, legacyWatchOptions)
372+
if err != nil {
373+
t.Fatalf("expected template watch to accept defaulted legacy watch options: %v", err)
374+
}
375+
if templateWatcher == nil {
376+
t.Fatal("assertion failed: template watcher must not be nil")
377+
}
378+
templateWatcher.Stop()
379+
}
380+
342381
func TestValidateUnsupportedWatchListOptions(t *testing.T) {
343382
t.Parallel()
344383

@@ -351,9 +390,29 @@ func TestValidateUnsupportedWatchListOptions(t *testing.T) {
351390
}
352391

353392
sendInitialEvents := true
393+
legacyOptionsWithEmptyRV := &metainternalversion.ListOptions{
394+
Watch: true,
395+
ResourceVersion: "",
396+
SendInitialEvents: &sendInitialEvents,
397+
ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
398+
}
399+
if err := validateUnsupportedWatchListOptions(legacyOptionsWithEmptyRV); err != nil {
400+
t.Fatalf("expected defaulted legacy watch-list options with empty RV to be accepted, got %v", err)
401+
}
402+
403+
legacyOptionsWithZeroRV := &metainternalversion.ListOptions{
404+
Watch: true,
405+
ResourceVersion: "0",
406+
SendInitialEvents: &sendInitialEvents,
407+
ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
408+
}
409+
if err := validateUnsupportedWatchListOptions(legacyOptionsWithZeroRV); err != nil {
410+
t.Fatalf("expected defaulted legacy watch-list options with RV=0 to be accepted, got %v", err)
411+
}
412+
354413
err := validateUnsupportedWatchListOptions(&metainternalversion.ListOptions{SendInitialEvents: &sendInitialEvents})
355414
if err == nil {
356-
t.Fatal("expected sendInitialEvents to be rejected")
415+
t.Fatal("expected sendInitialEvents=true without matching legacy defaults to be rejected")
357416
}
358417
if !strings.Contains(err.Error(), "sendInitialEvents") {
359418
t.Fatalf("expected sendInitialEvents error, got %v", err)
@@ -363,11 +422,24 @@ func TestValidateUnsupportedWatchListOptions(t *testing.T) {
363422
ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
364423
})
365424
if err == nil {
366-
t.Fatal("expected resourceVersionMatch to be rejected")
425+
t.Fatal("expected resourceVersionMatch without matching legacy defaults to be rejected")
367426
}
368427
if !strings.Contains(err.Error(), "resourceVersionMatch") {
369428
t.Fatalf("expected resourceVersionMatch error, got %v", err)
370429
}
430+
431+
err = validateUnsupportedWatchListOptions(&metainternalversion.ListOptions{
432+
Watch: true,
433+
ResourceVersion: "12345",
434+
SendInitialEvents: &sendInitialEvents,
435+
ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
436+
})
437+
if err == nil {
438+
t.Fatal("expected non-legacy watch-list options to be rejected")
439+
}
440+
if !strings.Contains(err.Error(), "sendInitialEvents") {
441+
t.Fatalf("expected non-legacy watch-list rejection to reference sendInitialEvents, got %v", err)
442+
}
371443
}
372444

373445
func TestValidateFieldSelector(t *testing.T) {

0 commit comments

Comments
 (0)