runs: add non-destructive flag + operator Cancel button
Non-destructive pre-declares "don't touch the disks" on Start: the Storage stage skips wipe-probe, badblocks -w, and write-mode fio, and reports a read-only summary. Runs a new non_destructive column; threaded through Claim → agent tests.Deps → Storage stage. Cancel halts an in-flight run. The orchestrator transitions to a new StateCancelled via TriggerOperatorCancelled (valid from any active state); the agent's next heartbeat returns cmd=cancel_stage, which fires a stored CancelFunc on the per-stage context. Stage subprocesses spawned with exec.CommandContext die with the context, the agent posts a cancelled outcome, then powers the host off. Destructive stages mid-run may leave the host in an intermediate state — the UI confirm dialog warns the operator; recovery is manual for now. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -207,11 +207,12 @@ func (a *Agent) Claim(w http.ResponseWriter, r *http.Request) {
|
||||
iperfPort = 5201
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"ok": true,
|
||||
"run_id": runID,
|
||||
"stages": store.DefaultStageOrder,
|
||||
"expected_disks": expectedDisks,
|
||||
"iperf_port": iperfPort,
|
||||
"ok": true,
|
||||
"run_id": runID,
|
||||
"stages": store.DefaultStageOrder,
|
||||
"expected_disks": expectedDisks,
|
||||
"iperf_port": iperfPort,
|
||||
"non_destructive": run.NonDestructive,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -236,6 +237,10 @@ func (a *Agent) Heartbeat(w http.ResponseWriter, r *http.Request) {
|
||||
case run.State == model.StateCompleted:
|
||||
// Pipeline succeeded — agent should power the host down.
|
||||
cmd = "shutdown"
|
||||
case run.State == model.StateCancelled:
|
||||
// Operator clicked Cancel — agent cancels the active stage ctx,
|
||||
// posts a cancelled outcome, and powers off.
|
||||
cmd = "cancel_stage"
|
||||
case run.State == model.StateFailedHolding || run.State == model.StateReleased:
|
||||
cmd = "abort"
|
||||
case run.FailedStage == "Storage" && overrideWipeSet(run.OverrideFlagsJSON):
|
||||
|
||||
@@ -46,7 +46,7 @@ func setupAgent(t *testing.T) (*api.Agent, int64, string) {
|
||||
if err != nil {
|
||||
t.Fatalf("issue token: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(context.Background(), hostID, hash)
|
||||
runID, err := runs.Create(context.Background(), hostID, hash, false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func TestUIHeartbeat_QueuedDispatches(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func TestUIHeartbeat_WaitingRebootRetries(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
@@ -241,7 +241,7 @@ func TestUIHeartbeat_CompletedRunIsIdle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestHostDetail_OK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, id, "deadbeef")
|
||||
runID, err := runs.Create(ctx, id, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func TestHostDetail_LogTabsRendered(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, id, "cafef00d")
|
||||
runID, err := runs.Create(ctx, id, "cafef00d", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ func fullAgent(t *testing.T) (*api.Agent, int64, string) {
|
||||
if err != nil {
|
||||
t.Fatalf("issue token: %v", err)
|
||||
}
|
||||
runID, err := runStore.Create(context.Background(), hostID, hash)
|
||||
runID, err := runStore.Create(context.Background(), hostID, hash, false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -191,21 +191,20 @@ func (u *UI) StartRun(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Guard: refuse to start a second run while one is still active.
|
||||
if latest, err := u.Runs.LatestForHost(r.Context(), hostID); err == nil && latest != nil {
|
||||
switch latest.State {
|
||||
case model.StateCompleted, model.StateReleased, model.StateFailed, model.StateFailedHolding:
|
||||
// ok to start fresh
|
||||
default:
|
||||
if !latest.State.IsTerminal() {
|
||||
http.Error(w, "host already has an active run", http.StatusConflict)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nonDestructive := r.PostFormValue("non_destructive") == "1"
|
||||
|
||||
_, hash, err := orchestrator.IssueRunToken()
|
||||
if err != nil {
|
||||
http.Error(w, "token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
runID, err := u.Runs.Create(r.Context(), hostID, hash)
|
||||
runID, err := u.Runs.Create(r.Context(), hostID, hash, nonDestructive)
|
||||
if err != nil {
|
||||
http.Error(w, "create run: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -471,6 +470,35 @@ func (u *UI) OverrideWipeStorage(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// CancelRun halts an in-flight run. Transitions the run to
|
||||
// StateCancelled; the next agent heartbeat receives cmd=cancel_stage
|
||||
// which cancels the stage ctx on the agent side. Destructive stages
|
||||
// mid-run can leave the host in an intermediate state — the confirm
|
||||
// dialog in the UI warns the operator.
|
||||
func (u *UI) CancelRun(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
hostID, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, "bad host id", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
latest, err := u.Runs.LatestForHost(r.Context(), hostID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if latest == nil || latest.State.IsTerminal() {
|
||||
http.Error(w, "no active run to cancel", http.StatusConflict)
|
||||
return
|
||||
}
|
||||
if _, err := u.Runner.Transition(r.Context(), latest.ID, orchestrator.TriggerOperatorCancelled); err != nil {
|
||||
http.Error(w, "cancel: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("ui: cancelled run %d for host %d", latest.ID, hostID)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (u *UI) DeleteHost(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE runs ADD COLUMN non_destructive INTEGER NOT NULL DEFAULT 0;
|
||||
@@ -73,6 +73,7 @@ func NewRouter(d Deps) http.Handler {
|
||||
r.Get("/hosts/{id}", d.UI.HostDetail)
|
||||
r.Post("/hosts/{id}/delete", d.UI.DeleteHost)
|
||||
r.Post("/hosts/{id}/start", d.UI.StartRun)
|
||||
r.Post("/hosts/{id}/cancel", d.UI.CancelRun)
|
||||
r.Post("/hosts/{id}/override-wipe", d.UI.OverrideWipeStorage)
|
||||
r.Get("/reports/{runID}", d.UI.Report)
|
||||
r.Get("/register/quick.sh", d.UI.QuickRegisterScript)
|
||||
|
||||
@@ -38,8 +38,17 @@ const (
|
||||
StateFailed RunState = "Failed"
|
||||
StateFailedHolding RunState = "FailedHolding"
|
||||
StateReleased RunState = "Released"
|
||||
StateCancelled RunState = "Cancelled"
|
||||
)
|
||||
|
||||
func (s RunState) IsTerminal() bool {
|
||||
switch s {
|
||||
case StateCompleted, StateFailed, StateFailedHolding, StateReleased, StateCancelled:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Run struct {
|
||||
ID int64
|
||||
HostID int64
|
||||
@@ -53,6 +62,7 @@ type Run struct {
|
||||
ReportPath string
|
||||
HoldIP string
|
||||
OverrideFlagsJSON string
|
||||
NonDestructive bool
|
||||
}
|
||||
|
||||
type StageState string
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestDispatcher_TransitionsToWaitingRebootNoWoL(t *testing.T) {
|
||||
d, _, runs, hostID, cleanup := setupPickNext(t)
|
||||
defer cleanup()
|
||||
ctx := context.Background()
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func TestDispatcher_FailsStaleHeartbeat(t *testing.T) {
|
||||
if err := hosts.UpdateLastSeen(ctx, "aa:bb:cc:dd:ee:50", time.Now().UTC().Add(-5*time.Minute)); err != nil {
|
||||
t.Fatalf("stamp stale: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ func TestDispatcher_FailsNeverSeenHost(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, neverID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, neverID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestPublishesTileAndPipelineOnTransition(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func TestCompleteStagePublishesPipeline(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef")
|
||||
runID, err := runs.Create(ctx, hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ const (
|
||||
TriggerStageFailed Trigger = "StageFailed" // a stage reported failure
|
||||
TriggerStageCompleted Trigger = "StageCompleted" // a stage reported success → advance
|
||||
TriggerAllStagesPassed Trigger = "AllStagesPassed" // final stage passed
|
||||
TriggerOperatorReleased Trigger = "OperatorReleased" // user clicked Release on a held run
|
||||
TriggerOperatorOverride Trigger = "OperatorOverride" // user overrode a held stage; re-enter it
|
||||
TriggerOperatorReleased Trigger = "OperatorReleased" // user clicked Release on a held run
|
||||
TriggerOperatorOverride Trigger = "OperatorOverride" // user overrode a held stage; re-enter it
|
||||
TriggerOperatorCancelled Trigger = "OperatorCancelled" // user clicked Cancel on an active run
|
||||
)
|
||||
|
||||
// stageStates maps the canonical stage name (from DefaultStageOrder)
|
||||
@@ -63,9 +64,10 @@ var table = map[Trigger]transition{
|
||||
TriggerRebootCommanded: {from: []model.RunState{model.StateQueued}, to: model.StateWaitingReboot},
|
||||
TriggerPXEObserved: {from: []model.RunState{model.StateWaitingReboot, model.StateWaitingWoL, model.StateBooting}, to: model.StateBooting},
|
||||
TriggerAgentClaimed: {from: []model.RunState{model.StateBooting, model.StateWaitingReboot, model.StateWaitingWoL}, to: model.StateInventoryCheck},
|
||||
TriggerStageFailed: {from: allActiveStates(), to: model.StateFailedHolding},
|
||||
TriggerAllStagesPassed: {from: []model.RunState{model.StateReporting}, to: model.StateCompleted},
|
||||
TriggerOperatorReleased: {from: []model.RunState{model.StateFailedHolding}, to: model.StateReleased},
|
||||
TriggerStageFailed: {from: allActiveStates(), to: model.StateFailedHolding},
|
||||
TriggerAllStagesPassed: {from: []model.RunState{model.StateReporting}, to: model.StateCompleted},
|
||||
TriggerOperatorReleased: {from: []model.RunState{model.StateFailedHolding}, to: model.StateReleased},
|
||||
TriggerOperatorCancelled: {from: allActiveStates(), to: model.StateCancelled},
|
||||
}
|
||||
|
||||
// Next computes the target state for a trigger against the current state.
|
||||
|
||||
+19
-15
@@ -14,12 +14,16 @@ type Runs struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
func (r *Runs) Create(ctx context.Context, hostID int64, tokenHash string) (int64, error) {
|
||||
func (r *Runs) Create(ctx context.Context, hostID int64, tokenHash string, nonDestructive bool) (int64, error) {
|
||||
now := time.Now().UTC()
|
||||
nd := 0
|
||||
if nonDestructive {
|
||||
nd = 1
|
||||
}
|
||||
res, err := r.DB.ExecContext(ctx, `
|
||||
INSERT INTO runs(host_id, state, agent_token_hash, next_boot_target, started_at)
|
||||
VALUES(?,?,?,?,?)
|
||||
`, hostID, string(model.StateQueued), tokenHash, "linux", now)
|
||||
INSERT INTO runs(host_id, state, agent_token_hash, next_boot_target, started_at, non_destructive)
|
||||
VALUES(?,?,?,?,?,?)
|
||||
`, hostID, string(model.StateQueued), tokenHash, "linux", now, nd)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("insert run: %w", err)
|
||||
}
|
||||
@@ -103,14 +107,14 @@ func (r *Runs) Get(ctx context.Context, id int64) (*model.Run, error) {
|
||||
SELECT id, host_id, state, COALESCE(result,''), COALESCE(failed_stage,''),
|
||||
COALESCE(next_boot_target,''), agent_token_hash, started_at,
|
||||
completed_at, COALESCE(report_path,''), COALESCE(hold_ip,''),
|
||||
COALESCE(override_flags_json,'')
|
||||
COALESCE(override_flags_json,''), COALESCE(non_destructive,0)
|
||||
FROM runs WHERE id = ?
|
||||
`, id)
|
||||
var run model.Run
|
||||
var completedAt sql.NullTime
|
||||
err := row.Scan(&run.ID, &run.HostID, &run.State, &run.Result, &run.FailedStage,
|
||||
&run.NextBootTarget, &run.AgentTokenHash, &run.StartedAt,
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON)
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON, &run.NonDestructive)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
@@ -129,7 +133,7 @@ func (r *Runs) LatestForHost(ctx context.Context, hostID int64) (*model.Run, err
|
||||
SELECT id, host_id, state, COALESCE(result,''), COALESCE(failed_stage,''),
|
||||
COALESCE(next_boot_target,''), agent_token_hash, started_at,
|
||||
completed_at, COALESCE(report_path,''), COALESCE(hold_ip,''),
|
||||
COALESCE(override_flags_json,'')
|
||||
COALESCE(override_flags_json,''), COALESCE(non_destructive,0)
|
||||
FROM runs WHERE host_id = ?
|
||||
ORDER BY id DESC LIMIT 1
|
||||
`, hostID)
|
||||
@@ -137,7 +141,7 @@ func (r *Runs) LatestForHost(ctx context.Context, hostID int64) (*model.Run, err
|
||||
var completedAt sql.NullTime
|
||||
err := row.Scan(&run.ID, &run.HostID, &run.State, &run.Result, &run.FailedStage,
|
||||
&run.NextBootTarget, &run.AgentTokenHash, &run.StartedAt,
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON)
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON, &run.NonDestructive)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -156,9 +160,9 @@ func (r *Runs) Active(ctx context.Context) ([]model.Run, error) {
|
||||
SELECT id, host_id, state, COALESCE(result,''), COALESCE(failed_stage,''),
|
||||
COALESCE(next_boot_target,''), agent_token_hash, started_at,
|
||||
completed_at, COALESCE(report_path,''), COALESCE(hold_ip,''),
|
||||
COALESCE(override_flags_json,'')
|
||||
COALESCE(override_flags_json,''), COALESCE(non_destructive,0)
|
||||
FROM runs
|
||||
WHERE state NOT IN ('Completed','Released')
|
||||
WHERE state NOT IN ('Completed','Released','Cancelled')
|
||||
ORDER BY id
|
||||
`)
|
||||
if err != nil {
|
||||
@@ -171,7 +175,7 @@ func (r *Runs) Active(ctx context.Context) ([]model.Run, error) {
|
||||
var completedAt sql.NullTime
|
||||
if err := rows.Scan(&run.ID, &run.HostID, &run.State, &run.Result, &run.FailedStage,
|
||||
&run.NextBootTarget, &run.AgentTokenHash, &run.StartedAt,
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON); err != nil {
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON, &run.NonDestructive); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if completedAt.Valid {
|
||||
@@ -189,7 +193,7 @@ func (r *Runs) Active(ctx context.Context) ([]model.Run, error) {
|
||||
func (r *Runs) CompletedOlderThan(ctx context.Context, cutoff time.Time) ([]int64, error) {
|
||||
rows, err := r.DB.QueryContext(ctx, `
|
||||
SELECT id FROM runs
|
||||
WHERE state IN ('Completed','Released','FailedHolding')
|
||||
WHERE state IN ('Completed','Released','FailedHolding','Cancelled')
|
||||
AND COALESCE(completed_at, started_at) < ?
|
||||
ORDER BY id
|
||||
`, cutoff)
|
||||
@@ -215,17 +219,17 @@ func (r *Runs) FindActiveByMAC(ctx context.Context, mac string) (*model.Run, err
|
||||
SELECT r.id, r.host_id, r.state, COALESCE(r.result,''), COALESCE(r.failed_stage,''),
|
||||
COALESCE(r.next_boot_target,''), r.agent_token_hash, r.started_at,
|
||||
r.completed_at, COALESCE(r.report_path,''), COALESCE(r.hold_ip,''),
|
||||
COALESCE(r.override_flags_json,'')
|
||||
COALESCE(r.override_flags_json,''), COALESCE(r.non_destructive,0)
|
||||
FROM runs r
|
||||
JOIN hosts h ON h.id = r.host_id
|
||||
WHERE h.mac = ? AND r.state NOT IN ('Completed','Released')
|
||||
WHERE h.mac = ? AND r.state NOT IN ('Completed','Released','Cancelled')
|
||||
ORDER BY r.id DESC LIMIT 1
|
||||
`, mac)
|
||||
var run model.Run
|
||||
var completedAt sql.NullTime
|
||||
err := row.Scan(&run.ID, &run.HostID, &run.State, &run.Result, &run.FailedStage,
|
||||
&run.NextBootTarget, &run.AgentTokenHash, &run.StartedAt,
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON)
|
||||
&completedAt, &run.ReportPath, &run.HoldIP, &run.OverrideFlagsJSON, &run.NonDestructive)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func seedRun(t *testing.T, runs *store.Runs) (int64, int64) {
|
||||
if err != nil {
|
||||
t.Fatalf("create host: %v", err)
|
||||
}
|
||||
runID, err := runs.Create(context.Background(), hostID, "deadbeef")
|
||||
runID, err := runs.Create(context.Background(), hostID, "deadbeef", false)
|
||||
if err != nil {
|
||||
t.Fatalf("create run: %v", err)
|
||||
}
|
||||
|
||||
@@ -89,7 +89,11 @@ templ HostDetail(d HostDetailData) {
|
||||
<h2>Actions</h2>
|
||||
<div class="detail-actions-row">
|
||||
if canStart(d.Tile) {
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/start", d.Tile.Host.ID)) } class="inline">
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/start", d.Tile.Host.ID)) } class="inline detail-start-form">
|
||||
<label class="detail-nd-toggle">
|
||||
<input type="checkbox" name="non_destructive" value="1"/>
|
||||
Non-destructive (skip wipe-probe + disk writes)
|
||||
</label>
|
||||
<button type="submit">Start vetting</button>
|
||||
</form>
|
||||
} else if canStartIfOnline(d.Tile.Latest) {
|
||||
@@ -97,6 +101,11 @@ templ HostDetail(d HostDetailData) {
|
||||
} else {
|
||||
<button type="button" disabled>Run in flight</button>
|
||||
}
|
||||
if canCancel(d.Tile.Latest) {
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/cancel", d.Tile.Host.ID)) } class="inline" onsubmit="return confirm('Cancel run? Destructive stages may leave the host in an intermediate state requiring manual cleanup.');">
|
||||
<button type="submit" class="danger">Cancel run</button>
|
||||
</form>
|
||||
}
|
||||
if canOverrideWipe(d.Tile.Latest) {
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/override-wipe", d.Tile.Host.ID)) } class="inline">
|
||||
<button type="submit" class="danger">Override wipe-probe</button>
|
||||
|
||||
@@ -319,7 +319,7 @@ func HostDetail(d HostDetailData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"inline\"><button type=\"submit\">Start vetting</button></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"inline detail-start-form\"><label class=\"detail-nd-toggle\"><input type=\"checkbox\" name=\"non_destructive\" value=\"1\"> Non-destructive (skip wipe-probe + disk writes)</label> <button type=\"submit\">Start vetting</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -334,153 +334,172 @@ func HostDetail(d HostDetailData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if canOverrideWipe(d.Tile.Latest) {
|
||||
if canCancel(d.Tile.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<form method=\"post\" action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 templ.SafeURL
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/hosts/%d/override-wipe", d.Tile.Host.ID)))
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/hosts/%d/cancel", d.Tile.Host.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 101, Col: 104}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 105, Col: 97}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" class=\"inline\"><button type=\"submit\" class=\"danger\">Override wipe-probe</button></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" class=\"inline\" onsubmit=\"return confirm('Cancel run? Destructive stages may leave the host in an intermediate state requiring manual cleanup.');\"><button type=\"submit\" class=\"danger\">Cancel run</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if hasReport(d.Tile.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<a class=\"button-like\" href=\"")
|
||||
if canOverrideWipe(d.Tile.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<form method=\"post\" action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 templ.SafeURL
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/reports/%d", d.Tile.Latest.ID)))
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/hosts/%d/override-wipe", d.Tile.Host.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 106, Col: 95}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 110, Col: 104}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" target=\"_blank\" rel=\"noopener\">View report</a>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" class=\"inline\"><button type=\"submit\" class=\"danger\">Override wipe-probe</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<form method=\"post\" action=\"")
|
||||
if hasReport(d.Tile.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<a class=\"button-like\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 templ.SafeURL
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/reports/%d", d.Tile.Latest.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 115, Col: 95}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\" target=\"_blank\" rel=\"noopener\">View report</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<form method=\"post\" action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 templ.SafeURL
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/hosts/%d/delete", d.Tile.Host.ID)))
|
||||
var templ_7745c5c3_Var22 templ.SafeURL
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/hosts/%d/delete", d.Tile.Host.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 108, Col: 96}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 117, Col: 96}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\" class=\"inline\"><button type=\"submit\" class=\"danger\">Delete host</button></form></div></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\" class=\"inline\"><button type=\"submit\" class=\"danger\">Delete host</button></form></div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(d.SpecDiffs) > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<section class=\"detail-section detail-diffs\"><details")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<section class=\"detail-section detail-diffs\"><details")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if hasCriticalDiff(d.SpecDiffs) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, " open")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, " open")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "><summary><h2>Spec diffs (")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "><summary><h2>Spec diffs (")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(d.SpecDiffs)))
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(d.SpecDiffs)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 117, Col: 68}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 126, Col: 68}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, ")</h2></summary><ul class=\"diff-list\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, ")</h2></summary><ul class=\"diff-list\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, diff := range d.SpecDiffs {
|
||||
var templ_7745c5c3_Var23 = []any{"diff-row", "diff-" + diff.Severity}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var23...)
|
||||
var templ_7745c5c3_Var24 = []any{"diff-row", "diff-" + diff.Severity}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var24...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<li class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var23).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\"><div class=\"diff-field\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<li class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(diff.Field)
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var24).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 121, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</div><div class=\"diff-expected\">expected: <code>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\"><div class=\"diff-field\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(diff.Expected)
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(diff.Field)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 122, Col: 67}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 130, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</code></div><div class=\"diff-actual\">actual: <code>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</div><div class=\"diff-expected\">expected: <code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(diff.Actual)
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(diff.Expected)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 123, Col: 61}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 131, Col: 67}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</code></div></li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</code></div><div class=\"diff-actual\">actual: <code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(diff.Actual)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 132, Col: 61}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</code></div></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</ul></details></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</ul></details></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -491,43 +510,43 @@ func HostDetail(d HostDetailData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "<section class=\"detail-section detail-host-meta\"><details><summary><h2>Host details</h2></summary> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<section class=\"detail-section detail-host-meta\"><details><summary><h2>Host details</h2></summary> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if d.Tile.Host.Notes != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<div class=\"detail-notes\"><h3>Notes</h3><p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<div class=\"detail-notes\"><h3>Notes</h3><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(d.Tile.Host.Notes)
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(d.Tile.Host.Notes)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 141, Col: 29}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 150, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</p></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<div class=\"detail-spec\"><h3>Expected spec</h3><pre class=\"detail-spec-yaml\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div class=\"detail-spec\"><h3>Expected spec</h3><pre class=\"detail-spec-yaml\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(d.Tile.Host.ExpectedSpecYAML)
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(d.Tile.Host.ExpectedSpecYAML)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 146, Col: 66}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 155, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</pre></div></details></section></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</pre></div></details></section></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -573,157 +592,157 @@ func LogTabs(runID int64, replay string) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var30 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var30 == nil {
|
||||
templ_7745c5c3_Var30 = templ.NopComponent
|
||||
templ_7745c5c3_Var31 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var31 == nil {
|
||||
templ_7745c5c3_Var31 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<section class=\"detail-section log-section\"><h2>Log</h2><div class=\"log-tabs\"><input type=\"radio\" name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 174, Col: 62}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\" id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<section class=\"detail-section log-section\"><h2>Log</h2><div class=\"log-tabs\"><input type=\"radio\" name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-all", runID))
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 174, Col: 106}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 183, Col: 62}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" class=\"log-tab-input log-tab-all\" checked> <label for=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var33 string
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-all", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 175, Col: 52}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 183, Col: 106}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\" class=\"log-tab-label\">All</label> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" class=\"log-tab-input log-tab-all\" checked> <label for=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var34 string
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-all", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 184, Col: 52}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" class=\"log-tab-label\">All</label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, s := range store.DefaultStageOrder {
|
||||
var templ_7745c5c3_Var34 = []any{"log-tab-input", "log-tab-" + s}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var34...)
|
||||
var templ_7745c5c3_Var35 = []any{"log-tab-input", "log-tab-" + s}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var35...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<input type=\"radio\" name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var35 string
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 177, Col: 63}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<input type=\"radio\" name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var36 string
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-%s", runID, s))
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 177, Col: 109}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 186, Col: 63}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var37 string
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var34).String())
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-%s", runID, s))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 1, Col: 0}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 186, Col: 109}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\"> <label for=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var38 string
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-%s", runID, s))
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var35).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 178, Col: 55}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" class=\"log-tab-label\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\"> <label for=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var39 string
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(s)
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-tab-%d-%s", runID, s))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 178, Col: 83}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 187, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</label>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\" class=\"log-tab-label\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(s)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 187, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</label>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<div class=\"log-pane log-pane-all\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 182, Col: 37}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" sse-swap=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<div class=\"log-pane log-pane-all\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var41 string
|
||||
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 183, Col: 43}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 191, Col: 37}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" hx-swap=\"beforeend show:bottom\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\" sse-swap=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var42 string
|
||||
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-%d", runID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 192, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\" hx-swap=\"beforeend show:bottom\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -731,61 +750,61 @@ func LogTabs(runID int64, replay string) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, s := range store.DefaultStageOrder {
|
||||
var templ_7745c5c3_Var42 = []any{"log-pane", "log-pane-" + s}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var42...)
|
||||
var templ_7745c5c3_Var43 = []any{"log-pane", "log-pane-" + s}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var43...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var43 string
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var42).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\" id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var44 string
|
||||
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-%d-%s", runID, s))
|
||||
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var43).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 191, Col: 44}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" sse-swap=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\" id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var45 string
|
||||
templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-%d-%s", runID, s))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 192, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 200, Col: 44}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\" hx-swap=\"beforeend show:bottom\"></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\" sse-swap=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var46 string
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("log-%d-%s", runID, s))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_detail.templ`, Line: 201, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\" hx-swap=\"beforeend show:bottom\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</div></section>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</div></section>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -29,11 +29,19 @@ templ HostTile(t TileData) {
|
||||
</header>
|
||||
<div class="tile-primary-action">
|
||||
if canStart(t) {
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/start", t.Host.ID)) } class="inline">
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/start", t.Host.ID)) } class="inline tile-start-form">
|
||||
<label class="tile-nd-toggle">
|
||||
<input type="checkbox" name="non_destructive" value="1"/>
|
||||
Non-destructive
|
||||
</label>
|
||||
<button type="submit">Start vetting</button>
|
||||
</form>
|
||||
} else if canStartIfOnline(t.Latest) {
|
||||
<button type="button" disabled title="host is not heartbeating — install the reporter via /register/quick.sh on the target host">Start vetting</button>
|
||||
} else if canCancel(t.Latest) {
|
||||
<form method="post" action={ templ.SafeURL(fmt.Sprintf("/hosts/%d/cancel", t.Host.ID)) } class="inline tile-cancel-form" onsubmit="return confirm('Cancel run? Destructive stages may leave the host in an intermediate state requiring manual cleanup.');">
|
||||
<button type="submit" class="danger">Cancel run</button>
|
||||
</form>
|
||||
} else if hasReport(t.Latest) {
|
||||
<a class="button-like" href={ templ.SafeURL(fmt.Sprintf("/reports/%d", t.Latest.ID)) } target="_blank" rel="noopener">View report</a>
|
||||
}
|
||||
@@ -76,11 +84,15 @@ func canStartIfOnline(r *model.Run) bool {
|
||||
if r == nil {
|
||||
return true
|
||||
}
|
||||
switch r.State {
|
||||
case model.StateCompleted, model.StateReleased, model.StateFailed, model.StateFailedHolding:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return r.State.IsTerminal()
|
||||
}
|
||||
|
||||
// canCancel is true for any non-terminal run — the Cancel button shows
|
||||
// whenever the pipeline is live (Queued through the stage states). The
|
||||
// handler refuses the action once the run enters a terminal state, so
|
||||
// the render decision just has to mirror that.
|
||||
func canCancel(r *model.Run) bool {
|
||||
return r != nil && !r.State.IsTerminal()
|
||||
}
|
||||
|
||||
func tileStatus(r *model.Run) string {
|
||||
@@ -103,7 +115,7 @@ func tileMood(r *model.Run) string {
|
||||
return "pass"
|
||||
case model.StateFailed, model.StateFailedHolding:
|
||||
return "fail"
|
||||
case model.StateReleased:
|
||||
case model.StateReleased, model.StateCancelled:
|
||||
return "idle"
|
||||
}
|
||||
return "active"
|
||||
|
||||
@@ -190,7 +190,7 @@ func HostTile(t TileData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"inline\"><button type=\"submit\">Start vetting</button></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"inline tile-start-form\"><label class=\"tile-nd-toggle\"><input type=\"checkbox\" name=\"non_destructive\" value=\"1\"> Non-destructive</label> <button type=\"submit\">Start vetting</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -199,26 +199,44 @@ func HostTile(t TileData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else if hasReport(t.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<a class=\"button-like\" href=\"")
|
||||
} else if canCancel(t.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<form method=\"post\" action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 templ.SafeURL
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/reports/%d", t.Latest.ID)))
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/hosts/%d/cancel", t.Host.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_tile.templ`, Line: 38, Col: 88}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_tile.templ`, Line: 42, Col: 90}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" target=\"_blank\" rel=\"noopener\">View report</a>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"inline tile-cancel-form\" onsubmit=\"return confirm('Cancel run? Destructive stages may leave the host in an intermediate state requiring manual cleanup.');\"><button type=\"submit\" class=\"danger\">Cancel run</button></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else if hasReport(t.Latest) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<a class=\"button-like\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 templ.SafeURL
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/reports/%d", t.Latest.ID)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/templates/host_tile.templ`, Line: 46, Col: 88}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" target=\"_blank\" rel=\"noopener\">View report</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</div></article>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div></article>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -261,11 +279,15 @@ func canStartIfOnline(r *model.Run) bool {
|
||||
if r == nil {
|
||||
return true
|
||||
}
|
||||
switch r.State {
|
||||
case model.StateCompleted, model.StateReleased, model.StateFailed, model.StateFailedHolding:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return r.State.IsTerminal()
|
||||
}
|
||||
|
||||
// canCancel is true for any non-terminal run — the Cancel button shows
|
||||
// whenever the pipeline is live (Queued through the stage states). The
|
||||
// handler refuses the action once the run enters a terminal state, so
|
||||
// the render decision just has to mirror that.
|
||||
func canCancel(r *model.Run) bool {
|
||||
return r != nil && !r.State.IsTerminal()
|
||||
}
|
||||
|
||||
func tileStatus(r *model.Run) string {
|
||||
@@ -288,7 +310,7 @@ func tileMood(r *model.Run) string {
|
||||
return "pass"
|
||||
case model.StateFailed, model.StateFailedHolding:
|
||||
return "fail"
|
||||
case model.StateReleased:
|
||||
case model.StateReleased, model.StateCancelled:
|
||||
return "idle"
|
||||
}
|
||||
return "active"
|
||||
|
||||
Reference in New Issue
Block a user