diff --git a/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs b/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs index d120abcfd..595fbb65d 100644 --- a/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/EasyLoggingIT.cs @@ -95,5 +95,30 @@ public void TestFailToEnableEasyLoggingWhenConfigHasWrongPermissions() Assert.IsFalse(EasyLoggerManager.HasEasyLoggingAppender()); } } + + [Test] + public void TestFailToEnableEasyLoggingWhenLogDirectoryNotAccessible() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Ignore("skip test on Windows"); + } + + // arrange + var configFilePath = CreateConfigTempFile(s_workingDirectory, Config("WARN", "/")); + using (IDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString + $"CLIENT_CONFIG_FILE={configFilePath}"; + + // act + var thrown = Assert.Throws(() => conn.Open()); + + // assert + Assert.That(thrown.Message, Does.Contain("Connection string is invalid: Unable to connect")); + Assert.That(thrown.InnerException.Message, Does.Contain("Failed to create logs directory")); + Assert.IsFalse(EasyLoggerManager.HasEasyLoggingAppender()); + } + + } } } \ No newline at end of file diff --git a/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs b/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs index ba4460531..b23fbbf0e 100644 --- a/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Configuration/EasyLoggingConfigFinderTest.cs @@ -43,6 +43,7 @@ public void Setup() t_environmentOperations = new Mock(); t_finder = new EasyLoggingConfigFinder(t_fileOperations.Object, t_unixOperations.Object, t_environmentOperations.Object); MockHomeDirectory(); + MockExecutionDirectory(); } [Test] @@ -213,6 +214,13 @@ private static void MockHomeDirectoryFails() .Throws(() => new Exception("No home directory")); } + private static void MockExecutionDirectory() + { + t_environmentOperations + .Setup(e => e.GetExecutionDirectory()) + .Returns(DriverDirectory); + } + private static void MockFileOnHomePathDoesNotExist() { t_fileOperations diff --git a/Snowflake.Data.Tests/UnitTests/Session/EasyLoggingStarterTest.cs b/Snowflake.Data.Tests/UnitTests/Session/EasyLoggingStarterTest.cs index 3b96339a0..8b1c6ebfe 100644 --- a/Snowflake.Data.Tests/UnitTests/Session/EasyLoggingStarterTest.cs +++ b/Snowflake.Data.Tests/UnitTests/Session/EasyLoggingStarterTest.cs @@ -151,6 +151,29 @@ public void TestThatDoesNotFailWhenLogDirectoryPermissionIsNot700() FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR), Times.Never); } + [Test] + public void TestFailIfDirectoryCreationFails() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Ignore("skip test on Windows"); + } + + // arrange + t_easyLoggingProvider + .Setup(provider => provider.ProvideConfig(ConfigPath)) + .Returns(s_configWithErrorLevel); + t_unixOperations + .Setup(u => u.CreateDirectoryWithPermissions(s_expectedLogPath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR)) + .Returns((int)Errno.EPERM); + + // act + var thrown = Assert.Throws(() => t_easyLoggerStarter.Init(ConfigPath)); + + // assert + Assert.That(thrown.Message, Does.Contain("Failed to create logs directory")); + } + [Test] public void TestThatConfiguresEasyLoggingOnlyOnceWhenInitializedWithConfigPath() { diff --git a/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs b/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs index fab097027..d417eb4d8 100644 --- a/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs +++ b/Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs @@ -68,8 +68,8 @@ private string GetFilePathFromInputParameter(string filePath, string inputDescri private string GetHomeDirectory() => HomeDirectoryProvider.HomeDirectory(_environmentOperations); - private string GetFilePathFromDriverLocation() => SearchForConfigInDirectory(() => ".", "driver"); - + private string GetFilePathFromDriverLocation() => SearchForConfigInDirectory(() => _environmentOperations.GetExecutionDirectory(), "driver"); + private string SearchForConfigInDirectory(Func directoryProvider, string directoryDescription) { try diff --git a/Snowflake.Data/Core/Session/EasyLoggingStarter.cs b/Snowflake.Data/Core/Session/EasyLoggingStarter.cs index 894e9308a..51a402bf1 100644 --- a/Snowflake.Data/Core/Session/EasyLoggingStarter.cs +++ b/Snowflake.Data/Core/Session/EasyLoggingStarter.cs @@ -141,8 +141,13 @@ private string GetLogPath(string logPath) { Directory.CreateDirectory(logPathOrDefault); } - _unixOperations.CreateDirectoryWithPermissions(pathWithDotnetSubdirectory, + var createDirResult = _unixOperations.CreateDirectoryWithPermissions(pathWithDotnetSubdirectory, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR | FilePermissions.S_IXUSR); + if (createDirResult != 0) + { + s_logger.Error($"Failed to create logs directory: {pathWithDotnetSubdirectory}"); + throw new Exception("Failed to create logs directory"); + } } } CheckDirPermissionsOnlyAllowUser(pathWithDotnetSubdirectory); diff --git a/Snowflake.Data/Core/Tools/EnvironmentOperations.cs b/Snowflake.Data/Core/Tools/EnvironmentOperations.cs index a295d73a5..1f1959986 100644 --- a/Snowflake.Data/Core/Tools/EnvironmentOperations.cs +++ b/Snowflake.Data/Core/Tools/EnvironmentOperations.cs @@ -3,12 +3,15 @@ */ using System; +using System.IO; +using Snowflake.Data.Log; namespace Snowflake.Data.Core.Tools { internal class EnvironmentOperations { public static readonly EnvironmentOperations Instance = new EnvironmentOperations(); + private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); public virtual string GetEnvironmentVariable(string variable) { @@ -19,5 +22,17 @@ public virtual string GetFolderPath(Environment.SpecialFolder folder) { return Environment.GetFolderPath(folder); } + + public virtual string GetExecutionDirectory() + { + var executablePath = Environment.GetCommandLineArgs()[0]; + var directoryName = string.IsNullOrEmpty(executablePath) ? null : Path.GetDirectoryName(executablePath); + if (string.IsNullOrEmpty(directoryName)) + { + s_logger.Warn("Unable to determine execution directory"); + return null; + } + return directoryName; + } } } diff --git a/Snowflake.Data/Core/Tools/UnixOperations.cs b/Snowflake.Data/Core/Tools/UnixOperations.cs index 2437e7b74..cb44099b7 100644 --- a/Snowflake.Data/Core/Tools/UnixOperations.cs +++ b/Snowflake.Data/Core/Tools/UnixOperations.cs @@ -11,9 +11,9 @@ internal class UnixOperations { public static readonly UnixOperations Instance = new UnixOperations(); - public virtual void CreateDirectoryWithPermissions(string path, FilePermissions permissions) + public virtual int CreateDirectoryWithPermissions(string path, FilePermissions permissions) { - Syscall.mkdir(path, permissions); + return Syscall.mkdir(path, permissions); } public virtual FileAccessPermissions GetDirPermissions(string path) diff --git a/Snowflake.Data/Snowflake.Data.csproj b/Snowflake.Data/Snowflake.Data.csproj index 89a70685f..627cbfcf7 100644 --- a/Snowflake.Data/Snowflake.Data.csproj +++ b/Snowflake.Data/Snowflake.Data.csproj @@ -24,7 +24,7 @@ - +