Skip to content

Commit 692dc41

Browse files
authored
Isolate the use of host.docker.internal (#2685)
* Isolate the use of docker.host.internal - For podman support we need to use another host name (host.containers.internal). That's configurable using a setting AppHost:ContainerHostName. Remove the hardcoded use of docker.host.internal and instead read from configuration everywhere. * Fixed nit
1 parent 499440e commit 692dc41

File tree

8 files changed

+132
-46
lines changed

8 files changed

+132
-46
lines changed

src/Aspire.Hosting/MongoDB/MongoDBBuilderExtensions.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.Net.Sockets;
55
using Aspire.Hosting.ApplicationModel;
66
using Aspire.Hosting.MongoDB;
7+
using Aspire.Hosting.Utils;
8+
using Microsoft.Extensions.Configuration;
79

810
namespace Aspire.Hosting;
911

@@ -66,18 +68,20 @@ public static IResourceBuilder<T> WithMongoExpress<T>(this IResourceBuilder<T> b
6668
var mongoExpressContainer = new MongoExpressContainerResource(containerName);
6769
builder.ApplicationBuilder.AddResource(mongoExpressContainer)
6870
.WithAnnotation(new ContainerImageAnnotation { Image = "mongo-express", Tag = "1.0.2-20" })
69-
.WithEnvironment(context => ConfigureMongoExpressContainer(context, builder.Resource))
71+
.WithEnvironment(context => ConfigureMongoExpressContainer(builder.ApplicationBuilder.Configuration, context, builder.Resource))
7072
.WithHttpEndpoint(containerPort: 8081, hostPort: hostPort, name: containerName)
7173
.ExcludeFromManifest();
7274

7375
return builder;
7476
}
7577

76-
private static void ConfigureMongoExpressContainer(EnvironmentCallbackContext context, IResource resource)
78+
private static void ConfigureMongoExpressContainer(IConfiguration configuration, EnvironmentCallbackContext context, IResource resource)
7779
{
80+
var containerHostName = HostNameResolver.ReplaceLocalhostWithContainerHost("localhost", configuration);
81+
7882
var hostPort = GetResourcePort(resource);
7983

80-
context.EnvironmentVariables.Add("ME_CONFIG_MONGODB_URL", $"mongodb://host.docker.internal:{hostPort}/?directConnection=true");
84+
context.EnvironmentVariables.Add("ME_CONFIG_MONGODB_URL", $"mongodb://{containerHostName}:{hostPort}/?directConnection=true");
8185
context.EnvironmentVariables.Add("ME_CONFIG_BASICAUTH", "false");
8286

8387
static int GetResourcePort(IResource resource)

src/Aspire.Hosting/MySql/PhpMyAdminConfigWriterHook.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
using Aspire.Hosting.ApplicationModel;
55
using Aspire.Hosting.Lifecycle;
6+
using Aspire.Hosting.Utils;
7+
using Microsoft.Extensions.Configuration;
68

79
namespace Aspire.Hosting.MySql;
810

9-
internal class PhpMyAdminConfigWriterHook : IDistributedApplicationLifecycleHook
11+
internal class PhpMyAdminConfigWriterHook(IConfiguration configuration) : IDistributedApplicationLifecycleHook
1012
{
1113
public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken)
1214
{
@@ -26,6 +28,8 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
2628
return Task.CompletedTask;
2729
}
2830

31+
var containerHostName = HostNameResolver.ReplaceLocalhostWithContainerHost("localhost", configuration);
32+
2933
if (mySqlInstances.Count() == 1)
3034
{
3135
var singleInstance = mySqlInstances.Single();
@@ -34,7 +38,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
3438
var endpoint = allocatedEndPoints.Where(ae => ae.Name == "tcp").Single();
3539
myAdminResource.Annotations.Add(new EnvironmentCallbackAnnotation((EnvironmentCallbackContext context) =>
3640
{
37-
context.EnvironmentVariables.Add("PMA_HOST", $"host.docker.internal:{endpoint.Port}");
41+
context.EnvironmentVariables.Add("PMA_HOST", $"{containerHostName}:{endpoint.Port}");
3842
context.EnvironmentVariables.Add("PMA_USER", "root");
3943
context.EnvironmentVariables.Add("PMA_PASSWORD", singleInstance.Password);
4044
}));
@@ -55,7 +59,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
5559
{
5660
var endpoint = allocatedEndpoints.Where(ae => ae.Name == "tcp").Single();
5761
writer.WriteLine("$i++;");
58-
writer.WriteLine($"$cfg['Servers'][$i]['host'] = 'host.docker.internal:{endpoint.Port}';");
62+
writer.WriteLine($"$cfg['Servers'][$i]['host'] = '{containerHostName}:{endpoint.Port}';");
5963
writer.WriteLine($"$cfg['Servers'][$i]['verbose'] = '{mySqlInstance.Name}';");
6064
writer.WriteLine($"$cfg['Servers'][$i]['auth_type'] = 'cookie';");
6165
writer.WriteLine($"$cfg['Servers'][$i]['user'] = 'root';");

src/Aspire.Hosting/Postgres/PgAdminConfigWriterHook.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
using System.Text.Json;
66
using Aspire.Hosting.ApplicationModel;
77
using Aspire.Hosting.Lifecycle;
8+
using Aspire.Hosting.Utils;
9+
using Microsoft.Extensions.Configuration;
810

911
namespace Aspire.Hosting.Postgres;
1012

11-
internal class PgAdminConfigWriterHook : IDistributedApplicationLifecycleHook
13+
internal class PgAdminConfigWriterHook(IConfiguration configuration) : IDistributedApplicationLifecycleHook
1214
{
1315
public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken)
1416
{
@@ -21,11 +23,13 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
2123
using var stream = new FileStream(serverFileMount.Source!, FileMode.Create);
2224
using var writer = new Utf8JsonWriter(stream);
2325

24-
var serverIndex = 1;
26+
var containerHostName = HostNameResolver.ReplaceLocalhostWithContainerHost("localhost", configuration);
2527

2628
writer.WriteStartObject();
2729
writer.WriteStartObject("Servers");
2830

31+
var serverIndex = 1;
32+
2933
foreach (var postgresInstance in postgresInstances)
3034
{
3135
if (postgresInstance.TryGetAllocatedEndPoints(out var allocatedEndpoints))
@@ -35,7 +39,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
3539
writer.WriteStartObject($"{serverIndex}");
3640
writer.WriteString("Name", postgresInstance.Name);
3741
writer.WriteString("Group", "Aspire instances");
38-
writer.WriteString("Host", "host.docker.internal");
42+
writer.WriteString("Host", containerHostName);
3943
writer.WriteNumber("Port", endpoint.Port);
4044
writer.WriteString("Username", "postgres");
4145
writer.WriteString("SSLMode", "prefer");

src/Aspire.Hosting/Redis/RedisCommanderConfigWriterHook.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
using Aspire.Hosting.ApplicationModel;
55
using System.Text;
66
using Aspire.Hosting.Lifecycle;
7+
using Microsoft.Extensions.Configuration;
8+
using Aspire.Hosting.Utils;
79

810
namespace Aspire.Hosting.Redis;
911

10-
internal class RedisCommanderConfigWriterHook : IDistributedApplicationLifecycleHook
12+
internal class RedisCommanderConfigWriterHook(IConfiguration configuration) : IDistributedApplicationLifecycleHook
1113
{
1214
public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken)
1315
{
@@ -25,6 +27,8 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
2527
return Task.CompletedTask;
2628
}
2729

30+
var containerHostName = HostNameResolver.ReplaceLocalhostWithContainerHost("localhost", configuration);
31+
2832
var hostsVariableBuilder = new StringBuilder();
2933

3034
foreach (var redisInstance in redisInstances)
@@ -33,7 +37,7 @@ public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, C
3337
{
3438
var endpoint = allocatedEndpoints.Where(ae => ae.Name == "tcp").Single();
3539

36-
var hostString = $"{(hostsVariableBuilder.Length > 0 ? "," : string.Empty)}{redisInstance.Name}:host.docker.internal:{endpoint.Port}:0";
40+
var hostString = $"{(hostsVariableBuilder.Length > 0 ? "," : string.Empty)}{redisInstance.Name}:{containerHostName}:{endpoint.Port}:0";
3741
hostsVariableBuilder.Append(hostString);
3842
}
3943
}

tests/Aspire.Hosting.Tests/MongoDB/AddMongoDBTests.cs

+35-5
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,43 @@ public void MongoDBCreatesConnectionString()
102102
Assert.Equal("{mongodb.connectionString}/mydatabase", connectionStringResource.ConnectionStringExpression);
103103
}
104104

105-
[Fact]
106-
public void WithMongoExpressAddsContainer()
105+
[Theory]
106+
[InlineData(null, "host.docker.internal")]
107+
[InlineData("host.containers.internal", "host.containers.internal")]
108+
public void WithMongoExpressAddsContainer(string? containerHost, string expectedHost)
107109
{
108110
var builder = DistributedApplication.CreateBuilder();
109-
builder.AddMongoDB("mongo").WithMongoExpress();
110111

111-
Assert.Single(builder.Resources.OfType<MongoExpressContainerResource>());
112+
if (containerHost is not null)
113+
{
114+
builder.Configuration["AppHost:ContainerHostname"] = containerHost;
115+
}
116+
117+
builder.AddMongoDB("mongo")
118+
.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "locahost", 3452, "tcp"))
119+
.WithMongoExpress();
120+
121+
var mongoExpress = Assert.Single(builder.Resources.OfType<MongoExpressContainerResource>());
122+
123+
Assert.Equal("mongo-mongoexpress", mongoExpress.Name);
124+
Assert.True(mongoExpress.TryGetEnvironmentVariables(out var environmentCallbackAnnotations));
125+
var context = new EnvironmentCallbackContext(new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run)); ;
126+
foreach (var annotation in environmentCallbackAnnotations)
127+
{
128+
annotation.Callback(context);
129+
}
130+
131+
Assert.Collection(context.EnvironmentVariables,
132+
kvp =>
133+
{
134+
Assert.Equal("ME_CONFIG_MONGODB_URL", kvp.Key);
135+
Assert.Equal($"mongodb://{expectedHost}:3452/?directConnection=true", kvp.Value);
136+
},
137+
kvp =>
138+
{
139+
Assert.Equal("ME_CONFIG_BASICAUTH", kvp.Key);
140+
Assert.Equal("false", kvp.Value);
141+
});
112142
}
113143

114144
[Fact]
@@ -130,7 +160,7 @@ public void VerifyManifest()
130160

131161
var mongoManifest = ManifestUtils.GetManifest(mongo.Resource);
132162
var dbManifest = ManifestUtils.GetManifest(db.Resource);
133-
163+
134164
Assert.Equal("container.v0", mongoManifest["type"]?.ToString());
135165
Assert.Equal(mongo.Resource.ConnectionStringExpression, mongoManifest["connectionString"]?.ToString());
136166

tests/Aspire.Hosting.Tests/MySql/AddMySqlTests.cs

+28-12
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,26 @@ public void WithMySqlTwiceEndsUpWithOneAdminContainer()
188188
Assert.Single(builder.Resources.OfType<PhpMyAdminContainerResource>());
189189
}
190190

