fix(inputs.upsd): Enforce string type for vendor and product IDs#18626
fix(inputs.upsd): Enforce string type for vendor and product IDs#18626majiayu000 wants to merge 3 commits intoinfluxdata:masterfrom
Conversation
The go.nut library auto-detects numeric-looking values and converts them to integers, stripping leading zeros (e.g. "0764" becomes 764). This causes type conflicts in InfluxDB when different UPS vendors report non-numeric IDs for the same fields. Convert ups.vendorid, ups.productid, driver.parameter.vendorid, and driver.parameter.productid to strings before writing, following the same pattern used for ups.firmware. Signed-off-by: majiayu000 <1835304752@qq.com>
Signed-off-by: majiayu000 <1835304752@qq.com>
srebhan
left a comment
There was a problem hiding this comment.
Thanks for your contribution @majiayu000!
|
Hi @majiayu000, thanks for this fix. After discussion with @srebhan, we'd like to land this behind a config option so users can migrate gracefully rather than getting a silent type change on upgrade. Here's the plan: This PR (step 1): Add a config option (e.g. Future PRs (for our tracking, not this one):
One small thing worth knowing: because go.nut parses Let me know if you have questions on the option naming or wording of the warning. |
Per maintainer feedback, introduce the fix behind an opt-in config
option so users get a migration path rather than a silent type change
on upgrade. This is step 1 of a 3-step migration:
Step 1 (this commit): Add `stringify_ids` bool, default false to
preserve the legacy numeric behavior. Emit a warning in Init()
when the option is unset noting the default will flip in a
future release.
Step 2 (future): flip the default to true, deprecate the option.
Step 3 (future): remove the option.
When `stringify_ids = true`, ups.vendorid, ups.productid,
driver.parameter.vendorid and driver.parameter.productid are forced
to string before the ForceFloat conversion, so the numeric-looking
values that go.nut auto-casts to int64 (e.g. "0764" -> 764) and the
non-numeric values ("ABCD") are all emitted with the same field type.
Note: because go.nut parses "0764" into int64(764) before Telegraf
sees it, the emitted string is "764"; leading zeros are not
recoverable at this layer. Documented in the plugin README.
Revert CyberPowerSystems_CP900EPFCLCD_full/expected.out to the
pre-PR integer form so the default-off behavior is covered, and
add a CyberPowerSystems_CP900EPFCLCD_full_stringify_ids testcase
that exercises the opt-in path.
Signed-off-by: majiayu000 <1835304752@qq.com>
|
Thanks @skartikey, updated the PR to follow the 3-step migration plan. Pushed as 8f42f14. Changes in this push:
Happy to adjust the warning wording or the option name if you'd prefer something else. Also let me know if you'd rather have |
|
Download PR build artifacts for linux_amd64.tar.gz, darwin_arm64.tar.gz, and windows_amd64.zip. 🥳 This pull request decreases the Telegraf binary size by -1.05 % for linux amd64 (new size: 300.7 MB, nightly size 303.9 MB) 📦 Click here to get additional PR build artifactsArtifact URLs |
skartikey
left a comment
There was a problem hiding this comment.
@majiayu000 some more comments.
| behavior, but will flip to `true` in a future release. If the option is left | ||
| unset, a warning is logged on startup. | ||
|
|
||
| > **Note:** the NUT library parses `"0764"` into `int64(764)` before Telegraf |
There was a problem hiding this comment.
Please switch the > **Note:** block to GitHub alert syntax (> [!NOTE]). Matches the style guide and renders as a proper callout on GitHub.
| unset, a warning is logged on startup. | ||
|
|
||
| > **Note:** the NUT library parses `"0764"` into `int64(764)` before Telegraf | ||
| > sees it, so the stringified value will be `"764"` — leading zeros are lost |
There was a problem hiding this comment.
nit: replace the em-dash in "764" — leading zeros are lost with a semicolon or period.
| u.dumped = make(map[string]bool) | ||
|
|
||
| if u.StringifyIDs == nil { | ||
| u.Log.Warn("'stringify_ids' is not set explicitly; vendor/product IDs may be emitted as int64 " + |
There was a problem hiding this comment.
Would you mind adding a small test asserting the warning fires when stringify_ids is unset (and stays silent when set to true/false)? The warning is load-bearing for the migration; a regression test will keep it honest across future refactors.
| u.Log.Warn("'stringify_ids' is not set explicitly; vendor/product IDs may be emitted as int64 " + | ||
| "when the underlying value looks numeric, causing type conflicts between UPS devices. " + | ||
| "The default will change to 'true' in a future release. " + | ||
| "Set 'stringify_ids = true' to opt into the new behavior now, or 'stringify_ids = false' to keep the legacy behavior and silence this warning.") |
There was a problem hiding this comment.
The warning string is a 4-line concat; consider tightening the wording
| u.Log.Warn("'stringify_ids' is not set explicitly; vendor/product IDs may be emitted as int64 " + | |
| "when the underlying value looks numeric, causing type conflicts between UPS devices. " + | |
| "The default will change to 'true' in a future release. " + | |
| "Set 'stringify_ids = true' to opt into the new behavior now, or 'stringify_ids = false' to keep the legacy behavior and silence this warning.") | |
| u.Log.Warn("Option 'stringify_ids' is not set; the default will flip from 'false' to 'true' " + | |
| "in a future release to fix vendor/product ID type conflicts. " + | |
| "Set it explicitly to lock in behavior and silence this warning.") |
| if err == nil { | ||
| v = str | ||
| } | ||
| } else if u.ForceFloat { |
There was a problem hiding this comment.
please keep the stringify/force-float comments in the same position relative to their if blocks (both above).
| ## Force vendor/product IDs to always be emitted as strings to avoid type | ||
| ## conflicts between UPS devices with numeric-looking IDs (e.g. "0764", | ||
| ## auto-converted to int64 by the NUT client library) and UPS devices with | ||
| ## non-numeric IDs (e.g. "ABCD"). Affected fields: ups_vendorid, ups_productid, | ||
| ## driver_parameter_vendorid, driver_parameter_productid. | ||
| ## Note: leading zeros are lost on the wire (e.g. "0764" becomes "764"). | ||
| ## If left unset, a warning is logged. The default will change to true in a | ||
| ## future release. | ||
| # stringify_ids = false |
There was a problem hiding this comment.
Could you trim the comment block down to 2-3 lines? The detailed rationale is already in the README section. The sample.conf should stay scannable.
| ## Force vendor/product IDs to always be emitted as strings to avoid type | |
| ## conflicts between UPS devices with numeric-looking IDs (e.g. "0764", | |
| ## auto-converted to int64 by the NUT client library) and UPS devices with | |
| ## non-numeric IDs (e.g. "ABCD"). Affected fields: ups_vendorid, ups_productid, | |
| ## driver_parameter_vendorid, driver_parameter_productid. | |
| ## Note: leading zeros are lost on the wire (e.g. "0764" becomes "764"). | |
| ## If left unset, a warning is logged. The default will change to true in a | |
| ## future release. | |
| # stringify_ids = false | |
| ## Emit vendor/product IDs as strings regardless of their value. Avoids | |
| ## type conflicts when some UPS devices report numeric-looking IDs and | |
| ## others report alphanumeric. See README for migration notes. | |
| # stringify_ids = false |
| driver.parameter.vendor: NUMBER | ||
| driver.parameter.vendorid: NUMBER | ||
| driver.state: NUMBER | ||
| driver.version: |
There was a problem hiding this comment.
types.dev inconsistency: the sibling _full_stringify_ids/types.dev:27 has driver.version: (trailing space, no type) while this file has driver.version: (no trailing space). Make them identical, pick one and apply everywhere.
Summary
The go.nut library auto-detects numeric-looking values using a regex and converts them to int64. This means a vendorid like "0764" becomes int64(764), but a non-numeric vendorid like "ABCD" stays a string. When both types of UPS devices report to the same InfluxDB bucket, you get type conflicts on the vendorid/productid fields.
This adds a
stringFieldSetthat forcesups.vendorid,ups.productid,driver.parameter.vendorid, anddriver.parameter.productidto always be emitted as strings, following the sameinternal.ToStringpattern already used forups.firmware. The string check runs before theForceFloatconversion so these fields aren't accidentally cast to float.Also adds a test case with non-numeric vendor/product IDs to confirm type consistency.
Checklist
Related issues
resolves #18308