Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1444876: Support for TOML connections #995

Merged
merged 17 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0d9abd0
Initial implementation for TOML connections
sfc-gh-jmartinezramirez Jul 10, 2024
2fe1ca6
Added testing for toml builder and snowflakedbconnection
sfc-gh-jmartinezramirez Jul 18, 2024
4588af9
Fix test to support all environments
sfc-gh-jmartinezramirez Jul 19, 2024
501dc04
Changed attribute to IgnoreOnCI to avoid run test in github actions o…
sfc-gh-jmartinezramirez Jul 19, 2024
7c2c632
Added CI flag to run test on jenkins
sfc-gh-jmartinezramirez Jul 19, 2024
82f748f
Initial support for loading token from token_file_path for oauth auth…
sfc-gh-jmartinezramirez Jul 19, 2024
84c0481
Added testing for oauth token loading mechanism
sfc-gh-jmartinezramirez Jul 22, 2024
ba71da6
Added documentation of configuration file.
sfc-gh-jmartinezramirez Jul 22, 2024
430921c
Added test for unix and file operations
sfc-gh-jmartinezramirez Jul 23, 2024
ab36f3f
Applying PR suggestions
sfc-gh-jmartinezramirez Aug 22, 2024
dca4e78
Applying additional PR suggestions
sfc-gh-jmartinezramirez Sep 3, 2024
abde7b3
Applying PR suggestions
sfc-gh-jmartinezramirez Oct 4, 2024
804dd0f
Added additional debug log messages
sfc-gh-jmartinezramirez Oct 10, 2024
5086293
fix not compiling code
sfc-gh-knozderko Oct 18, 2024
26142c1
Added mechanism to throw exception if file in token_file_path does no…
sfc-gh-jmartinezramirez Oct 30, 2024
1002467
Changed log level messages for toml configuration file reading
sfc-gh-jmartinezramirez Oct 31, 2024
615718a
Changed toml file permissions to check 400 and 600 permissions only.
sfc-gh-jmartinezramirez Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
633 changes: 319 additions & 314 deletions .gitignore

Large diffs are not rendered by default.

23 changes: 11 additions & 12 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@
* Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
*/

using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Snowflake.Data.Client;
using Snowflake.Data.Core;
using Snowflake.Data.Core.Session;
using Snowflake.Data.Log;
using Snowflake.Data.Tests.Mock;
using Snowflake.Data.Tests.Util;

namespace Snowflake.Data.Tests.IntegrationTests
{
using NUnit.Framework;
using Snowflake.Data.Client;
using System.Data;
using System;
using Snowflake.Data.Core;
using System.Threading.Tasks;
using System.Threading;
using Snowflake.Data.Log;
using System.Diagnostics;
using Snowflake.Data.Tests.Mock;
using System.Runtime.InteropServices;
using System.Net.Http;

[TestFixture]
class SFConnectionIT : SFBaseTest
Expand Down
143 changes: 143 additions & 0 deletions Snowflake.Data.Tests/IntegrationTests/SFConnectionWithTomlIT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2024 Snowflake Computing Inc. All rights reserved.
*/

using System;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;
using Mono.Unix.Native;
using NUnit.Framework;
using Snowflake.Data.Client;
using Snowflake.Data.Core;
using Snowflake.Data.Log;
using Tomlyn;
using Tomlyn.Model;

namespace Snowflake.Data.Tests.IntegrationTests
{

[TestFixture, NonParallelizable]
class SFConnectionWithTomlIT : SFBaseTest
{
private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger<SFConnectionIT>();

private static string s_workingDirectory;


[SetUp]
public new void BeforeTest()
{
s_workingDirectory ??= Path.Combine(TestContext.CurrentContext.WorkDirectory, "../../..", "toml_config_folder");
if (!Directory.Exists(s_workingDirectory))
{
Directory.CreateDirectory(s_workingDirectory);
}
CreateTomlConfigBaseOnConnectionString(ConnectionString);
}

[TearDown]
public new void AfterTest()
{
Directory.Delete(s_workingDirectory, true);
}

[Test]
public void TestLocalDefaultConnectStringReadFromToml()
{
var snowflakeHome = Environment.GetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome);
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome, s_workingDirectory);
try
{
using (var conn = new SnowflakeDbConnection())
{
conn.Open();
Assert.AreEqual(ConnectionState.Open, conn.State);
}
}
finally
{
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome, snowflakeHome);
}
}

