diff --git a/src/Akka.Bootstrap.Docker.Sample/Akka.Bootstrap.Docker.Sample.csproj b/src/Akka.Bootstrap.Docker.Sample/Akka.Bootstrap.Docker.Sample.csproj index b16e9c6..f9f950a 100644 --- a/src/Akka.Bootstrap.Docker.Sample/Akka.Bootstrap.Docker.Sample.csproj +++ b/src/Akka.Bootstrap.Docker.Sample/Akka.Bootstrap.Docker.Sample.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/src/Akka.Bootstrap.Docker.Sample/Program.cs b/src/Akka.Bootstrap.Docker.Sample/Program.cs index e5c7355..73fa763 100644 --- a/src/Akka.Bootstrap.Docker.Sample/Program.cs +++ b/src/Akka.Bootstrap.Docker.Sample/Program.cs @@ -5,6 +5,7 @@ using Akka.Configuration; using Akka.Event; using Akka.Routing; +using Hocon; using Petabridge.Cmd.Cluster; using Petabridge.Cmd.Host; using Petabridge.Cmd.Remote; @@ -20,7 +21,7 @@ static void Main(string[] args) Environment.SetEnvironmentVariable("CLUSTER_IP", "localhost"); } - var config = ConfigurationFactory.ParseString(File.ReadAllText("app.conf")) + var config = HoconConfigurationFactory.Default() .BootstrapFromDocker(); // forces all Docker environment variable substitution var actorSystem = ActorSystem.Create("DockerBootstrap", config); diff --git a/src/Akka.Bootstrap.Docker.Tests/DockerBootstrapSpecs.cs b/src/Akka.Bootstrap.Docker.Tests/DockerBootstrapSpecs.cs index f4d4757..5864eec 100644 --- a/src/Akka.Bootstrap.Docker.Tests/DockerBootstrapSpecs.cs +++ b/src/Akka.Bootstrap.Docker.Tests/DockerBootstrapSpecs.cs @@ -10,7 +10,6 @@ using Akka.Configuration; using FluentAssertions; using Xunit; -using Hocon; namespace Akka.Bootstrap.Docker.Tests { @@ -20,20 +19,18 @@ namespace Akka.Bootstrap.Docker.Tests public class DockerBootstrapSpecs { [Theory] - [InlineData("\"akka.tcp://MySys@localhost:9140\"")] - [InlineData("\"akka.tcp://MySys@localhost:9140\", \"akka.tcp://MySys@localhost:9141\"")] - [InlineData("\"akka.tcp://MySys@localhost:9140\", \"akka.tcp://MySys@localhost:9141\", \"akka.tcp://MySys@localhost:9142\"")] + [InlineData("akka.tcp://MySys@localhost:9140")] + [InlineData("akka.tcp://MySys@localhost:9140, akka.tcp://MySys@localhost:9141")] + [InlineData("akka.tcp://MySys@localhost:9140, akka.tcp://MySys@localhost:9141, akka.tcp://MySys@localhost:9142")] public void ShouldStartIfValidSeedNodesIfSupplied(string seedNodes) { try { Environment.SetEnvironmentVariable("CLUSTER_SEEDS", seedNodes, EnvironmentVariableTarget.Process); var myConfig = ConfigurationFactory.Empty.BootstrapFromDocker(); - Config expected = $"array=[{seedNodes}]"; - myConfig.HasPath("akka.cluster.seed-nodes").Should().BeTrue(); - var seeds = myConfig.GetStringList("akka.cluster.seed-nodes"); - seeds.Should().BeEquivalentTo(expected.GetStringList("array")); + var seeds = myConfig.GetStringList("akka.cluster.seed-nodes").Select(x => x.Trim()); + seeds.Should().BeEquivalentTo(seedNodes.Split(",").Select(x => x.Trim())); } finally { @@ -90,37 +87,23 @@ public void ShouldStartNormallyIfNotEnvironmentVariablesAreSupplied() [Fact] public void ShouldStartIfValidAkkaConfigurationSuppliedByEnvironmentVariables() { - try - { - Environment.SetEnvironmentVariable("AKKA__COORDINATED_SHUTDOWN__EXIT_CLR", "on", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__ACTOR__PROVIDER", "cluster", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__HOSTNAME", "127.0.0.1", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__PUBLIC_HOSTNAME", "example.local", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__PORT", "2559", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__0", "demo", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__1", "test", EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__2", "backup", EnvironmentVariableTarget.Process); - - var myConfig = ConfigurationFactory.Empty.BootstrapFromDocker(); + Environment.SetEnvironmentVariable("AKKA__COORDINATED_SHUTDOWN__EXIT_CLR", "on", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__ACTOR__PROVIDER", "cluster", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__HOSTNAME", "127.0.0.1", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__PUBLIC_HOSTNAME", "example.local", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__PORT", "2559", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__0", "demo", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__1", "test", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__2", "backup", EnvironmentVariableTarget.Process); - myConfig.GetBoolean("akka.coordinated-shutdown.exit-clr").Should().BeTrue(); - myConfig.GetString("akka.actor.provider").Should().Be("cluster"); - myConfig.GetString("akka.remote.dot-netty.tcp.hostname").Should().Be("127.0.0.1"); - myConfig.GetString("akka.remote.dot-netty.tcp.public-hostname").Should().Be("example.local"); - myConfig.GetInt("akka.remote.dot-netty.tcp.port").Should().Be(2559); - myConfig.GetStringList("akka.cluster.roles").Should().BeEquivalentTo(new[] { "demo", "test", "backup" }); - } - finally - { - Environment.SetEnvironmentVariable("AKKA__COORDINATED_SHUTDOWN__EXIT_CLR", null); - Environment.SetEnvironmentVariable("AKKA__ACTOR__PROVIDER", null); - Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__HOSTNAME", null); - Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__PUBLIC_HOSTNAME", null); - Environment.SetEnvironmentVariable("AKKA__REMOTE__DOT_NETTY__TCP__PORT", null); - Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__0", null); - Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__1", null); - Environment.SetEnvironmentVariable("AKKA__CLUSTER__ROLES__2", null); - } + var myConfig = ConfigurationFactory.Empty.BootstrapFromDocker(); + + myConfig.GetBoolean("akka.coordinated-shutdown.exit-clr").Should().BeTrue(); + myConfig.GetString("akka.actor.provider").Should().Be("cluster"); + myConfig.GetString("akka.remote.dot-netty.tcp.hostname").Should().Be("127.0.0.1"); + myConfig.GetString("akka.remote.dot-netty.tcp.public-hostname").Should().Be("example.local"); + myConfig.GetInt("akka.remote.dot-netty.tcp.port").Should().Be(2559); + myConfig.GetStringList("akka.cluster.roles").Should().BeEquivalentTo(new [] { "demo", "test", "backup" }); } } } \ No newline at end of file diff --git a/src/Akka.Bootstrap.Docker/Akka.Bootstrap.Docker.csproj b/src/Akka.Bootstrap.Docker/Akka.Bootstrap.Docker.csproj index 97bed9b..4f668d3 100644 --- a/src/Akka.Bootstrap.Docker/Akka.Bootstrap.Docker.csproj +++ b/src/Akka.Bootstrap.Docker/Akka.Bootstrap.Docker.csproj @@ -14,14 +14,6 @@ bin\Release\netstandard1.6\Akka.Bootstrap.Docker.xml - - - - - - - - diff --git a/src/Akka.Bootstrap.Docker/AssemblyMarker.cs b/src/Akka.Bootstrap.Docker/AssemblyMarker.cs deleted file mode 100644 index 8521e96..0000000 --- a/src/Akka.Bootstrap.Docker/AssemblyMarker.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Akka.Bootstrap.Docker -{ - internal class AssemblyMarker - { - } -} diff --git a/src/Akka.Bootstrap.Docker/Docker.Environment.conf b/src/Akka.Bootstrap.Docker/Docker.Environment.conf deleted file mode 100644 index 1f012fb..0000000 --- a/src/Akka.Bootstrap.Docker/Docker.Environment.conf +++ /dev/null @@ -1,7 +0,0 @@ -############################################### -# Docker Environment Variable Overrides # -############################################### - -akka.remote.dot-netty.tcp.public-hostname = ${?CLUSTER_IP} -akka.remote.dot-netty.tcp.port = ${?CLUSTER_PORT} -environment.seed-nodes = ${?CLUSTER_SEEDS} \ No newline at end of file diff --git a/src/Akka.Bootstrap.Docker/EnvironmentVariableConfigLoader.cs b/src/Akka.Bootstrap.Docker/EnvironmentVariableConfigLoader.cs index 38c8919..ce4e4fb 100644 --- a/src/Akka.Bootstrap.Docker/EnvironmentVariableConfigLoader.cs +++ b/src/Akka.Bootstrap.Docker/EnvironmentVariableConfigLoader.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Text; using Akka.Configuration; -using System.Net; using Hocon; namespace Akka.Bootstrap.Docker @@ -21,8 +20,6 @@ namespace Akka.Bootstrap.Docker /// public static class EnvironmentVariableConfigLoader { - private const string DefaultConfigResource = "Akka.Bootstrap.Docker.Docker.Environment.conf"; - private static IEnumerable GetEnvironmentVariables() { // Currently, exclude environment variables that do not start with "AKKA__" @@ -31,11 +28,34 @@ private static IEnumerable GetEnvironmentV // to other non "AKKA__" variables. bool UseAllEnvironmentVariables = false; + // List of environment variable mappings that do not follow the "AKKA__" convention. + // We are currently supporting these out of convenience, and may choose to officially + // create a set of aliases in the future. Doing so would allow envvar configuration + // to be less verbose but might perpetuate confusion as to source of truth for keys. + Dictionary ExistingMappings = new Dictionary() + { + { "CLUSTER_IP", "akka.remote.dot-netty.tcp.public-hostname" }, + { "CLUSTER_PORT", "akka.remote.dot-netty.tcp.port" }, + { "CLUSTER_SEEDS", "akka.cluster.seed-nodes" } + }; + + // Identify environment variable mappings that are expected to be lists + string[] ExistingMappingLists = new string[] { "CLUSTER_SEEDS" }; + foreach (DictionaryEntry set in Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process)) { var key = set.Key.ToString(); var isList = false; + if (ExistingMappings.TryGetValue(key, out var mappedKey)) + { + isList = ExistingMappingLists.Contains(key); + + // Format the key to appear as if it were an environment variable + // in the "AKKA__" format + key = mappedKey.ToUpper().Replace(".", "__").Replace("-", "_"); + } + if (!UseAllEnvironmentVariables) if (!key.StartsWith("AKKA__", StringComparison.OrdinalIgnoreCase)) continue; @@ -74,36 +94,31 @@ private static IEnumerable GetEnvironmentV /// /// /// - public static Config FromEnvironment(this Config _) + public static Config FromEnvironment(this Config input) { - var environmentConfig = HoconConfigurationFactory.FromResource(DefaultConfigResource); - var defaultValues = new StringBuilder(); - defaultValues.AppendLine($@" - akka.remote.dot-netty.tcp {{ - hostname=0.0.0.0 - public-hostname={Dns.GetHostName()} - }}"); - if (environmentConfig.HasPath("environment.seed-nodes")) - defaultValues.AppendLine($"akka.cluster.seed-nodes=[{environmentConfig.GetString("environment.seed-nodes")}]"); - var entries = GetEnvironmentVariables() .OrderByDescending(x => x.Depth) .GroupBy(x => x.Key); + StringBuilder sb = new StringBuilder(); foreach (var set in entries) { - defaultValues.Append($"{set.Key}="); + sb.Append($"{set.Key}="); if (set.Count() > 1) - { - defaultValues.AppendLine($"[\n\t\"{String.Join("\",\n\t\"", set.OrderBy(y => y.Index).Select(y => y.Value.Trim()))}\"]"); + { + sb.AppendLine($"[\n\t\"{String.Join("\",\n\t\"", set.OrderBy(y => y.Index).Select(y => y.Value.Trim()))}\"]"); } else { - defaultValues.AppendLine($"{set.First().Value}"); + sb.AppendLine($"{set.First().Value}"); } } - return environmentConfig.WithFallback(HoconConfigurationFactory.ParseString(defaultValues.ToString())); + if(sb.Length == 0) + return Config.Empty; + var config = ConfigurationFactory.ParseString(sb.ToString()); + + return config; } } diff --git a/src/Akka.Bootstrap.ServiceFabric.Tests/ServiceFabricBootstrapSpecs.cs b/src/Akka.Bootstrap.ServiceFabric.Tests/ServiceFabricBootstrapSpecs.cs index 118a6f7..4400648 100644 --- a/src/Akka.Bootstrap.ServiceFabric.Tests/ServiceFabricBootstrapSpecs.cs +++ b/src/Akka.Bootstrap.ServiceFabric.Tests/ServiceFabricBootstrapSpecs.cs @@ -7,6 +7,7 @@ using System; using System.Linq; using Akka.Configuration; +using Hocon; using FluentAssertions; using Xunit; diff --git a/src/common.props b/src/common.props index 47f5c39..5ad10e6 100644 --- a/src/common.props +++ b/src/common.props @@ -1,6 +1,6 @@ - Copyright © 2015-2019 Petabridge® + Copyright © 2015-2020 Petabridge® Petabridge 0.4.0 Upgraded to Akka.NET v1.4.1-rc1 and used native environment variable substitution from HOCON