From f3fbfd73b0c87af18151dd7facf58a1e05ded422 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 14 Jul 2025 15:23:48 -0700 Subject: [PATCH] Throw if container runtime is unhealthy during image build --- .../ResourceContainerImageBuilder.cs | 2 +- .../ResourceContainerImageBuilderTests.cs | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Hosting/Publishing/ResourceContainerImageBuilder.cs b/src/Aspire.Hosting/Publishing/ResourceContainerImageBuilder.cs index e846f24bf77..d903ae8dd32 100644 --- a/src/Aspire.Hosting/Publishing/ResourceContainerImageBuilder.cs +++ b/src/Aspire.Hosting/Publishing/ResourceContainerImageBuilder.cs @@ -154,7 +154,7 @@ await task.FailAsync( cancellationToken).ConfigureAwait(false); await step.CompleteAsync("Building container images failed", CompletionState.CompletedWithError, cancellationToken).ConfigureAwait(false); - return; + throw new InvalidOperationException("Container runtime is not running or is unhealthy."); } await task.SucceedAsync( diff --git a/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageBuilderTests.cs b/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageBuilderTests.cs index 9d0729f8d68..bbe6a9091ec 100644 --- a/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageBuilderTests.cs +++ b/tests/Aspire.Hosting.Tests/Publishing/ResourceContainerImageBuilderTests.cs @@ -382,4 +382,51 @@ public void ContainerBuildOptions_CanSetAllProperties() Assert.Equal("/custom/path", options.OutputPath); Assert.Equal(ContainerTargetPlatform.LinuxArm64, options.TargetPlatform); } + + [Fact] + public async Task BuildImageAsync_ThrowsInvalidOperationException_WhenDockerRuntimeNotAvailable() + { + using var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(output); + + builder.Services.AddLogging(logging => + { + logging.AddFakeLogging(); + logging.AddXunit(output); + }); + + builder.Services.AddKeyedSingleton("docker", new UnhealthyMockContainerRuntime()); + + var (tempContextPath, tempDockerfilePath) = await DockerfileUtils.CreateTemporaryDockerfileAsync(); + var container = builder.AddDockerfile("container", tempContextPath, tempDockerfilePath); + + using var app = builder.Build(); + + using var cts = new CancellationTokenSource(TestConstants.LongTimeoutTimeSpan); + var imageBuilder = app.Services.GetRequiredService(); + + var exception = await Assert.ThrowsAsync(() => + imageBuilder.BuildImagesAsync([container.Resource], options: null, cts.Token)); + + Assert.Equal("Container runtime is not running or is unhealthy.", exception.Message); + + var collector = app.Services.GetFakeLogCollector(); + var logs = collector.GetSnapshot(); + + Assert.Contains(logs, log => log.Message.Contains("Container runtime is not running or is unhealthy. Cannot build container images.")); + } + + private sealed class UnhealthyMockContainerRuntime : IContainerRuntime + { + public string Name => "MockDocker"; + + public Task CheckIfRunningAsync(CancellationToken cancellationToken) + { + return Task.FromResult(false); + } + + public Task BuildImageAsync(string contextPath, string dockerfilePath, string imageName, ContainerBuildOptions? options, CancellationToken cancellationToken) + { + throw new InvalidOperationException("This mock runtime should not be used for building images."); + } + } }