Skip to content

Let 1.5 record what FOS tells it: taskLog gets a type and a body (schema 280) - #1210

Merged
mastacontrola merged 1 commit into
dev-branchfrom
claude/fos-reports-1.5
Aug 19, 2026
Merged

Let 1.5 record what FOS tells it: taskLog gets a type and a body (schema 280)#1210
mastacontrola merged 1 commit into
dev-branchfrom
claude/fos-reports-1.5

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

Ported from working-1.6 (#1207, #1208, #1209). This is a feature on a
maintenance branch, and the reason it belongs here anyway is that FOS is not
branched. FOGProject/fos#152 makes handleError() AND handleWarning() post a
report to whatever server the machine booted from, and a 1.5 server is just as
likely to be that server. Without this the POST reaches a 404 and 1.5 keeps
the behaviour it has had since the beginning: a machine stops mid-image and
says nothing to anyone. HOST_IMAGE_FAIL has two listeners in this tree (slack,
pushbullet) and no core caller, so neither has ever fired on any 1.5 server.

WHAT ARRIVES. service/taskerror.php takes mac, sysuuid, a type of error or
warning, the text, and the script that raised it. A report lands in three
places, none of which is the task's state:

a taskLog row, typed, with the text in it -- the one correlated with the
task, carrying taskID and the state the task was in;
/var/log/fog/fos/fosreports.log, which the Log Viewer lists like any other;
HOST_IMAGE_FAIL -- errors only, imaging tasks only.

SCHEMA 280 adds logType (default 'state') and logText (NULL) to taskLog.
Every row in that table so far is a state transition, which is what the
default backfills them as, so TaskingElement::taskLog() is untouched. A
closure, not a bare ALTER, because ADD COLUMN has no IF NOT EXISTS below
MariaDB 10.0.2 and a re-run has to converge.

A warning is recorded and fires nothing, because the machine carried on;
announcing a failed deploy for a task that went on to succeed would be worse
than silence. A report with NO type is an error -- on this branch that is the
normal case rather than the exotic one, because a FOS newer than the server is
what 1.5 will usually be talking to.

THE INSTALLER PROBE HAD TO COME WITH IT, and this is the part that is not
optional. installFOGDB() probes fogstorage's INSERT privilege with a throwaway
row and reads any failure as "the grants need redoing", which is what makes it
demand a database root password. That probe was positional:

INSERT INTO taskLog VALUES ( 0, '999test', 3, '127.0.0.1', NOW(), 'fog');

Six values into what schema 280 makes an eight column table is error 1136,
"Column count doesn't match value count" -- so this schema step alone would
have made every 1.5 upgrade stop and ask for a root password nobody needs to
type, on servers whose grants are perfectly correct. 1.6 shipped exactly that
regression and had already been bitten once before by the same statement; see
#1209. Naming the columns is the repair that holds, and it lands here in the
same commit as the cause rather than after it.

THE LOG DIRECTORY is created by the installer as the web user with
httpd_sys_rw_content_t, in its own subdirectory: $servicelogs is root's and
holds the daemons' logs, and rotation renames and unlinks. error_log() stays
as the fallback so a server updated but not yet re-installed still records
something. 'fos' is added to all THREE lists 1.5 keeps -- StorageNode::
_getData(), status/getfiles.php and status/logtoview.php -- because they fail
differently: miss the first two and the selector has no entry, miss the last
and it answers "Invalid Folder".

VERIFIED against a throwaway copy of a real 1.5 database (2079 hosts, schema
278) in a container, with the web tier served from a shadow tree, so the live
1.5 install was never written to:

step 280 applied through its own closure, backfilled all 7 existing rows to
'state', and ran clean a second time;
the OLD probe against the migrated table -> ERROR 1136, as predicted;
the new one -> ROW_COUNT() = 1;
error on a Deploy task -> row, log line, HOST_IMAGE_FAIL to a listener;
warning on the same task -> row and log line, no event;
no type at all -> recorded as an error, event fired;
error on a non-imaging task -> row and log line, no event;
an embedded newline arrived flattened to spaces;
the file rotated to .1 once it passed SERVICE_LOG_SIZE.

WHAT IS DELIBERATELY NOT PORTED. The slack and pushbullet listeners are left
exactly as they are. 1.6's #1202 rewrote them to name the image and the
reason; here they keep reading only $data['HostName'], and the extra payload
keys are simply ignored. Making them fire at all is the change this branch
needed; changing what they say is a separate one.

Two tests, both mutation-verified: task-error-report.test.php (the sanitizer
run for real against stubbed base classes, plus the type routing, the row's
position relative to both gates, and all three log-path lists) and
installer-db-probes-name-columns.test.php (any positional INSERT anywhere in
the installer).


Base is dev-branch. Pairs with FOGProject/fos#152 (merged) and mirrors
#1208 / #1209 on working-1.6.

🤖 Generated with Claude Code

https://claude.ai/code/session_013mJVe4CpK3rRbi9H5GubXd

…ema 280)

