schedule: control — DELETE refuses a declared schedule on error too
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+17
−1
internal/schedule/control.go+5 −1internal/schedule/control_test.go+12 −0
diff --git a/internal/schedule/control.go b/internal/schedule/control.go| index 693417597..5a5e249dc 100644 |
| --- a/internal/schedule/control.go |
| +++ b/internal/schedule/control.go |
| @@ -111,9 +111,13 @@ func Mount(mux *http.ServeMux, host string, store TimerStore, token func() (stri |
| http.Error(w, "bad name", http.StatusBadRequest) |
| return |
| } |
| - if row, err := store.GetTimer(host, name); err == nil && row.Kind != KindOnce { |
| + switch row, err := store.GetTimer(host, name); { |
| + case err == nil && row.Kind != KindOnce: |
| http.Error(w, "name is a declared schedule — remove it with carlos schedule rm", http.StatusConflict) |
| return |
| + case err != nil && !errors.Is(err, os.ErrNotExist): |
| + http.Error(w, "registry error", http.StatusInternalServerError) |
| + return |
| } |
| if err := store.DeleteTimer(host, name); err != nil { |
| http.Error(w, "registry error", http.StatusInternalServerError) |
diff --git a/internal/schedule/control_test.go b/internal/schedule/control_test.go| index 3349da0d0..13bd412b5 100644 |
| --- a/internal/schedule/control_test.go |
| +++ b/internal/schedule/control_test.go |
| @@ -110,6 +110,18 @@ func TestControlRefusals(t *testing.T) { |
| } |
| } |
| +func TestControlDeleteRefusesDeclaredSchedule(t *testing.T) { |
| + reg, mux := controlFixture(t) |
| + reg.UpsertTimer(registry.Timer{Host: "a.bab.oncarlos.com", Name: "sync", Kind: KindSchedule, Spec: "every 6h0m0s", Path: "/s", NextAt: 1}) |
| + if rec := do(mux, "DELETE", "/schedule/sync", "Bearer tok", ""); rec.Code != http.StatusConflict { |
| + t.Fatalf("delete declared schedule = %d, want 409 (%s)", rec.Code, rec.Body) |
| + } |
| + tm, err := reg.GetTimer("a.bab.oncarlos.com", "sync") |
| + if err != nil || tm == nil || tm.Kind != KindSchedule { |
| + t.Fatalf("row did not survive the refused delete: %v, err=%v", tm, err) |
| + } |
| +} |
| + |
| func TestControlCapsOncePerHost(t *testing.T) { |
| reg, mux := controlFixture(t) |
| for i := 0; i < MaxOncePerHost; i++ { |