[Test]
public void TestThrowExceptionIfTomlNotFoundWithOtherConnectionString()
{
var snowflakeHome = Environment.GetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome);
var connectionName = Environment.GetEnvironmentVariable(TomlConnectionBuilder.SnowflakeDefaultConnectionName);
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome, s_workingDirectory);
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeDefaultConnectionName, "notfoundconnection");
try
{
using (var conn = new SnowflakeDbConnection())
{
Assert.Throws<SnowflakeDbException>(() => conn.Open(), "Unable to connect. Specified connection name does not exist in connections.toml");
}
}
finally
{
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome, snowflakeHome);
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeDefaultConnectionName, connectionName);
}
}

[Test]
public void TestThrowExceptionIfTomlFromNotFoundFromDbConnection()
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
var snowflakeHome = Environment.GetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome);
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome, Path.Combine(s_workingDirectory, "InvalidFolder"));
try
{
using (var conn = new SnowflakeDbConnection())
{
Assert.Throws<SnowflakeDbException>(() => conn.Open(), "Error: Required property ACCOUNT is not provided");
}
}
finally
{
Environment.SetEnvironmentVariable(TomlConnectionBuilder.SnowflakeHome, snowflakeHome);
}
}

private static void CreateTomlConfigBaseOnConnectionString(string connectionString)
{
var tomlModel = new TomlTable();
var properties = SFSessionProperties.ParseConnectionString(connectionString, null);

var defaultTomlTable = new TomlTable();
tomlModel.Add("default", defaultTomlTable);

foreach (var property in properties)
{
defaultTomlTable.Add(property.Key.ToString(), property.Value);
}

var filePath = Path.Combine(s_workingDirectory, "connections.toml");

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
using (var writer = File.CreateText(filePath))
{
writer.Write(Toml.FromModel(tomlModel));
}
}
else
{
using (var writer = File.CreateText(filePath))
{
writer.Write(string.Empty);
}
Syscall.chmod(filePath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR);
using (var writer = File.CreateText(filePath))
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
writer.Write(Toml.FromModel(tomlModel));
}
Syscall.chmod(filePath, FilePermissions.S_IRUSR | FilePermissions.S_IWUSR);
}
}
}

}


15 changes: 13 additions & 2 deletions Snowflake.Data.Tests/SFBaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,14 @@ public class IgnoreOnEnvIsAttribute : Attribute, ITestAction
private readonly string _key;

private readonly string[] _values;
public IgnoreOnEnvIsAttribute(string key, string[] values)

private readonly string _reason;

public IgnoreOnEnvIsAttribute(string key, string[] values, string reason = null)
{
_key = key;
_values = values;
_reason = reason;
}

public void BeforeTest(ITest test)
Expand All @@ -433,7 +437,7 @@ public void BeforeTest(ITest test)
{
if (Environment.GetEnvironmentVariable(_key) == value)
{
Assert.Ignore("Test is ignored when environment variable {0} is {1} ", _key, value);
Assert.Ignore("Test is ignored when environment variable {0} is {1}. {2}", _key, value, _reason);
}
}
}
Expand Down Expand Up @@ -468,4 +472,11 @@ public void AfterTest(ITest test)

public ActionTargets Targets => ActionTargets.Test | ActionTargets.Suite;
}

public class IgnoreOnCI : IgnoreOnEnvIsAttribute
{
public IgnoreOnCI(string reason = null) : base("CI", new[] { "true" }, reason)
{
}
}
}
1 change: 1 addition & 0 deletions Snowflake.Data.Tests/Snowflake.Data.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="Tomlyn.Signed" Version="0.17.0" />
<ProjectReference Include="..\Snowflake.Data\Snowflake.Data.csproj" />
</ItemGroup>
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ public void Setup()
MockHomeDirectory();
MockExecutionDirectory();
}

[Test]
public void TestThatTakesFilePathFromTheInput()
{
// arrange
MockFileFromEnvironmentalVariable();
MockFileOnDriverPath();
MockFileOnHomePath();

// act
var filePath = t_finder.FindConfigFilePath(InputConfigFilePath);

// assert
Assert.AreEqual(InputConfigFilePath, filePath);
t_fileOperations.VerifyNoOtherCalls();
Expand All @@ -71,14 +71,14 @@ public void TestThatTakesFilePathFromEnvironmentVariableIfInputNotPresent(
MockFileFromEnvironmentalVariable();
MockFileOnDriverPath();
MockFileOnHomePath();

// act
var filePath = t_finder.FindConfigFilePath(inputFilePath);

// assert
Assert.AreEqual(EnvironmentalConfigFilePath, filePath);
}

[Test]
public void TestThatTakesFilePathFromDriverLocationWhenNoInputParameterNorEnvironmentVariable()
{
Expand All @@ -88,20 +88,20 @@ public void TestThatTakesFilePathFromDriverLocationWhenNoInputParameterNorEnviro

// act
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.AreEqual(s_driverConfigFilePath, filePath);
}

[Test]
public void TestThatTakesFilePathFromHomeLocationWhenNoInputParamEnvironmentVarNorDriverLocation()
{
// arrange
MockFileOnHomePath();

// act
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.AreEqual(s_homeConfigFilePath, filePath);
}
Expand Down Expand Up @@ -138,13 +138,13 @@ public void TestThatConfigFileIsNotUsedIfOthersCanModifyTheConfigFile()
Assert.IsNotNull(thrown);
Assert.AreEqual(thrown.Message, $"Error due to other users having permission to modify the config file: {s_homeConfigFilePath}");
}