191-
[Fact]
192-
public async Task SingleMySqlInstanceProducesCorrectMySqlHostsVariable()
191+
[Theory]
192+
[InlineData(null, "host.docker.internal")]
193+
[InlineData("host.containers.internal", "host.containers.internal")]
194+
public async Task SingleMySqlInstanceProducesCorrectMySqlHostsVariable(string? containerHost, string expectedHost)
193195
{
194196
var builder = DistributedApplication.CreateBuilder();
197+
198+
if (containerHost is not null)
199+
{
200+
builder.Configuration["AppHost:ContainerHostname"] = containerHost;
201+
}
202+
195203
var mysql = builder.AddMySql("mySql").WithPhpMyAdmin();
196204
using var app = builder.Build();
197205

198206
// Add fake allocated endpoints.
199-
mysql.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "host.docker.internal", 5001, "tcp"));
207+
mysql.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "localhost", 5001, "tcp"));
200208

201209
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
202-
var hook = new PhpMyAdminConfigWriterHook();
210+
var hook = new PhpMyAdminConfigWriterHook(builder.Configuration);
203211
await hook.AfterEndpointsAllocatedAsync(model, CancellationToken.None);
204212

205213
var myAdmin = builder.Resources.Single(r => r.Name.EndsWith("-phpmyadmin"));
@@ -215,7 +223,7 @@ public async Task SingleMySqlInstanceProducesCorrectMySqlHostsVariable()
215223
annotation.Callback(context);
216224
}
217225

218-
Assert.Equal("host.docker.internal:5001", context.EnvironmentVariables["PMA_HOST"]);
226+
Assert.Equal($"{expectedHost}:5001", context.EnvironmentVariables["PMA_HOST"]);
219227
Assert.NotNull(context.EnvironmentVariables["PMA_USER"]);
220228
Assert.NotNull(context.EnvironmentVariables["PMA_PASSWORD"]);
221229
}
@@ -233,32 +241,40 @@ public void WithPhpMyAdminAddsContainer()
233241
Assert.Equal("/etc/phpmyadmin/config.user.inc.php", volume.Target);
234242
}
235243

236-
[Fact]
237-
public void WithPhpMyAdminProducesValidServerConfigFile()
244+
[Theory]
245+
[InlineData(null, "host.docker.internal")]
246+
[InlineData("host.containers.internal", "host.containers.internal")]
247+
public void WithPhpMyAdminProducesValidServerConfigFile(string? containerHost, string expectedHost)
238248
{
239249
var builder = DistributedApplication.CreateBuilder();
250+
251+
if (containerHost is not null)
252+
{
253+
builder.Configuration["AppHost:ContainerHostname"] = containerHost;
254+
}
255+
240256
var mysql1 = builder.AddMySql("mysql1").WithPhpMyAdmin(8081);
241257
var mysql2 = builder.AddMySql("mysql2").WithPhpMyAdmin(8081);
242258

243259
// Add fake allocated endpoints.
244-
mysql1.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "host.docker.internal", 5001, "tcp"));
245-
mysql2.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "host.docker.internal", 5002, "tcp"));
260+
mysql1.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "localhost", 5001, "tcp"));
261+
mysql2.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "localhost", 5002, "tcp"));
246262

247263
var myAdmin = builder.Resources.Single(r => r.Name.EndsWith("-phpmyadmin"));
248264
var volume = myAdmin.Annotations.OfType<ContainerMountAnnotation>().Single();
249265

250266
using var app = builder.Build();
251267
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
252268

253-
var hook = new PhpMyAdminConfigWriterHook();
269+
var hook = new PhpMyAdminConfigWriterHook(builder.Configuration);
254270
hook.AfterEndpointsAllocatedAsync(appModel, CancellationToken.None);
255271

256272
using var stream = File.OpenRead(volume.Source!);
257273
var fileContents = new StreamReader(stream).ReadToEnd();
258274

259275
// check to see that the two hosts are in the file
260-
string pattern1 = @"\$cfg\['Servers'\]\[\$i\]\['host'\] = 'host.docker.internal:5001';";
261-
string pattern2 = @"\$cfg\['Servers'\]\[\$i\]\['host'\] = 'host.docker.internal:5002';";
276+
string pattern1 = $@"\$cfg\['Servers'\]\[\$i\]\['host'\] = '{expectedHost}:5001';";
277+
string pattern2 = $@"\$cfg\['Servers'\]\[\$i\]\['host'\] = '{expectedHost}:5002';";
262278
Match match1 = Regex.Match(fileContents, pattern1);
263279
Assert.True(match1.Success);
264280
Match match2 = Regex.Match(fileContents, pattern2);

tests/Aspire.Hosting.Tests/Postgres/AddPostgresTests.cs

+15-7
Original file line numberDiff line numberDiff line change
@@ -275,24 +275,32 @@ public void WithPostgresTwiceEndsUpWithOneContainer()
275275
builder.Resources.Single(r => r.Name.EndsWith("-pgadmin"));
276276
}
277277

278-
[Fact]
279-
public void WithPostgresProducesValidServersJsonFile()
278+
[Theory]
279+
[InlineData(null, "host.docker.internal")]
280+
[InlineData("host.containers.internal", "host.containers.internal")]
281+
public void WithPostgresProducesValidServersJsonFile(string? containerHost, string expectedHost)
280282
{
281283
var builder = DistributedApplication.CreateBuilder();
284+
285+
if (containerHost is not null)
286+
{
287+
builder.Configuration["AppHost:ContainerHostname"] = containerHost;
288+
}
289+
282290
var pg1 = builder.AddPostgres("mypostgres1").WithPgAdmin(8081);
283291
var pg2 = builder.AddPostgres("mypostgres2").WithPgAdmin(8081);
284292

285293
// Add fake allocated endpoints.
286-
pg1.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "host.docker.internal", 5001, "tcp"));
287-
pg2.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "host.docker.internal", 5002, "tcp"));
294+
pg1.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "localhost", 5001, "tcp"));
295+
pg2.WithAnnotation(new AllocatedEndpointAnnotation("tcp", ProtocolType.Tcp, "localhost", 5002, "tcp"));
288296

289297
var pgadmin = builder.Resources.Single(r => r.Name.EndsWith("-pgadmin"));
290298
var volume = pgadmin.Annotations.OfType<ContainerMountAnnotation>().Single();
291299

292300
using var app = builder.Build();
293301
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
294302

295-
var hook = new PgAdminConfigWriterHook();
303+
var hook = new PgAdminConfigWriterHook(builder.Configuration);
296304
hook.AfterEndpointsAllocatedAsync(appModel, CancellationToken.None);
297305

298306
using var stream = File.OpenRead(volume.Source!);
@@ -303,7 +311,7 @@ public void WithPostgresProducesValidServersJsonFile()
303311
// Make sure the first server is correct.
304312
Assert.Equal(pg1.Resource.Name, servers.GetProperty("1").GetProperty("Name").GetString());
305313
Assert.Equal("Aspire instances", servers.GetProperty("1").GetProperty("Group").GetString());
306-
Assert.Equal("host.docker.internal", servers.GetProperty("1").GetProperty("Host").GetString());
314+
Assert.Equal(expectedHost, servers.GetProperty("1").GetProperty("Host").GetString());
307315
Assert.Equal(5001, servers.GetProperty("1").GetProperty("Port").GetInt32());
308316
Assert.Equal("postgres", servers.GetProperty("1").GetProperty("Username").GetString());
309317
Assert.Equal("prefer", servers.GetProperty("1").GetProperty("SSLMode").GetString());
@@ -313,7 +321,7 @@ public void WithPostgresProducesValidServersJsonFile()
313321
// Make sure the second server is correct.
314322
Assert.Equal(pg2.Resource.Name, servers.GetProperty("2").GetProperty("Name").GetString());
315323
Assert.Equal("Aspire instances", servers.GetProperty("2").GetProperty("Group").GetString());
316-
Assert.Equal("host.docker.internal", servers.GetProperty("2").GetProperty("Host").GetString());
324+
Assert.Equal(expectedHost, servers.GetProperty("2").GetProperty("Host").GetString());
317325
Assert.Equal(5002, servers.GetProperty("2").GetProperty("Port").GetInt32());
318326
Assert.Equal("postgres", servers.GetProperty("2").GetProperty("Username").GetString());
319327
Assert.Equal("prefer", servers.GetProperty("2").GetProperty("SSLMode").GetString());

0 commit comments

Comments
 (0)