This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 5c9a8c7a95b IGNITE-17063 .NET: Improve Java detection on Linux (#10069)
5c9a8c7a95b is described below

commit 5c9a8c7a95be59a5299a2ae05a61611a3a45129b
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Tue Jun 7 09:59:33 2022 +0300

    IGNITE-17063 .NET: Improve Java detection on Linux (#10069)
    
    * Try `/usr/bin/readlink` as well as `readlink` - it is not in PATH in some 
rare scenarios.
    * Improve logging when commands fail.
---
 .../dotnet/Apache.Ignite.Core.Tests/ShellTests.cs  | 82 +++++++++++++++++-----
 .../dotnet/Apache.Ignite.Core/Impl/Shell.cs        | 56 ++++++++++++++-
 .../Impl/Unmanaged/Jni/JvmDll.cs                   |  7 +-
 3 files changed, 123 insertions(+), 22 deletions(-)

diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ShellTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ShellTests.cs
index 220c071e3d5..4c689876247 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ShellTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/ShellTests.cs
@@ -17,38 +17,82 @@
 
 namespace Apache.Ignite.Core.Tests
 {
+    using System;
+    using System.Linq;
     using Apache.Ignite.Core.Impl;
-    using Apache.Ignite.Core.Impl.Unmanaged;
+    using Apache.Ignite.Core.Log;
+    using Apache.Ignite.Core.Tests.Client.Cache;
     using NUnit.Framework;
 
     /// <summary>
     /// Tests for <see cref="Shell"/> class.
     /// </summary>
+    [Platform("Linux")]
+    [Order(1)] // Execute first to avoid zombie processes (see 
https://issues.apache.org/jira/browse/IGNITE-13536).
     public class ShellTests
     {
         /// <summary>
         /// Tests <see cref="Shell.ExecuteSafe"/> method.
         /// </summary>
         [Test]
-        public void TestExecuteSafe()
+        public void TestExecuteSafeReturnsStdout()
         {
-            if (Os.IsWindows)
-            {
-                return;
-            }
-            
-            var uname = Shell.ExecuteSafe("uname", string.Empty);
-            Assert.IsNotEmpty(uname, uname);
-
-            var readlink = Shell.ExecuteSafe("readlink", "-f /usr/bin/java");
-            Assert.IsNotEmpty(readlink, readlink);
-            
-            if (Os.IsLinux)
-            {
-                Assert.AreEqual("Linux", uname.Trim());
-            }
+            var log = GetLogger();
+
+            var uname = Shell.ExecuteSafe("uname", string.Empty, log: log);
+            Assert.AreEqual("Linux", uname.Trim(), string.Join(", ", 
log.Entries.Select(e => e.ToString())));
+            Assert.IsEmpty(log.Entries);
+
+            var readlink = Shell.ExecuteSafe("readlink", "-f /usr/bin/java", 
log: log);
+            Assert.IsNotEmpty(readlink, readlink, string.Join(", ", 
log.Entries.Select(e => e.ToString())));
+            Assert.IsEmpty(log.Entries);
+        }
+
+        /// <summary>
+        /// Tests that non-zero error code and stderr are logged.
+        /// </summary>
+        [Test]
+        public void TestExecuteSafeLogsNonZeroExitCodeAndStderr()
+        {
+            var log = GetLogger();
+            var res = Shell.ExecuteSafe("uname", "--badarg", log: log);
+
+            var entries = log.Entries;
+            var entriesString = string.Join(", ", entries.Select(e => 
e.ToString()));
+
+            Assert.IsEmpty(res, entriesString);
+            Assert.AreEqual(2, entries.Count, entriesString);
+
+            Assert.AreEqual(LogLevel.Warn, entries[0].Level);
+            StringAssert.StartsWith("Shell command 'uname' stderr: 'uname: 
unrecognized option", entries[0].Message);
 
-            Assert.IsEmpty(Shell.ExecuteSafe("foo_bar", "abc"));
+            Assert.AreEqual(LogLevel.Warn, entries[1].Level);
+            Assert.AreEqual("Shell command 'uname' exit code: 1", 
entries[1].Message);
+        }
+
+        /// <summary>
+        /// Tests that failure to execute a command is logged.
+        /// </summary>
+        [Test]
+        public void TestExecuteSafeLogsException()
+        {
+            var log = GetLogger();
+            var res = Shell.ExecuteSafe("foo_bar", "abc", log: log);
+            var entries = log.Entries;
+
+            Assert.IsEmpty(res);
+            Assert.AreEqual(1, entries.Count);
+
+            Assert.AreEqual(LogLevel.Warn, entries[0].Level);
+            Assert.AreEqual("Shell command 'foo_bar' failed: 'No such file or 
directory'", entries[0].Message);
+        }
+
+        private static ListLogger GetLogger()
+        {
+            return new ListLogger
+            {
+                EnabledLevels = 
Enum.GetValues(typeof(LogLevel)).Cast<LogLevel>().ToArray()
+            };
         }
     }
-}
\ No newline at end of file
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Shell.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Shell.cs
index 458d2255894..3694c648bfd 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Shell.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Shell.cs
@@ -20,6 +20,8 @@ namespace Apache.Ignite.Core.Impl
     using System;
     using System.Diagnostics;
     using System.Diagnostics.CodeAnalysis;
+    using System.Text;
+    using System.Threading;
     using Apache.Ignite.Core.Log;
 
     /// <summary>
@@ -42,14 +44,49 @@ namespace Apache.Ignite.Core.Impl
                     FileName = file,
                     Arguments = args,
                     RedirectStandardOutput = true,
+                    RedirectStandardError = true,
                     UseShellExecute = false,
                     CreateNoWindow = true
                 };
 
+                var stdOut = new StringBuilder();
+                var stdErr = new StringBuilder();
+
+                using (var stdOutEvt = new ManualResetEventSlim())
+                using (var stdErrEvt = new ManualResetEventSlim())
                 using (var process = new Process {StartInfo = 
processStartInfo})
                 {
+                    process.OutputDataReceived += (_, e) =>
+                    {
+                        if (e.Data == null)
+                        {
+                            // ReSharper disable once AccessToDisposedClosure
+                            stdOutEvt.Set();
+                        }
+                        else
+                        {
+                            stdOut.AppendLine(e.Data);
+                        }
+                    };
+
+                    process.ErrorDataReceived += (_, e) =>
+                    {
+                        if (e.Data == null)
+                        {
+                            // ReSharper disable once AccessToDisposedClosure
+                            stdErrEvt.Set();
+                        }
+                        else
+                        {
+                            stdErr.AppendLine(e.Data);
+                        }
+                    };
+
                     process.Start();
 
+                    process.BeginOutputReadLine();
+                    process.BeginErrorReadLine();
+
                     if (!process.WaitForExit(timeoutMs))
                     {
                         log?.Warn("Shell command '{0}' timed out.", file);
@@ -57,12 +94,27 @@ namespace Apache.Ignite.Core.Impl
                         process.Kill();
                     }
 
-                    return process.StandardOutput.ReadToEnd();
+                    if (!stdOutEvt.Wait(timeoutMs) || 
!stdErrEvt.Wait(timeoutMs))
+                    {
+                        log?.Warn("Shell command '{0}' timed out when waiting 
for stdout/stderr.", file);
+                    }
+
+                    if (stdErr.Length > 0)
+                    {
+                        log?.Warn("Shell command '{0}' stderr: '{1}'", file, 
stdErr);
+                    }
+
+                    if (process.ExitCode != 0)
+                    {
+                        log?.Warn("Shell command '{0}' exit code: {1}", file, 
process.ExitCode);
+                    }
+
+                    return stdOut.ToString();
                 }
             }
             catch (Exception e)
             {
-                log?.Warn("Shell command '{0}' failed: {1}", file, e.Message, 
e);
+                log?.Warn("Shell command '{0}' failed: '{1}'", file, 
e.Message, e);
 
                 return string.Empty;
             }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs
index 9bfdbf301ff..e30b0ab5dd5 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Unmanaged/Jni/JvmDll.cs
@@ -367,9 +367,14 @@ namespace Apache.Ignite.Core.Impl.Unmanaged.Jni
                 yield break;
             }
 
-            var file = Shell.ExecuteSafe("readlink", "-f /usr/bin/java", log: 
log);
+            var file = Shell.ExecuteSafe("/usr/bin/readlink", "-f 
/usr/bin/java", log: log);
             // /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
 
+            if (string.IsNullOrWhiteSpace(file))
+            {
+                file = Shell.ExecuteSafe("readlink", "-f /usr/bin/java", log: 
log);
+            }
+
             if (string.IsNullOrWhiteSpace(file))
             {
                 log.Debug("readlink -f /usr/bin/java failed.");

Reply via email to