[Test]
public void TestThatReturnsNullIfNoWayOfGettingTheFile()
{
// act
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.IsNull(filePath);
}
Expand All @@ -157,7 +157,7 @@ public void TestThatDoesNotFailWhenSearchForOneOfDirectoriesFails()

// act
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.IsNull(filePath);
t_environmentOperations.Verify(e => e.GetFolderPath(Environment.SpecialFolder.UserProfile), Times.Once);
Expand Down Expand Up @@ -186,7 +186,7 @@ public void TestThatDoesNotFailWhenHomeDirectoryDoesNotExist()

// act
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.IsNull(filePath);
t_environmentOperations.Verify(e => e.GetFolderPath(Environment.SpecialFolder.UserProfile), Times.Once);
Expand Down Expand Up @@ -220,7 +220,7 @@ private static void MockExecutionDirectory()
.Setup(e => e.GetExecutionDirectory())
.Returns(DriverDirectory);
}

private static void MockFileOnHomePathDoesNotExist()
{
t_fileOperations
Expand Down
61 changes: 61 additions & 0 deletions Snowflake.Data.Tests/UnitTests/SnowflakeDbConnectionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@


using System;
using System.IO;
using Mono.Unix;

namespace Snowflake.Data.Tests.UnitTests
{
using Core;
using Core.Tools;
using Moq;
using NUnit.Framework;
using Snowflake.Data.Client;

public class SnowflakeDbConnectionTest
{
[Test]
public void TestFillConnectionStringFromTomlConfig()
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
// Arrange
var mockFileOperations = new Mock<FileOperations>();
var mockEnvironmentOperations = new Mock<EnvironmentOperations>();
mockEnvironmentOperations.Setup(e => e.GetFolderPath(Environment.SpecialFolder.UserProfile))
.Returns($"{Path.DirectorySeparatorChar}home");
mockFileOperations.Setup(f => f.Exists(It.IsAny<string>())).Returns(true);
mockFileOperations.Setup(f => f.ReadAllText(It.IsAny<string>(), It.IsAny<Action<UnixStream>>()))
.Returns("[default]\naccount=\"testaccount\"\nuser=\"testuser\"\npassword=\"testpassword\"\n");
var tomlConnectionBuilder = new TomlConnectionBuilder(mockFileOperations.Object, mockEnvironmentOperations.Object);

// Act
using (var conn = new SnowflakeDbConnection(tomlConnectionBuilder))
{
conn.FillConnectionStringFromTomlConfigIfNotSet();
// Assert
Assert.AreEqual("account=testaccount;user=testuser;password=testpassword;", conn.ConnectionString);
}
}

[Test]
public void TestTomlConfigurationDoesNotOverrideExistingConnectionString()
{
// Arrange
var connectionTest = "account=user1account;user=user1;password=user1password;";
var mockFileOperations = new Mock<FileOperations>();
var mockEnvironmentOperations = new Mock<EnvironmentOperations>();
mockFileOperations.Setup(f => f.Exists(It.IsAny<string>())).Returns(true);
mockFileOperations.Setup(f => f.ReadAllText(It.IsAny<string>()))
.Returns("[default]\naccount=\"testaccount\"\nuser=\"testuser\"\npassword=\"testpassword\"\n");
var tomlConnectionBuilder = new TomlConnectionBuilder(mockFileOperations.Object, mockEnvironmentOperations.Object);

// Act
using (var conn = new SnowflakeDbConnection(tomlConnectionBuilder))
{
conn.ConnectionString = connectionTest;
conn.FillConnectionStringFromTomlConfigIfNotSet();
// Assert
Assert.AreEqual(connectionTest, conn.ConnectionString);
}
}
}
}
Loading
Loading