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.");