Repository navigation
[BUG] Docker instance with leading/trailing spaces in Name is synchronized but never scanned or sampled #266
Description
Summary
The configuration synchronization stores a Docker instance under its trimmed Name, but the runtime container scan and the resource-statistics collection look the stored instance up in a dictionary keyed by the untrimmed configured Name. A configured name with leading or trailing whitespace (e.g. "Name": "Production ") is therefore created in the database, passes validation, but is skipped on every scan and every sampling round as "configuration missing". Both sides must use the same, trimmed name.
Area
docs/areas/docker-instances.md(Configured instances, Container snapshots, Resource statistics)- Security area: None
Current behavior
Synchronization trims the name, both when creating/matching and when computing obsolete instances:
src/DockerUpdateGuard/Images/InstanceDiscoveryService.cs:91–var configuredInstanceName = configuredInstance.Name.Trim();andName = configuredInstanceName(line 100)src/DockerUpdateGuard/Images/InstanceDiscoveryService.cs:169–configuredInstances.Select(instance => instance.Name.Trim())
The validator also compares trimmed names for duplicates (src/DockerUpdateGuard/Configuration/DockerUpdateGuardOptionsValidator.cs:283).
The consumers build their lookup from the raw configured names and query it with the stored (trimmed) name:
// RuntimeContainerScanOrchestrator.cs:1179 / ResourceStatisticsCollector.cs:226
var optionsByName = _optionsMonitor.CurrentValue.DockerInstances.ToDictionary(entity => entity.Name, StringComparer.OrdinalIgnoreCase);
...
if (optionsByName.TryGetValue(dockerInstance.Name, out var configuredInstance) == false)src/DockerUpdateGuard/Images/RuntimeContainerScanOrchestrator.cs:1179and:1197-1204– the instance is counted as skipped andRuntimeContainerScanSkippedConfigurationMissingis logged.src/DockerUpdateGuard/Images/ResourceStatisticsCollector.cs:226and:233-237– the instance is silently skipped.
Reproduction:
- Configure
DockerUpdateGuard:DockerInstances:0:Name="Production "(trailing space) with a reachableBaseUrl. - Start the application. Validation passes; the Docker Instances page lists
Production. - Every runtime container scan logs "configuration missing" for
Productionand records no scan run; no resource samples are ever written. The instance stays empty forever.
Secondary effect of the same inconsistency: the untrimmed name is also passed into DockerInstanceClient (src/DockerUpdateGuard/Docker/DockerInstanceClient.cs:251 connection-pool key, :289 the once-per-instance SkipCertificateValidation warning) and into Portainer log messages and error texts, so logs and messages show a different spelling than the UI.
Expected behavior
- A Docker instance is identified everywhere by its trimmed
Name, compared case-insensitively (asdocs/areas/docker-instances.mdalready states). - A configured instance whose
Namehas surrounding whitespace is scanned and sampled exactly like the same instance without the whitespace. - Log entries, scan-run messages and integration error messages use the trimmed name.
Acceptance criteria
- A configured instance with
Name=" Production "is scanned byRuntimeContainerScanOrchestrator.ScanAllAsync(one scan run recorded, no "configuration missing" skip). - The same instance gets resource samples from
ResourceStatisticsCollector.CollectAsync. - Unit tests (MSTest, naming per
docs/UNIT_TESTS.md), for example:RuntimeContainerScanOrchestratorScanAllWithUntrimmedConfiguredNameScansInstanceResourceStatisticsCollectorCollectWithUntrimmedConfiguredNameRecordsSamples(there are no tests forResourceStatisticsCollectoryet; add the test class)- if the fix normalizes options centrally: a test that the bound/post-configured options carry the trimmed name.
- Existing behavior for names without whitespace is unchanged (existing tests stay green).
- The area document
docs/areas/docker-instances.mddescribes the new behavior and its Known gaps entry for this issue (currently Open questions item 1) is removed.
Implementation notes
Options (recommendation first):
- Recommended – normalize once at the options boundary. Add a
PostConfigure<DockerUpdateGuardOptions>(insrc/DockerUpdateGuard/ServiceCollectionExtensions.cs, next toAddOptions<DockerUpdateGuardOptions>()at line 54) that trimsNameof everyDockerInstanceOptions. Every consumer (InstanceDiscoveryService, both lookups,DockerInstanceClient,PortainerClient, logging) then sees the same name, and the explicit.Trim()calls inInstanceDiscoveryServicebecome redundant (they may stay). Post-configure runs on every reload, so a reloaded configuration is normalized too. Make sure the validator still sees an empty/whitespace-only name as missing (it does,IsNullOrWhiteSpace). - Alternative – build both dictionaries with
entity => entity.Name.Trim(). Smaller, but leaves the untrimmed name inDockerInstanceClientand Portainer messages and keeps the trimming rule scattered.
Pitfalls:
ToDictionarythrows on duplicate keys; with trimmed keys, two entries differing only by whitespace would collide — the validator already rejects that as a duplicate, keep it that way.- Keep the case-insensitive comparison (
StringComparer.OrdinalIgnoreCase).
No configuration, README or migration impact. Must not change: the synchronization semantics (removal of an instance no longer configured), the guarantees in .squad/project.md.
Related
- F-029 (P3) —
ImageRegistrationServicesearches with an untrimmed name but creates a trimmed one #4 (same untrimmed-search/trimmed-create pattern inImageRegistrationService) - PR [Docs] Describe the current behavior by area #263 (area documents)
Generated by Claude Code
Activity