Ported from working-1.6 (#1207, #1208, #1209). This is a feature on a
maintenance branch, and the reason it belongs here anyway is that FOS is not
branched. FOGProject/fos#152 makes handleError() AND handleWarning() post a
report to whatever server the machine booted from, and a 1.5 server is just as
likely to be that server. Without this the POST reaches a 404 and 1.5 keeps
the behaviour it has had since the beginning: a machine stops mid-image and
says nothing to anyone. HOST_IMAGE_FAIL has two listeners in this tree (slack,
pushbullet) and no core caller, so neither has ever fired on any 1.5 server.

WHAT ARRIVES. service/taskerror.php takes mac, sysuuid, a type of error or
warning, the text, and the script that raised it. A report lands in three
places, none of which is the task's state:

  a `taskLog` row, typed, with the text in it -- the one correlated with the
    task, carrying taskID and the state the task was in;
  /var/log/fog/fos/fosreports.log, which the Log Viewer lists like any other;
  HOST_IMAGE_FAIL -- errors only, imaging tasks only.

SCHEMA 280 adds `logType` (default 'state') and `logText` (NULL) to taskLog.
Every row in that table so far is a state transition, which is what the
default backfills them as, so TaskingElement::taskLog() is untouched. A
closure, not a bare ALTER, because ADD COLUMN has no IF NOT EXISTS below
MariaDB 10.0.2 and a re-run has to converge.

A warning is recorded and fires nothing, because the machine carried on;
announcing a failed deploy for a task that went on to succeed would be worse
than silence. A report with NO type is an error -- on this branch that is the
normal case rather than the exotic one, because a FOS newer than the server is
what 1.5 will usually be talking to.

THE INSTALLER PROBE HAD TO COME WITH IT, and this is the part that is not
optional. installFOGDB() probes fogstorage's INSERT privilege with a throwaway
row and reads any failure as "the grants need redoing", which is what makes it
demand a database root password. That probe was positional:

    INSERT INTO taskLog VALUES ( 0, '999test', 3, '127.0.0.1', NOW(), 'fog');

Six values into what schema 280 makes an eight column table is error 1136,
"Column count doesn't match value count" -- so this schema step alone would
have made every 1.5 upgrade stop and ask for a root password nobody needs to
type, on servers whose grants are perfectly correct. 1.6 shipped exactly that
regression and had already been bitten once before by the same statement; see
same commit as the cause rather than after it.

THE LOG DIRECTORY is created by the installer as the web user with
httpd_sys_rw_content_t, in its own subdirectory: $servicelogs is root's and
holds the daemons' logs, and rotation renames and unlinks. error_log() stays
as the fallback so a server updated but not yet re-installed still records
something. 'fos' is added to all THREE lists 1.5 keeps -- StorageNode::
_getData(), status/getfiles.php and status/logtoview.php -- because they fail
differently: miss the first two and the selector has no entry, miss the last
and it answers "Invalid Folder".

VERIFIED against a throwaway copy of a real 1.5 database (2079 hosts, schema
278) in a container, with the web tier served from a shadow tree, so the live
1.5 install was never written to:

  step 280 applied through its own closure, backfilled all 7 existing rows to
    'state', and ran clean a second time;
  the OLD probe against the migrated table -> ERROR 1136, as predicted;
    the new one -> ROW_COUNT() = 1;
  error on a Deploy task    -> row, log line, HOST_IMAGE_FAIL to a listener;
  warning on the same task  -> row and log line, no event;
  no type at all            -> recorded as an error, event fired;
  error on a non-imaging task -> row and log line, no event;
  an embedded newline arrived flattened to spaces;
  the file rotated to .1 once it passed SERVICE_LOG_SIZE.

WHAT IS DELIBERATELY NOT PORTED. The slack and pushbullet listeners are left
exactly as they are. 1.6's #1202 rewrote them to name the image and the
reason; here they keep reading only $data['HostName'], and the extra payload
keys are simply ignored. Making them fire at all is the change this branch
needed; changing what they say is a separate one.

Two tests, both mutation-verified: task-error-report.test.php (the sanitizer
run for real against stubbed base classes, plus the type routing, the row's
position relative to both gates, and all three log-path lists) and
installer-db-probes-name-columns.test.php (any positional INSERT anywhere in
the installer).

Co-Authored-By: Claude <noreply@anthropic.com>
@mastacontrola
mastacontrola force-pushed the claude/fos-reports-1.5 branch from 1767711 to f2191e2 Compare August 19, 2026 12:26
@mastacontrola
mastacontrola merged commit 2624732 into dev-branch Aug 19, 2026
3 checks passed
@mastacontrola
mastacontrola deleted the claude/fos-reports-1.5 branch August 19, 2026 12:27
mastacontrola added a commit that referenced this pull request Aug 19, 2026
Ported from 1.6 #1211, completing the port of #1210. A FOS error report is
recorded there, and the task itself was left exactly where it was: Queued or
In-Progress, forever. The task page still said the machine was working on
it, and because Host::loadTask() and getActiveTaskCount() count those states
as live, the host could not be re-tasked until somebody noticed and
cancelled it by hand.

Adds taskStates row 6, Failed, and moves the task to it when FOS reports an
error. A warning never does -- a warning means FOS carried on.

Not Cancelled, which was the alternative. Cancelled means an administrator
stopped it; losing the difference between "somebody stopped this" and "this
broke" costs the operator the one fact they open the task list to find.

Additive on a maintenance branch, which is why it is portable at all: every
"is this task live" test in the tree is an allowlist -- getQueuedStates()
plus getProgressState() -- so a state nobody listed is inactive by
construction, and no call site had to learn about it. 1.5 has no history
pane, so a Failed task is as invisible in the UI as a Complete or Cancelled
one already is; what changes is that the host is free and the task has
stopped claiming otherwise.

The state is written for every task type, not just imaging ones: a Memtest
the host died on is as finished as a deploy. Only HOST_IMAGE_FAIL stays
imaging-specific. Guarded on the taskStates row existing, so a web tree
updated ahead of its database leaves the task alone rather than pointing it
at a state that is not there.

Verified against an isolated copy of the 1.5 database (podman MariaDB,
shadow web tree, throwaway host on a locally-administered MAC, 2079 real
hosts): a warning left the task Queued and wrote a row typed warning; an
error moved it to 6 and dropped the host's active task count to zero; error,
warning and unknown-MAC all still answer an identical `##`. Step 281 applies
clean on a database at 278. Seven assertions mutation-tested.

Co-authored-by: JJ Fullmer <7743340+darksidemilk@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants