Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/claw-api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,10 @@ func (h *apiHandler) handleScheduleFire(w http.ResponseWriter, r *http.Request,
writeJSONError(w, http.StatusNotFound, err.Error())
return
}
if errors.Is(err, errScheduleInvocationInFlight) {
writeJSONError(w, http.StatusConflict, err.Error())
return
}
writeJSONError(w, http.StatusInternalServerError, err.Error())
return
}
Expand Down
28 changes: 28 additions & 0 deletions cmd/claw-api/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,34 @@ func TestHandlerScheduleFireBypassesCalendarWhenRequested(t *testing.T) {
}
}

func TestHandlerScheduleFireReturnsConflictWhenInvocationIsInFlight(t *testing.T) {
manifest := sampleScheduleManifest()
state := newTestScheduleStateStore(t, manifest)
scheduler, err := newScheduler(manifest, nil, state, io.Discard)
if err != nil {
t.Fatalf("newScheduler: %v", err)
}
entry := scheduler.lookupEntry("westin-open")
scheduler.mu.Lock()
entry.inFlight = true
scheduler.mu.Unlock()
h := newScheduleTestHandler(t, manifest, state, scheduler, clawapi.Principal{
Name: "westin-ops",
Token: "capi_westin_ops",
Verbs: []string{clawapi.VerbScheduleControl},
Services: []string{"westin"},
})

w := postJSON(t, h, "/schedule/westin-open/fire", map[string]any{}, "capi_westin_ops")

if w.Code != http.StatusConflict {
t.Fatalf("expected 409, got %d body=%s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), errScheduleInvocationInFlight.Error()) {
t.Fatalf("expected in-flight error body, got %s", w.Body.String())
}
}

func TestHandlerRestartRequiresRestartVerb(t *testing.T) {
h := newWriteHandler(t, t.TempDir(), clawapi.Principal{
Name: "reader",
Expand Down
8 changes: 5 additions & 3 deletions cmd/claw-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,20 @@ func run(args []string, stdout, stderr io.Writer) error {

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
var serveErr error
select {
case sig := <-sigCh:
fmt.Fprintf(stderr, "received signal %s, shutting down\n", sig)
case err := <-errCh:
stopRuntime()
return err
serveErr = err
}

stopRuntime()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return server.Shutdown(ctx)
serverErr := server.Shutdown(ctx)
schedulerErr := scheduler.Wait(ctx)
return errors.Join(serveErr, serverErr, schedulerErr)
}

func loadManifest(path string) (*manifestpkg.PodManifest, error) {
Expand Down
1 change: 1 addition & 0 deletions cmd/claw-api/schedule_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ func normalizeInvocationState(state *schedulepkg.InvocationState) {
state.LastAttemptedAt = nilIfZeroTime(state.LastAttemptedAt)
state.LastFiredAt = nilIfZeroTime(state.LastFiredAt)
state.LastSkippedAt = nilIfZeroTime(state.LastSkippedAt)
state.LastSuppressedAt = nilIfZeroTime(state.LastSuppressedAt)
state.NextFireAt = nilIfZeroTime(state.NextFireAt)
}

Expand Down
2 changes: 2 additions & 0 deletions cmd/claw-api/schedule_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func TestScheduleStateStoreNormalizesZeroTimePointers(t *testing.T) {
state.LastAttemptedAt = &zero
state.LastFiredAt = &zero
state.LastSkippedAt = &zero
state.LastSuppressedAt = &zero
state.NextFireAt = &zero
file.Invocations["never"] = state
}); err != nil {
Expand All @@ -103,6 +104,7 @@ func TestScheduleStateStoreNormalizesZeroTimePointers(t *testing.T) {
state.LastAttemptedAt != nil ||
state.LastFiredAt != nil ||
state.LastSkippedAt != nil ||
state.LastSuppressedAt != nil ||
state.NextFireAt != nil {
t.Fatalf("expected zero time pointers to be nil, got %+v", state)
}
Expand Down
Loading
Loading