Skip to content

Commit ac5f484

Browse files
committed
🤖 fix: require resourceVersion on aggregated storage updates
- reject workspace/template updates that omit metadata.resourceVersion - preserve optimistic-lock semantics and return conflicts on mismatches - add regression tests for missing resourceVersion update requests --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.45`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=0.45 -->
1 parent 2815c01 commit ac5f484

4 files changed

Lines changed: 68 additions & 2 deletions

File tree

‎internal/aggregated/storage/template.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,10 @@ func (s *TemplateStorage) Update(
364364
)
365365
}
366366

367-
if candidate.ResourceVersion != "" && candidate.ResourceVersion != existing.ResourceVersion {
367+
if candidate.ResourceVersion == "" {
368+
return nil, false, apierrors.NewBadRequest("metadata.resourceVersion is required for update")
369+
}
370+
if candidate.ResourceVersion != existing.ResourceVersion {
368371
return nil, false, apierrors.NewConflict(
369372
aggregationv1alpha1.Resource("codertemplates"),
370373
name,

‎internal/aggregated/storage/template_test.go‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,33 @@ func TestTemplateStorageDeleteAmbiguousWithoutNamespace(t *testing.T) {
199199
t.Fatalf("expected BadRequest for ambiguous delete, got %v", err)
200200
}
201201
}
202+
203+
func TestTemplateStorageUpdateRequiresResourceVersion(t *testing.T) {
204+
t.Helper()
205+
206+
templateStorage := NewTemplateStorage()
207+
ctx := genericapirequest.WithNamespace(context.Background(), "default")
208+
209+
currentObj, err := templateStorage.Get(ctx, "starter-template", nil)
210+
if err != nil {
211+
t.Fatalf("get template: %v", err)
212+
}
213+
current := currentObj.(*aggregationv1alpha1.CoderTemplate)
214+
215+
modified := current.DeepCopy()
216+
modified.Spec.Running = !current.Spec.Running
217+
modified.ResourceVersion = ""
218+
219+
_, _, err = templateStorage.Update(
220+
ctx,
221+
modified.Name,
222+
rest.DefaultUpdatedObjectInfo(modified),
223+
nil,
224+
nil,
225+
false,
226+
nil,
227+
)
228+
if !apierrors.IsBadRequest(err) {
229+
t.Fatalf("expected BadRequest when resourceVersion is missing, got %v", err)
230+
}
231+
}

‎internal/aggregated/storage/workspace.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,10 @@ func (s *WorkspaceStorage) Update(
365365
)
366366
}
367367

368-
if candidate.ResourceVersion != "" && candidate.ResourceVersion != existing.ResourceVersion {
368+
if candidate.ResourceVersion == "" {
369+
return nil, false, apierrors.NewBadRequest("metadata.resourceVersion is required for update")
370+
}
371+
if candidate.ResourceVersion != existing.ResourceVersion {
369372
return nil, false, apierrors.NewConflict(
370373
aggregationv1alpha1.Resource("coderworkspaces"),
371374
name,

‎internal/aggregated/storage/workspace_test.go‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,33 @@ func TestWorkspaceStorageDeleteAmbiguousWithoutNamespace(t *testing.T) {
199199
t.Fatalf("expected BadRequest for ambiguous delete, got %v", err)
200200
}
201201
}
202+
203+
func TestWorkspaceStorageUpdateRequiresResourceVersion(t *testing.T) {
204+
t.Helper()
205+
206+
workspaceStorage := NewWorkspaceStorage()
207+
ctx := genericapirequest.WithNamespace(context.Background(), "default")
208+
209+
currentObj, err := workspaceStorage.Get(ctx, "dev-workspace", nil)
210+
if err != nil {
211+
t.Fatalf("get workspace: %v", err)
212+
}
213+
current := currentObj.(*aggregationv1alpha1.CoderWorkspace)
214+
215+
modified := current.DeepCopy()
216+
modified.Spec.Running = !current.Spec.Running
217+
modified.ResourceVersion = ""
218+
219+
_, _, err = workspaceStorage.Update(
220+
ctx,
221+
modified.Name,
222+
rest.DefaultUpdatedObjectInfo(modified),
223+
nil,
224+
nil,
225+
false,
226+
nil,
227+
)
228+
if !apierrors.IsBadRequest(err) {
229+
t.Fatalf("expected BadRequest when resourceVersion is missing, got %v", err)
230+
}
231+
}

0 commit comments

Comments
 (0)