Skip to content

Name the columns in the fogstorage grant probe, so a schema step stops breaking it - #1209

Merged
mastacontrola merged 1 commit into
working-1.6from
claude/installer-probe-name-columns
Aug 19, 2026
Merged

Name the columns in the fogstorage grant probe, so a schema step stops breaking it#1209
mastacontrola merged 1 commit into
working-1.6from
claude/installer-probe-name-columns

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

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.

Why a Normal Server needs a fogstorage user at all

Worth 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 unprivileged
database user with SELECT on the schema and INSERT/UPDATE on the eleven
tables 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

…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
mastacontrola merged commit 87b6a50 into working-1.6 Aug 19, 2026
3 checks passed
@mastacontrola
mastacontrola deleted the claude/installer-probe-name-columns branch August 19, 2026 11:57
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>
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