Summary
Mongo container creation never issues an explicit docker pull. MongoDbConnectionBroker.EnsureContainerStartedAsync relies on the implicit pull performed by docker create when an image is absent locally. Because the default image tag is the moving mongodb/mongodb-atlas-local:latest, an already-present-but-stale image is never refreshed, first-run pulls have no observable/diagnosable step, and pull failures surface only as a generic docker create failure.
Root Cause
Phantom.Workspaces.Data.MongoDB\MongoDbConnectionBroker.cs (lines ~153-169)
EnsureContainerStartedAsync tries StartAsync, and on InvalidOperationException calls CreateAsync then StartAsync again. There is NO pull step:
try
{
await _containerEngine.StartAsync(connectionDefinition.ContainerName, cancellationToken).ConfigureAwait(false);
}
catch (InvalidOperationException)
{
var containerDefinition = _containerDefinitionGenerator.Generate(connectionDefinition);
await _containerEngine.CreateAsync(containerDefinition, cancellationToken).ConfigureAwait(false);
await _containerEngine.StartAsync(connectionDefinition.ContainerName, cancellationToken).ConfigureAwait(false);
}
Phantom.Workspaces.Containers\WindowsDockerDesktopEngine.cs (lines ~42-99)
CreateAsync builds a docker create --name <name> --network <net> [-e ...] [--mount ...] [-p ...] <image> argument list and runs it via _commandRunner.RunAsync(...):
var arguments = new List<string>
{
"create",
"--name",
definition.ContainerName,
"--network",
definition.NetworkType.ToString().ToLowerInvariant(),
};
// ... env vars, mounts, port mappings ...
arguments.Add(definition.ImageName);
await RunAndEnsureSuccessAsync(arguments, cancellationToken).ConfigureAwait(false);
StartAsync/StopAsync/DestroyAsync run docker start|stop|rm. No pull command exists anywhere in Phantom.Workspaces.Containers.
Phantom.Workspaces.Data.MongoDB\MongoDBContainerDefinitionGenerator.cs (line ~13)
The default image is a moving :latest tag:
public const string DefaultMongoImageName = "mongodb/mongodb-atlas-local:latest";
Why This Is a Bug (Impact)
docker create/docker start only pull an image when it is ABSENT locally. Because the tag is a moving :latest, an already-present-but-stale image is never refreshed -> users silently run an outdated Atlas Local image. An explicit docker pull is the standard way to fetch the newest :latest.
- The implicit pull that does happen (first run, image absent) occurs inside
docker create. Image pulls can take minutes; there is no dedicated, observable pull step, so first-run latency looks like a hang and pull progress/errors are buried. (A sibling bug about docker stdout/stderr not being logged was searched for among open issues but not found at filing time; cross-link if one is later created.)
- Pull failures (auth, registry down, rate-limit) surface only as a generic
docker create failure rather than a clear "could not pull image" diagnostic.
Affected Files
| File |
Role |
Phantom.Workspaces.Data.MongoDB\MongoDbConnectionBroker.cs |
EnsureContainerStartedAsync must issue a pull before create |
Phantom.Workspaces.Containers\WindowsDockerDesktopEngine.cs |
Add docker pull invocation |
Phantom.Workspaces.Containers\ContainerEngine.cs / DockerDesktopEngine.cs (base abstraction) |
Add PullAsync to the abstraction |
Phantom.Workspaces.Data.MongoDB\MongoDBContainerDefinitionGenerator.cs |
Supplies the :latest image name that must be pulled |
Design / Fix
Recommended
Add an explicit image-pull step to the container engine and invoke it before create in the Mongo broker path.
- Add
PullAsync(string imageName, CancellationToken cancellationToken) to the ContainerEngine abstraction (Phantom.Workspaces.Containers\ContainerEngine.cs, inherited by DockerDesktopEngine). Implement it in WindowsDockerDesktopEngine as docker pull <image> via _commandRunner.RunAsync(["pull", imageName], cancellationToken), surfacing failure the same way RunAndEnsureSuccessAsync does (throw InvalidOperationException with the failing command + stderr).
- In
MongoDbConnectionBroker.EnsureContainerStartedAsync, before CreateAsync, call PullAsync(containerDefinition.ImageName, cancellationToken) so the newest :latest is fetched and the pull happens as its own observable step. Keep the create-only-on-start-failure structure (pull + create both run inside the catch (InvalidOperationException) block that follows a failed StartAsync).
Design decision: strict vs tolerant pull
Making pull best-effort avoids breaking offline users who already have a usable cached image. Recommended: attempt the pull, and on pull failure fall back to CreateAsync (which uses the cached image) rather than hard-failing. This keeps offline/registry-down scenarios working while still refreshing :latest whenever the registry is reachable. Mark the strict-vs-tolerant choice as an explicit design decision for review; a strict variant (fail loudly on pull error) is the alternative.
Considered / Background
- Do nothing and rely on
docker create's implicit pull (current behavior) -- rejected because :latest is never refreshed and there is no observable/diagnosable pull step.
Expected Tests
Test style mirrors WindowsDockerDesktopEngineTests (uses a fake IDockerCommandRunner, RecordingDockerCommandRunner, that captures the argument list per invocation) and MongoDbConnectionBrokerDockerDesktopTests (uses a FakeDockerEngine : ContainerEngine tracking call counts). Naming style: Subject_Scenario_ExpectedOutcome.
| Test Name |
Class |
What It Verifies |
WindowsDockerDesktopEngine_PullAsync_RunsDockerPullWithImageName |
WindowsDockerDesktopEngineTests |
PullAsync("test/sleep:latest") issues a single docker pull test/sleep:latest invocation (Commands[0] equals ["pull", "test/sleep:latest"]). |
WindowsDockerDesktopEngine_PullAsync_WhenDockerPullFails_ThrowsInvalidOperationException |
WindowsDockerDesktopEngineTests |
A non-zero ProcessResult from the runner causes PullAsync to throw InvalidOperationException including the failing command and stderr. |
MongoDbConnectionBroker_WhenContainerMissing_PullsImageBeforeCreate |
MongoDbConnectionBrokerDockerDesktopTests |
When StartAsync first throws InvalidOperationException, the broker calls PullAsync before CreateAsync (assert ordering via the fake engine, e.g. recorded call sequence Pull -> Create -> Start). |
MongoDbConnectionBroker_WhenPullFails_FallsBackToCreateUsingCachedImage |
MongoDbConnectionBrokerDockerDesktopTests |
Tolerant behavior: a failing PullAsync does not abort the flow; CreateAsync and StartAsync are still invoked (matches the recommended tolerant design decision). |
If the strict variant is chosen instead, replace the last test with MongoDbConnectionBroker_WhenPullFails_PropagatesError verifying the pull failure surfaces and CreateAsync is not called.
Summary
Mongo container creation never issues an explicit
docker pull.MongoDbConnectionBroker.EnsureContainerStartedAsyncrelies on the implicit pull performed bydocker createwhen an image is absent locally. Because the default image tag is the movingmongodb/mongodb-atlas-local:latest, an already-present-but-stale image is never refreshed, first-run pulls have no observable/diagnosable step, and pull failures surface only as a genericdocker createfailure.Root Cause
Phantom.Workspaces.Data.MongoDB\MongoDbConnectionBroker.cs(lines ~153-169)EnsureContainerStartedAsynctriesStartAsync, and onInvalidOperationExceptioncallsCreateAsyncthenStartAsyncagain. There is NO pull step:Phantom.Workspaces.Containers\WindowsDockerDesktopEngine.cs(lines ~42-99)CreateAsyncbuilds adocker create --name <name> --network <net> [-e ...] [--mount ...] [-p ...] <image>argument list and runs it via_commandRunner.RunAsync(...):StartAsync/StopAsync/DestroyAsyncrundocker start|stop|rm. Nopullcommand exists anywhere inPhantom.Workspaces.Containers.Phantom.Workspaces.Data.MongoDB\MongoDBContainerDefinitionGenerator.cs(line ~13)The default image is a moving
:latesttag:Why This Is a Bug (Impact)
docker create/docker startonly pull an image when it is ABSENT locally. Because the tag is a moving:latest, an already-present-but-stale image is never refreshed -> users silently run an outdated Atlas Local image. An explicitdocker pullis the standard way to fetch the newest:latest.docker create. Image pulls can take minutes; there is no dedicated, observable pull step, so first-run latency looks like a hang and pull progress/errors are buried. (A sibling bug about docker stdout/stderr not being logged was searched for among open issues but not found at filing time; cross-link if one is later created.)docker createfailure rather than a clear "could not pull image" diagnostic.Affected Files
Phantom.Workspaces.Data.MongoDB\MongoDbConnectionBroker.csEnsureContainerStartedAsyncmust issue a pull before createPhantom.Workspaces.Containers\WindowsDockerDesktopEngine.csdocker pullinvocationPhantom.Workspaces.Containers\ContainerEngine.cs/DockerDesktopEngine.cs(base abstraction)PullAsyncto the abstractionPhantom.Workspaces.Data.MongoDB\MongoDBContainerDefinitionGenerator.cs:latestimage name that must be pulledDesign / Fix
Recommended
Add an explicit image-pull step to the container engine and invoke it before create in the Mongo broker path.
PullAsync(string imageName, CancellationToken cancellationToken)to theContainerEngineabstraction (Phantom.Workspaces.Containers\ContainerEngine.cs, inherited byDockerDesktopEngine). Implement it inWindowsDockerDesktopEngineasdocker pull <image>via_commandRunner.RunAsync(["pull", imageName], cancellationToken), surfacing failure the same wayRunAndEnsureSuccessAsyncdoes (throwInvalidOperationExceptionwith the failing command + stderr).MongoDbConnectionBroker.EnsureContainerStartedAsync, beforeCreateAsync, callPullAsync(containerDefinition.ImageName, cancellationToken)so the newest:latestis fetched and the pull happens as its own observable step. Keep the create-only-on-start-failure structure (pull + create both run inside thecatch (InvalidOperationException)block that follows a failedStartAsync).Design decision: strict vs tolerant pull
Making pull best-effort avoids breaking offline users who already have a usable cached image. Recommended: attempt the pull, and on pull failure fall back to
CreateAsync(which uses the cached image) rather than hard-failing. This keeps offline/registry-down scenarios working while still refreshing:latestwhenever the registry is reachable. Mark the strict-vs-tolerant choice as an explicit design decision for review; a strict variant (fail loudly on pull error) is the alternative.Considered / Background
docker create's implicit pull (current behavior) -- rejected because:latestis never refreshed and there is no observable/diagnosable pull step.Expected Tests
Test style mirrors
WindowsDockerDesktopEngineTests(uses a fakeIDockerCommandRunner,RecordingDockerCommandRunner, that captures the argument list per invocation) andMongoDbConnectionBrokerDockerDesktopTests(uses aFakeDockerEngine : ContainerEnginetracking call counts). Naming style:Subject_Scenario_ExpectedOutcome.WindowsDockerDesktopEngine_PullAsync_RunsDockerPullWithImageNameWindowsDockerDesktopEngineTestsPullAsync("test/sleep:latest")issues a singledocker pull test/sleep:latestinvocation (Commands[0]equals["pull", "test/sleep:latest"]).WindowsDockerDesktopEngine_PullAsync_WhenDockerPullFails_ThrowsInvalidOperationExceptionWindowsDockerDesktopEngineTestsProcessResultfrom the runner causesPullAsyncto throwInvalidOperationExceptionincluding the failing command and stderr.MongoDbConnectionBroker_WhenContainerMissing_PullsImageBeforeCreateMongoDbConnectionBrokerDockerDesktopTestsStartAsyncfirst throwsInvalidOperationException, the broker callsPullAsyncbeforeCreateAsync(assert ordering via the fake engine, e.g. recorded call sequence Pull -> Create -> Start).MongoDbConnectionBroker_WhenPullFails_FallsBackToCreateUsingCachedImageMongoDbConnectionBrokerDockerDesktopTestsPullAsyncdoes not abort the flow;CreateAsyncandStartAsyncare still invoked (matches the recommended tolerant design decision).If the strict variant is chosen instead, replace the last test with
MongoDbConnectionBroker_WhenPullFails_PropagatesErrorverifying the pull failure surfaces andCreateAsyncis not called.