Skip to content

[containers] Mongo container creation never issues docker pull; relies on implicit pull by docker create #1374

Description

@JoshuaRowePhantom

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)

  1. 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.
  2. 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.)
  3. 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.

Metadata

Metadata

Labels

bugSomething isn't workingdiagnosedRoot cause identifiedneeds-slow-testsRequires full test suite including slow Git tests at checkinverified-locallyImplementation has been verified locally

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions