diff --git a/backend/src/Squidex/Config/Orleans/OrleansServices.cs b/backend/src/Squidex/Config/Orleans/OrleansServices.cs index ed575b2d9..246ad853c 100644 --- a/backend/src/Squidex/Config/Orleans/OrleansServices.cs +++ b/backend/src/Squidex/Config/Orleans/OrleansServices.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Globalization; using System.Net; using System.Net.Sockets; using Microsoft.Extensions.Configuration; @@ -16,6 +17,7 @@ using Orleans.Providers.MongoDB.Configuration; using Orleans.Providers.MongoDB.Utils; using OrleansDashboard; using Squidex.Domain.Apps.Entities; +using Squidex.Hosting.Configuration; using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; using Squidex.Web; @@ -70,30 +72,18 @@ namespace Squidex.Config.Orleans builder.AddIncomingGrainCallFilter(); builder.AddIncomingGrainCallFilter(); - var orleansPortSilo = config.GetOptionalValue("orleans:siloPort", 11111); - var orleansPortGateway = config.GetOptionalValue("orleans:gatewayPort", 40000); + var (siloPort, gatewayPort) = GetPorts(config); config.ConfigureByOption("orleans:clustering", new Alternatives { ["MongoDB"] = () => { - IPAddress address; - - var configuredAddress = config.GetOptionalValue("orleans:ipAddress", string.Empty); - - if (!string.IsNullOrWhiteSpace(configuredAddress)) - { - address = IPAddress.Parse(configuredAddress); - } - else - { - address = Helper.ResolveIPAddressAsync(Dns.GetHostName(), AddressFamily.InterNetwork).Result; - } + var address = GetIPAddress(config); builder.ConfigureEndpoints( address, - orleansPortSilo, - orleansPortGateway, + siloPort, + gatewayPort, true); builder.UseMongoDBClustering(options => @@ -110,7 +100,7 @@ namespace Squidex.Config.Orleans }, ["Development"] = () => { - builder.UseLocalhostClustering(orleansPortSilo, orleansPortGateway); + builder.UseLocalhostClustering(siloPort, gatewayPort); } }); @@ -126,6 +116,54 @@ namespace Squidex.Config.Orleans }); } + private static (int, int) GetPorts(IConfiguration config) + { + var orleansPortSilo = config.GetOptionalValue("orleans:siloPort", 11111); + var orleansPortGateway = config.GetOptionalValue("orleans:gatewayPort", 40000); + + var privatePorts = config.GetOptionalValue("WEBSITE_PRIVATE_PORTS", string.Empty); + + if (!string.IsNullOrWhiteSpace(privatePorts) && config.GetValue("orleans:useAzureNetwork")) + { + var ports = privatePorts.Split(','); + + if (ports.Length < 1 || !int.TryParse(ports[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out orleansPortSilo)) + { + var error = new ConfigurationError("Insufficient private ports configured.", "WEBSITE_PRIVATE_PORTS"); + + throw new ConfigurationException(error); + } + } + + return (orleansPortSilo, orleansPortGateway); + } + + private static IPAddress GetIPAddress(IConfiguration config) + { + IPAddress? address = null; + + var configuredAddress = config.GetOptionalValue("orleans:ipAddress", string.Empty); + + if (!string.IsNullOrWhiteSpace(configuredAddress)) + { + address = IPAddress.Parse(configuredAddress); + } + + if (address == null && config.GetValue("orleans:useAzureNetwork")) + { + var privateIP = config.GetOptionalValue("WEBSITE_PRIVATE_IP", string.Empty); + + if (!string.IsNullOrWhiteSpace(privateIP)) + { + address = IPAddress.Parse(privateIP); + } + } + + address ??= Helper.ResolveIPAddressAsync(Dns.GetHostName(), AddressFamily.InterNetwork).Result; + + return address; + } + private static void Configure(this MongoDBOptions options, IConfiguration config) { options.CollectionPrefix = "Orleans_"; diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index 2c8dab3b0..afeff303f 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -386,7 +386,10 @@ "gatewayPort": "40000", // The advertised IP address. Usually not needed. - "ipAddress": "" + "ipAddress": "", + + // Bind Orleans to a private network when running as a app service instance. + "useAzureNetwork": true }, "eventStore": {