diff --git a/README.md b/README.md index a8fe9d1..5ae3fc2 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,14 @@ In order to target as many possible .NET platforms as possible with minimal exte Dapr Sidekick also includes a package reference to [Newtonsoft Json.NET](https://github.com/JamesNK/Newtonsoft.Json) for parsing JSON log messages from Dapr. +## Non-Windows Platforms + +On platforms other than Windows (such as Linux and Mac OS) some features of Dapr Sidekick may not be available due to the required native API calls not being available. These include: + +| Feature | Platforms | Notes | +| --------------------------- | ------------ | ----------------------------------------------------------------- | +| Attach to existing instance | Linux/Mac OS | Will not detect existing `daprd` instance for same AppId and Port | + ## Acknowledgements Dapr Sidekick has been under active development at [Man Group](http://www.man.com/) since 2020. diff --git a/all.sln b/all.sln index c76a28c..686436a 100644 --- a/all.sln +++ b/all.sln @@ -39,6 +39,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A321995D-B530-4CAD-B467-16A9A7F36C67}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConsulSample", "ConsulSample", "{2DD56FCA-D5C3-4340-B5C5-27E284887A5F}" diff --git a/src/Man.Dapr.Sidekick/Native/NativeProcess.cs b/src/Man.Dapr.Sidekick/Native/NativeProcess.cs index 6706a46..9bcab91 100644 --- a/src/Man.Dapr.Sidekick/Native/NativeProcess.cs +++ b/src/Man.Dapr.Sidekick/Native/NativeProcess.cs @@ -84,6 +84,13 @@ public static extern IntPtr OpenProcess( public static int GetCommandLine(System.Diagnostics.Process process, out string commandLine) { + if (!DaprConstants.IsWindows) + { + // Return an empty command-line on non-Windows platforms + commandLine = string.Empty; + return 0; + } + var rc = 0; commandLine = null; var hProcess = OpenProcess( @@ -174,7 +181,7 @@ public static int GetCommandLine(System.Diagnostics.Process process, out string public static IEnumerable CommandLineToArgs(string commandLine) { - if (string.IsNullOrEmpty(commandLine)) + if (string.IsNullOrEmpty(commandLine) || !DaprConstants.IsWindows) { return new string[0]; } diff --git a/src/Man.Dapr.Sidekick/Process/PortAvailabilityChecker.cs b/src/Man.Dapr.Sidekick/Process/PortAvailabilityChecker.cs index 2f269e5..c0dad51 100644 --- a/src/Man.Dapr.Sidekick/Process/PortAvailabilityChecker.cs +++ b/src/Man.Dapr.Sidekick/Process/PortAvailabilityChecker.cs @@ -14,22 +14,31 @@ public int GetAvailablePort(int startingPort, IEnumerable reservedPorts = n throw new ArgumentException($"Starting Port cannot be greater than {ushort.MaxValue}", nameof(startingPort)); } - var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + try + { + // IPGlobalProperties.GetIPGlobalProperties() is not implemented in some platforms (throws System.NotImplementedException). + // For those we cannot do automatic port assignment, so just return starting port. + var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + + var connectionsEndpoints = ipGlobalProperties.GetActiveTcpConnections().Select(c => c.LocalEndPoint); + var tcpListenersEndpoints = ipGlobalProperties.GetActiveTcpListeners(); + var udpListenersEndpoints = ipGlobalProperties.GetActiveUdpListeners(); + var portsInUse = connectionsEndpoints.Concat(tcpListenersEndpoints) + .Concat(udpListenersEndpoints) + .Select(e => e.Port) + .ToList(); + // Add any additional reserved ports + if (reservedPorts != null) + { + portsInUse.AddRange(reservedPorts); + } - var connectionsEndpoints = ipGlobalProperties.GetActiveTcpConnections().Select(c => c.LocalEndPoint); - var tcpListenersEndpoints = ipGlobalProperties.GetActiveTcpListeners(); - var udpListenersEndpoints = ipGlobalProperties.GetActiveUdpListeners(); - var portsInUse = connectionsEndpoints.Concat(tcpListenersEndpoints) - .Concat(udpListenersEndpoints) - .Select(e => e.Port) - .ToList(); - // Add any additional reserved ports - if (reservedPorts != null) + return Enumerable.Range(startingPort, ushort.MaxValue - startingPort + 1).Except(portsInUse).FirstOrDefault(); + } + catch { - portsInUse.AddRange(reservedPorts); + return startingPort; } - - return Enumerable.Range(startingPort, ushort.MaxValue - startingPort + 1).Except(portsInUse).FirstOrDefault(); } } } diff --git a/tests/Man.Dapr.Sidekick.Tests/Man.Dapr.Sidekick.Tests.csproj b/tests/Man.Dapr.Sidekick.Tests/Man.Dapr.Sidekick.Tests.csproj index 7400fd3..1df72b3 100644 --- a/tests/Man.Dapr.Sidekick.Tests/Man.Dapr.Sidekick.Tests.csproj +++ b/tests/Man.Dapr.Sidekick.Tests/Man.Dapr.Sidekick.Tests.csproj @@ -23,4 +23,8 @@ + + + + diff --git a/tests/Man.Dapr.Sidekick.Tests/Process/DaprProcessTests.cs b/tests/Man.Dapr.Sidekick.Tests/Process/DaprProcessTests.cs index 8aa3fc6..3351bd4 100644 --- a/tests/Man.Dapr.Sidekick.Tests/Process/DaprProcessTests.cs +++ b/tests/Man.Dapr.Sidekick.Tests/Process/DaprProcessTests.cs @@ -1,5 +1,4 @@ -#if NETFRAMEWORK -using System.Linq; +using System.Linq; using Man.Dapr.Sidekick.Logging; using NSubstitute; using NUnit.Framework; @@ -273,4 +272,3 @@ public void Should_throw_exception_when_duplicate_process() } } } -#endif diff --git a/tests/Man.Dapr.Sidekick.Tests/Process/ProcessCommandLineTests.cs b/tests/Man.Dapr.Sidekick.Tests/Process/ProcessCommandLineTests.cs index 4b45439..441d65d 100644 --- a/tests/Man.Dapr.Sidekick.Tests/Process/ProcessCommandLineTests.cs +++ b/tests/Man.Dapr.Sidekick.Tests/Process/ProcessCommandLineTests.cs @@ -15,9 +15,18 @@ public void Should_retrieve_commandline() Assert.That(cmd, Is.Not.Null); Assert.That(cmd.Process, Is.SameAs(process)); - // Command line should always contain process EXE name - Assert.That(cmd.CommandLine, Is.Not.Null); - Assert.That(cmd.Arguments, Is.Not.Empty); + if (DaprConstants.IsWindows) + { + // Command line should always contain process EXE name + Assert.That(cmd.CommandLine, Is.Not.Null); + Assert.That(cmd.Arguments, Is.Not.Empty); + } + else + { + // On non-windows platforms everything is empty + Assert.That(cmd.CommandLine, Is.Empty); + Assert.That(cmd.Arguments, Is.Empty); + } var arguments = cmd.GetArgumentsAsDictionary(); Assert.That(arguments, Is.Not.Null); diff --git a/tests/Man.Dapr.Sidekick.Tests/TestResourceHelper.cs b/tests/Man.Dapr.Sidekick.Tests/TestResourceHelper.cs index 497bd34..db3728e 100644 --- a/tests/Man.Dapr.Sidekick.Tests/TestResourceHelper.cs +++ b/tests/Man.Dapr.Sidekick.Tests/TestResourceHelper.cs @@ -1,4 +1,8 @@ using System.IO; +#if !NETFRAMEWORK +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +#endif namespace Man.Dapr.Sidekick { @@ -25,12 +29,17 @@ public static string GetResourceFileText(string resourceFile) using var streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } -#if NETFRAMEWORK public static string CompileTestSystemProcessExe() { - var filename = Path.ChangeExtension(Path.GetTempFileName(), ".exe"); var source = GetResourceFileText("ProcessProgram.cs"); + var filename = Path.GetTempFileName(); + if (DaprConstants.IsWindows) + { + filename = Path.ChangeExtension(filename, "exe"); + } + +#if NETFRAMEWORK var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); var cp = new System.CodeDom.Compiler.CompilerParameters { @@ -39,10 +48,22 @@ public static string CompileTestSystemProcessExe() GenerateInMemory = false }; provider.CompileAssemblyFromSource(cp, source); +#else + var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location); + var syntaxTree = CSharpSyntaxTree.ParseText(source); + var compilation = CSharpCompilation + .Create(Path.GetFileName(filename)) + .WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication)) + .AddReferences( + MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")), + MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Console.dll")), + MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll"))) + .AddSyntaxTrees(syntaxTree); + var result = compilation.Emit(filename); +#endif return filename; } -#endif public static void DeleteTestProcess(string filename, int waitMilliseconds = 2000) {