Name the columns in the fogstorage grant probe, so a schema step stops breaking it - #1209
Merged
Merged
Conversation
…s breaking it Regression from #1208. An upgrade on a server whose grants are perfectly correct now stops and demands the database root password: * Granting access to fogstorage database user................. To improve the overall security the installer will restrict permissions for the *fogstorage* database user. Please provide the database *root* user password. Nothing is wrong with the grants. installFOGDB()'s probe writes a throwaway row as fogstorage to find out whether that user still holds INSERT, and reads ANY failure as "the grants need redoing". The probe was a positional INSERT: INSERT INTO taskLog VALUES ( 0, 0, 3, '127.0.0.1', NOW(), 'fog-install-probe'); #1208 added logType and logText, taking taskLog from six columns to eight, so that statement is now error 1136, "Column count doesn't match value count". The grants are untouched -- reaching a column-count error means the connection and the INSERT privilege both worked, which is exactly what the probe was asking about. THE SECOND TIME. Schema 336 made taskID the int(11) it always held, and the marker '999test' in that column became error 1265 under STRICT_TRANS_TABLES. Same prompt, same misleading symptom. That was repaired by moving the marker into createdBy -- which fixed the instance and left the positional list, so the next ADD COLUMN reopened it. Naming the columns is the repair that holds: a column added later takes its default and the INSERT does not care. VERIFIED on a live 1.6 server at schema 338, both statements run as fogstorage inside a transaction and rolled back so nothing persisted: positional -> ERROR 1136 (21S01): Column count doesn't match value count named -> ROW_COUNT() = 1 tests/installer-db-probes-name-columns.test.php fails on any positional INSERT anywhere in the installer, not just this one, and separately on the probe losing its column list or disappearing. Mutation-verified against the bug exactly as shipped, against a new positional INSERT elsewhere, and against the probe being deleted. 1.5 is deliberately not touched. dev-branch's copy is positional too, but its taskLog still has the six columns that statement was written for and 1.5 is not gaining any, so there is nothing broken there to fix. Co-Authored-By: Claude <noreply@anthropic.com>
mastacontrola
pushed a commit
that referenced
this pull request
Aug 19, 2026
…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
added a commit
that referenced
this pull request
Aug 19, 2026
…ema 280) (#1210) 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: 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.
Regression from #1208. An upgrade on a server whose grants are perfectly
correct now stops and demands the database root password:
Nothing is wrong with the grants. installFOGDB()'s probe writes a throwaway
row as fogstorage to find out whether that user still holds INSERT, and reads
ANY failure as "the grants need redoing". The probe was a positional INSERT:
#1208 added logType and logText, taking taskLog from six columns to eight, so
that statement is now error 1136, "Column count doesn't match value count".
The grants are untouched -- reaching a column-count error means the connection
and the INSERT privilege both worked, which is exactly what the probe was
asking about.
THE SECOND TIME. Schema 336 made taskID the int(11) it always held, and the
marker '999test' in that column became error 1265 under STRICT_TRANS_TABLES.
Same prompt, same misleading symptom. That was repaired by moving the marker
into createdBy -- which fixed the instance and left the positional list, so
the next ADD COLUMN reopened it. Naming the columns is the repair that holds:
a column added later takes its default and the INSERT does not care.
VERIFIED on a live 1.6 server at schema 338, both statements run as fogstorage
inside a transaction and rolled back so nothing persisted:
tests/installer-db-probes-name-columns.test.php fails on any positional INSERT
anywhere in the installer, not just this one, and separately on the probe
losing its column list or disappearing. Mutation-verified against the bug
exactly as shipped, against a new positional INSERT elsewhere, and against the
probe being deleted.
1.5 is deliberately not touched. dev-branch's copy is positional too, but its
taskLog still has the six columns that statement was written for and 1.5 is
not gaining any, so there is nothing broken there to fix.
Why a Normal Server needs a
fogstorageuser at allWorth stating, because the prompt makes it look like the installer is asking
for a storage node's credential on a machine that has no storage node.
It isn't. The password being asked for is the database root password, and
the account being repaired is
fogstorage— a deliberately unprivilegeddatabase user with
SELECTon the schema andINSERT/UPDATEon the eleventables imaging actually writes. A Normal Server is its own master storage
node, so the node-side code paths run here and authenticate as that user; the
point of it is that the credential sitting in a node's config cannot drop
tables or read the user table.
So the account is real and the grants are wanted. What was wrong was only the
probe that decides whether they need rebuilding.
🤖 Generated with Claude Code
https://claude.ai/code/session_013mJVe4CpK3rRbi9H5GubXd