Let 1.5 record what FOS tells it: taskLog gets a type and a body (schema 280) - #1210
Merged
Conversation
…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
force-pushed
the
claude/fos-reports-1.5
branch
from
August 19, 2026 12:26
1767711 to
f2191e2
Compare
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
taskLogrow, typed, with the text in it -- the one correlated with thetask, 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') andlogText(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:
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