Compare commits
2 Commits
8f75318e8c
...
554ce2b93a
| Author | SHA1 | Date | |
|---|---|---|---|
| 554ce2b93a | |||
| 3f3b977fb0 |
@@ -92,6 +92,16 @@ func DisplayStats(rt *domain.JobRuntime) string {
|
||||
rt.RunCount, rt.FailCount, rt.LastDurationMS, rt.AvgDurationMS, rt.MaxDurationMS)
|
||||
}
|
||||
|
||||
// DisplayOverlapPolicy formats a job's effective overlap policy for the details
|
||||
// panel. When the job has its own policy it is shown as-is; when empty (inherit
|
||||
// global), the global default is shown with "(global default)" appended.
|
||||
func DisplayOverlapPolicy(job domain.Job, globalPolicy domain.OverlapPolicy) string {
|
||||
if p := domain.OverlapPolicy(strings.TrimSpace(job.OverlapPolicy)); p != "" {
|
||||
return string(p)
|
||||
}
|
||||
return string(globalPolicy) + " (global default)"
|
||||
}
|
||||
|
||||
// DisplayIndex returns the position of jobIndex in the given slice of indexes,
|
||||
// or 0 if not found.
|
||||
func DisplayIndex(indexes []int, jobIndex int) int {
|
||||
|
||||
+14
-3
@@ -67,7 +67,6 @@ func (s *Service) RunDue(now time.Time) {
|
||||
var startErr error
|
||||
if !s.paused {
|
||||
sequential := s.store.Config.ExecutionMode == domain.ExecutionModeSequential
|
||||
queue := s.store.Config.OverlapPolicy == domain.OverlapPolicyQueue
|
||||
running := s.anyRunningLocked()
|
||||
for index := range s.jobs {
|
||||
job := &s.jobs[index]
|
||||
@@ -77,8 +76,9 @@ func (s *Service) RunDue(now time.Time) {
|
||||
}
|
||||
if runtime.LastState == "Running" {
|
||||
// The job came due again while its own run is still in flight.
|
||||
// Apply the overlap policy and step past this occurrence.
|
||||
if queue {
|
||||
// Apply the effective overlap policy and step past this
|
||||
// occurrence.
|
||||
if s.effectiveOverlapPolicy(job) == domain.OverlapPolicyQueue {
|
||||
runtime.Pending = true
|
||||
}
|
||||
s.advanceNextDueLocked(job, runtime, now)
|
||||
@@ -165,6 +165,17 @@ func (s *Service) executeRun(ctx context.Context, jobCopy domain.Job, trigger st
|
||||
s.emit(JobChanged{JobID: jobCopy.ID})
|
||||
}
|
||||
|
||||
// effectiveOverlapPolicy resolves the overlap policy that actually governs a
|
||||
// job: the job's own value when set, otherwise the global Config default. An
|
||||
// empty Job.OverlapPolicy means "inherit the global default", which is why
|
||||
// normalizeJobs leaves it empty rather than backfilling the configured value.
|
||||
func (s *Service) effectiveOverlapPolicy(job *domain.Job) domain.OverlapPolicy {
|
||||
if policy := domain.OverlapPolicy(strings.TrimSpace(job.OverlapPolicy)); policy != "" {
|
||||
return policy
|
||||
}
|
||||
return s.store.Config.OverlapPolicy
|
||||
}
|
||||
|
||||
// anyRunningLocked reports whether any loaded job is currently in the "Running"
|
||||
// state. It backs the sequential-mode guards in RunNow and RunDue. The caller
|
||||
// must hold mu.
|
||||
|
||||
@@ -13,6 +13,10 @@ import (
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// overlapPolicyInherit is the display label used when a job should inherit the
|
||||
// global overlap policy. It maps to an empty Job.OverlapPolicy on save.
|
||||
const overlapPolicyInherit = "(Use global default)"
|
||||
|
||||
// showJobDialog opens a create/edit form for a single job. onSave is called
|
||||
// with the populated job only when the user clicks Save and all fields pass
|
||||
// validation.
|
||||
@@ -40,6 +44,15 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
|
||||
startOnly.SetChecked(current.StartOnly)
|
||||
enabled := widget.NewCheck("Enabled", nil)
|
||||
enabled.SetChecked(current.Enabled)
|
||||
overlapSelect := widget.NewSelect(
|
||||
[]string{overlapPolicyInherit, string(domain.OverlapPolicySkip), string(domain.OverlapPolicyQueue)},
|
||||
nil,
|
||||
)
|
||||
overlapSelected := overlapPolicyInherit
|
||||
if current.OverlapPolicy != "" {
|
||||
overlapSelected = current.OverlapPolicy
|
||||
}
|
||||
overlapSelect.SetSelected(overlapSelected)
|
||||
|
||||
form := dialog.NewForm(
|
||||
title,
|
||||
@@ -52,6 +65,7 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
|
||||
widget.NewFormItem("Command", commandRow),
|
||||
widget.NewFormItem("Arguments", argumentsEntry),
|
||||
widget.NewFormItem("", startOnly),
|
||||
widget.NewFormItem("Overlap policy", overlapSelect),
|
||||
widget.NewFormItem("", enabled),
|
||||
},
|
||||
func(saved bool) {
|
||||
@@ -75,6 +89,10 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
|
||||
current.Arguments = strings.TrimSpace(argumentsEntry.Text)
|
||||
current.StartOnly = startOnly.Checked
|
||||
current.Enabled = enabled.Checked
|
||||
current.OverlapPolicy = overlapSelect.Selected
|
||||
if current.OverlapPolicy == overlapPolicyInherit {
|
||||
current.OverlapPolicy = ""
|
||||
}
|
||||
// The dialog only edits durable configuration. Runtime status is
|
||||
// initialized (new jobs) or updated (edits) by the caller against the
|
||||
// runtime map, keyed by job ID.
|
||||
|
||||
@@ -76,6 +76,7 @@ func newJobsView(w fyne.Window, svc *app.Service) (fyne.CanvasObject, func()) {
|
||||
nextRunLabel := newJobDetailLabel(selectedRuntime.NextRun)
|
||||
stateLabel := newJobDetailLabel(selectedRuntime.LastState)
|
||||
statsLabel := newJobDetailLabel(app.DisplayStats(selectedRuntime))
|
||||
overlapPolicyLabel := newJobDetailLabel(app.DisplayOverlapPolicy(jobs[selected], svc.Store().Config.OverlapPolicy))
|
||||
schedulerState := widget.NewLabel("Scheduler running")
|
||||
commandOutput := widget.NewTextGrid()
|
||||
commandOutput.SetText(selectedRuntime.Output)
|
||||
@@ -112,6 +113,7 @@ func newJobsView(w fyne.Window, svc *app.Service) (fyne.CanvasObject, func()) {
|
||||
nextRunLabel.SetText("")
|
||||
stateLabel.SetText("")
|
||||
statsLabel.SetText("")
|
||||
overlapPolicyLabel.SetText("")
|
||||
commandOutput.SetText("")
|
||||
selectedLogs = nil
|
||||
return
|
||||
@@ -125,6 +127,7 @@ func newJobsView(w fyne.Window, svc *app.Service) (fyne.CanvasObject, func()) {
|
||||
commandLabel.SetText(current.Command)
|
||||
argumentsLabel.SetText(app.DisplayArguments(current.Arguments))
|
||||
runModeLabel.SetText(app.DisplayRunMode(current))
|
||||
overlapPolicyLabel.SetText(app.DisplayOverlapPolicy(current, svc.Store().Config.OverlapPolicy))
|
||||
lastRunLabel.SetText(rt.LastRun)
|
||||
nextRunLabel.SetText(rt.NextRun)
|
||||
stateLabel.SetText(rt.LastState)
|
||||
@@ -356,6 +359,7 @@ func newJobsView(w fyne.Window, svc *app.Service) (fyne.CanvasObject, func()) {
|
||||
detailRow("Command", commandLabel),
|
||||
detailRow("Arguments", argumentsLabel),
|
||||
detailRow("Run mode", runModeLabel),
|
||||
detailRow("Overlap policy", overlapPolicyLabel),
|
||||
detailRow("Last run", lastRunLabel),
|
||||
detailRow("Next run", nextRunLabel),
|
||||
detailRow("State", stateLabel),
|
||||
|
||||
@@ -126,7 +126,7 @@ func settingsView(w fyne.Window, svc *app.Service) fyne.CanvasObject {
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabelWithStyle("Queue", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
settingsRow("Execution mode", container.New(minWidthLayout{width: settingsControlWidth}, executionModeSelect)),
|
||||
settingsRow("Overlap policy", container.New(minWidthLayout{width: settingsControlWidth}, overlapPolicySelect)),
|
||||
settingsRow("Default overlap policy", container.New(minWidthLayout{width: settingsControlWidth}, overlapPolicySelect)),
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabelWithStyle("Storage", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
settingsRow("Config YAML", widget.NewLabel(store.Paths.ConfigPath)),
|
||||
|
||||
Reference in New Issue
Block a user