Skip to content

Commit 653a8f6

Browse files
Fix Config File Path Issue (#1681)
## Why make this change? - Closes #1674 ConfigFile was not found when running in the Azure Container Apps. ## What is this change? - Because of the way we used to fetch the cofigFileName, the path was lost, due to which using config file present in a different directory was giving `file not found` error. - So we updated the method to fetch the configFileNamewihoutExtension to keep the original path. - To run Azure Container Apps `--ConfigFileName` is provided with the full path name to the mounted config file. NOTE: * This issue might be caught in Az Container apps but this is not specifically related to Containers, because the current file name generation process was itself incorrect in 0.8.* release. * When we try to get the `fileNameWithoutExtension` in the method `GetFileNameForEnvironment`, we used to lose directory information, for example: if baseConfigFile is`dab/configs/dab-config.json`, then `configFileNameWithoutExtension` was coming as `dab-config`, which is incorrect. * This issue started happening from 0.8.* because in the earlier release (0.7.6), if config file name is provided by user, we directly used it without searching for overrides and environments, unlike 0.8.*. And the precedence check used to happen for cases where config file is not provided by user. * Now, since 0.8.*, we started using the method `GetFileNameWithoutExtension`, which is incorrect causing File not found issue whenever the config file is not in current directory. * The method `DoesFileExistInCurrentDirectory()` checks if the file exists by combining the current directory with the fileName. Now if file is existing in some other directory inside current directory, then for proper searching the file should contain complete info about the directory. So, if current dicrectory is `C:/dab`, and file is in `C:/dab/configs/dab-config.json`, then the file name should either be full path or `/configs/dab-configs.json` for it to be searched. ## Additional Change 1. Fixing name of variables and function name to correctly reflect their behavior. 2. Fixing issue with not honoring the user provided config file, (caused after 0.8 release). (Putting in this PR, because this PR aims at fixing how we are using the config files and not just a bug fix.) ## How was this tested? - [X] Running In Azure Container Apps BEFORE: ![image](https://github.com/Azure/data-api-builder/assets/102276754/183d9399-9822-4435-8322-e7bfc5016e1e) AFTER: ![image](https://github.com/Azure/data-api-builder/assets/102276754/fb7010c9-4410-48cf-9146-9cc757f209a0) - [X] Local Testing BEFORE: ![image](https://github.com/Azure/data-api-builder/assets/102276754/742cfd03-ee54-4f0c-8ea4-9e1d2303bbf8) AFTER: ![image](https://github.com/Azure/data-api-builder/assets/102276754/bef4d153-9f5a-470b-8fbb-27ed7219fac6) - [X] Unit Tests --------- Co-authored-by: Aniruddh Munde <[email protected]>
1 parent 1b82fb8 commit 653a8f6

6 files changed

Lines changed: 132 additions & 32 deletions

File tree

src/Cli/ConfigGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun
966966
return false;
967967
}
968968

969-
loader.UpdateConfigFileName(runtimeConfigFile);
969+
loader.UpdateConfigFilePath(runtimeConfigFile);
970970

971971
// Validates that config file has data and follows the correct json schema
972972
// Replaces all the environment variables while deserializing when starting DAB.

src/Cli/ConfigMerger.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static bool TryMergeConfigsIfAvailable(IFileSystem fileSystem, FileSystem
2424
string baseConfigFile = FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME;
2525
string environmentBasedConfigFile = loader.GetFileName(environmentValue, considerOverrides: false);
2626

27-
if (loader.DoesFileExistInCurrentDirectory(baseConfigFile) && !string.IsNullOrEmpty(environmentBasedConfigFile))
27+
if (loader.DoesFileExistInDirectory(baseConfigFile) && !string.IsNullOrEmpty(environmentBasedConfigFile))
2828
{
2929
try
3030
{

src/Config/FileSystemRuntimeConfigLoader.cs

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace Azure.DataApiBuilder.Config;
2626
public class FileSystemRuntimeConfigLoader : RuntimeConfigLoader
2727
{
2828
// This stores either the default config name e.g. dab-config.json
29-
// or user provided config file.
30-
private string _baseConfigFileName;
29+
// or user provided config file which could be a relative file path, absolute file path or simply the file name assumed to be in current directory.
30+
private string _baseConfigFilePath;
3131

3232
private readonly IFileSystem _fileSystem;
3333

@@ -45,19 +45,20 @@ public class FileSystemRuntimeConfigLoader : RuntimeConfigLoader
4545
public const string DEFAULT_CONFIG_FILE_NAME = $"{CONFIGFILE_NAME}{CONFIG_EXTENSION}";
4646

4747
/// <summary>
48-
/// Stores the config file name actually loaded by the engine.
48+
/// Stores the config file actually loaded by the engine.
4949
/// It could be the base config file (e.g. dab-config.json), any of its derivatives with
5050
/// environment specific suffixes (e.g. dab-config.Development.json) or the user provided
5151
/// config file name.
52+
/// It could also be the config file provided by the user.
5253
/// </summary>
53-
public string ConfigFileName { get; internal set; }
54+
public string ConfigFilePath { get; internal set; }
5455

55-
public FileSystemRuntimeConfigLoader(IFileSystem fileSystem, string baseConfigFileName = DEFAULT_CONFIG_FILE_NAME, string? connectionString = null)
56+
public FileSystemRuntimeConfigLoader(IFileSystem fileSystem, string baseConfigFilePath = DEFAULT_CONFIG_FILE_NAME, string? connectionString = null)
5657
: base(connectionString)
5758
{
5859
_fileSystem = fileSystem;
59-
_baseConfigFileName = baseConfigFileName;
60-
ConfigFileName = GetFileNameForEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), false);
60+
_baseConfigFilePath = baseConfigFilePath;
61+
ConfigFilePath = GetFinalConfigFilePath();
6162
}
6263

6364
/// <summary>
@@ -75,6 +76,7 @@ public bool TryLoadConfig(
7576
{
7677
if (_fileSystem.File.Exists(path))
7778
{
79+
Console.WriteLine($"Loading config file from {path}.");
7880
string json = _fileSystem.File.ReadAllText(path);
7981
return TryParseConfig(json, out config, connectionString: _connectionString, replaceEnvVar: replaceEnvVar);
8082
}
@@ -98,7 +100,7 @@ public bool TryLoadConfig(
98100
/// <returns>True if the config was loaded, otherwise false.</returns>
99101
public override bool TryLoadKnownConfig([NotNullWhen(true)] out RuntimeConfig? config, bool replaceEnvVar = false)
100102
{
101-
return TryLoadConfig(ConfigFileName, out config, replaceEnvVar);
103+
return TryLoadConfig(ConfigFilePath, out config, replaceEnvVar);
102104
}
103105

104106
/// <summary>
@@ -143,6 +145,29 @@ public string GetFileNameForEnvironment(string? aspnetEnvironment, bool consider
143145
return configFileNameWithExtension;
144146
}
145147

148+
/// <summary>
149+
/// This method returns the final config file name that will be used by the runtime engine.
150+
/// </summary>
151+
private string GetFinalConfigFilePath()
152+
{
153+
if (!string.Equals(_baseConfigFilePath, DEFAULT_CONFIG_FILE_NAME))
154+
{
155+
// user provided config file is honoured.
156+
return _baseConfigFilePath;
157+
}
158+
159+
// ConfigFile not explicitly provided by user, so we need to get the config file name based on environment.
160+
string configFilePath = GetFileNameForEnvironment(Environment.GetEnvironmentVariable(ASP_NET_CORE_ENVIRONMENT_VAR_NAME), false);
161+
162+
// If file for environment is not found, then the baseConfigFile is used as the final configFile for runtime engine.
163+
if (string.IsNullOrWhiteSpace(configFilePath))
164+
{
165+
return _baseConfigFilePath;
166+
}
167+
168+
return configFilePath;
169+
}
170+
146171
/// <summary>
147172
/// Generates the config file name and a corresponding overridden file name,
148173
/// With precedence given to overridden file name, returns that name
@@ -154,31 +179,34 @@ public string GetFileNameForEnvironment(string? aspnetEnvironment, bool consider
154179
/// <returns></returns>
155180
public string GetFileName(string? environmentValue, bool considerOverrides)
156181
{
157-
string fileNameWithoutExtension = _fileSystem.Path.GetFileNameWithoutExtension(_baseConfigFileName);
158-
string fileExtension = _fileSystem.Path.GetExtension(_baseConfigFileName);
159-
string configFileName =
182+
// If the baseConfigFilePath contains directory info, we need to ensure that it is not lost. for example: baseConfigFilePath = "config/dab-config.json"
183+
// in this case, we need to get the directory name and the file name without extension and then combine them back. Else, we will lose the path
184+
// and the file will be searched in the current directory.
185+
string filePathWithoutExtension = _fileSystem.Path.Combine(_fileSystem.Path.GetDirectoryName(_baseConfigFilePath) ?? string.Empty, _fileSystem.Path.GetFileNameWithoutExtension(_baseConfigFilePath));
186+
string fileExtension = _fileSystem.Path.GetExtension(_baseConfigFilePath);
187+
string configFilePath =
160188
!string.IsNullOrEmpty(environmentValue)
161-
? $"{fileNameWithoutExtension}.{environmentValue}"
162-
: $"{fileNameWithoutExtension}";
163-
string configFileNameWithExtension = $"{configFileName}{fileExtension}";
164-
string overriddenConfigFileNameWithExtension = GetOverriddenName(configFileName);
189+
? $"{filePathWithoutExtension}.{environmentValue}"
190+
: $"{filePathWithoutExtension}";
191+
string configFileNameWithExtension = $"{configFilePath}{fileExtension}";
192+
string overriddenConfigFileNameWithExtension = GetOverriddenName(configFilePath);
165193

166-
if (considerOverrides && DoesFileExistInCurrentDirectory(overriddenConfigFileNameWithExtension))
194+
if (considerOverrides && DoesFileExistInDirectory(overriddenConfigFileNameWithExtension))
167195
{
168196
return overriddenConfigFileNameWithExtension;
169197
}
170198

171-
if (DoesFileExistInCurrentDirectory(configFileNameWithExtension))
199+
if (DoesFileExistInDirectory(configFileNameWithExtension))
172200
{
173201
return configFileNameWithExtension;
174202
}
175203

176204
return string.Empty;
177205
}
178206

179-
private static string GetOverriddenName(string fileName)
207+
private static string GetOverriddenName(string filePath)
180208
{
181-
return $"{fileName}.overrides{CONFIG_EXTENSION}";
209+
return $"{filePath}.overrides{CONFIG_EXTENSION}";
182210
}
183211

184212
/// <summary>
@@ -190,10 +218,16 @@ public static string GetEnvironmentFileName(string fileName, string environmentV
190218
return $"{fileName}.{environmentValue}{CONFIG_EXTENSION}";
191219
}
192220

193-
public bool DoesFileExistInCurrentDirectory(string fileName)
221+
/// <summary>
222+
/// Checks if the file exists in the directory.
223+
/// Works for both relative and absolute paths.
224+
/// </summary>
225+
/// <param name="filePath"></param>
226+
/// <returns>True if file is found, else false.</returns>
227+
public bool DoesFileExistInDirectory(string filePath)
194228
{
195229
string currentDir = _fileSystem.Directory.GetCurrentDirectory();
196-
return _fileSystem.File.Exists(_fileSystem.Path.Combine(currentDir, fileName));
230+
return _fileSystem.File.Exists(_fileSystem.Path.Combine(currentDir, filePath));
197231
}
198232

199233
/// <summary>
@@ -256,9 +290,9 @@ public static string GetMergedFileNameForEnvironment(string fileName, string env
256290
/// to be updated. This is commonly done when the CLI is starting up.
257291
/// </summary>
258292
/// <param name="fileName"></param>
259-
public void UpdateConfigFileName(string fileName)
293+
public void UpdateConfigFilePath(string filePath)
260294
{
261-
_baseConfigFileName = fileName;
262-
ConfigFileName = fileName;
295+
_baseConfigFilePath = filePath;
296+
ConfigFilePath = filePath;
263297
}
264298
}

src/Service.Tests/Configuration/AuthenticationConfigValidatorUnitTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void ValidateEasyAuthConfig()
4747

4848
// Since we added the config file to the filesystem above after the config loader was initialized
4949
// in TestInitialize, we need to update the ConfigfileName, otherwise it will be an empty string.
50-
_runtimeConfigLoader.UpdateConfigFileName(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
50+
_runtimeConfigLoader.UpdateConfigFilePath(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
5151

5252
try
5353
{
@@ -75,7 +75,7 @@ public void ValidateJwtConfigParamsSet()
7575
new MockFileData(config.ToJson())
7676
);
7777

78-
_runtimeConfigLoader.UpdateConfigFileName(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
78+
_runtimeConfigLoader.UpdateConfigFilePath(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
7979

8080
try
8181
{
@@ -96,7 +96,7 @@ public void ValidateAuthNSectionNotNecessary()
9696
new MockFileData(config.ToJson())
9797
);
9898

99-
_runtimeConfigLoader.UpdateConfigFileName(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
99+
_runtimeConfigLoader.UpdateConfigFilePath(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
100100

101101
try
102102
{
@@ -125,7 +125,7 @@ public void ValidateFailureWithIncompleteJwtConfig()
125125
new MockFileData(config.ToJson())
126126
);
127127

128-
_runtimeConfigLoader.UpdateConfigFileName(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
128+
_runtimeConfigLoader.UpdateConfigFilePath(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
129129

130130
Assert.ThrowsException<NotSupportedException>(() =>
131131
{
@@ -160,7 +160,7 @@ public void ValidateFailureWithUnneededEasyAuthConfig()
160160
new MockFileData(config.ToJson())
161161
);
162162

163-
_runtimeConfigLoader.UpdateConfigFileName(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
163+
_runtimeConfigLoader.UpdateConfigFilePath(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME);
164164

165165
Assert.ThrowsException<NotSupportedException>(() =>
166166
{

src/Service.Tests/Unittests/ConfigValidationUnitTests.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Data;
8+
using System.IO;
89
using System.IO.Abstractions.TestingHelpers;
910
using System.Linq;
1011
using System.Net;
@@ -2017,6 +2018,71 @@ public void ValidateRuntimeBaseRouteSettings(
20172018
}
20182019
}
20192020

2021+
/// <summary>
2022+
/// This test checks that the final config used by runtime engine doesn't lose the directory information
2023+
/// if provided by the user.
2024+
/// It also validates that if config file is provided by the user, it will be used directly irrespective of
2025+
/// environment variable being set or not.
2026+
/// When user doesn't provide a config file, we check if environment variable is set and if it is, we use
2027+
/// the config file specified by the environment variable, else we use the default config file.
2028+
/// <param name="userProvidedConfigFilePath"></param>
2029+
/// <param name="environmentValue"></param>
2030+
/// <param name="useAbsolutePath"></param>
2031+
/// <param name="environmentFile"></param>
2032+
/// <param name="finalConfigFilePath"></param>
2033+
[DataTestMethod]
2034+
[DataRow("my-config.json", "", false, null, "my-config.json", DisplayName = "Config file in the current directory provided by user and environment variable is not set")]
2035+
[DataRow("test-configs/my-config.json", "", false, null, "test-configs/my-config.json", DisplayName = "Config file in different directory provided by user and environment variable is not set")]
2036+
[DataRow("my-config.json", "Test", false, "my-config.Test.json", "my-config.json", DisplayName = "Config file in the current directory provided by user and environment variable is set")]
2037+
[DataRow("test-configs/my-config.json", "Test", false, "test-configs/my-config.Test.json", "test-configs/my-config.json", DisplayName = "Config file in different directory provided by user and environment variable is set")]
2038+
[DataRow("my-config.json", "Test", false, "dab-config.Test.json", "my-config.json", DisplayName = "Config file in the current directory provided by user and environment variable is set and environment file is present")]
2039+
[DataRow("test-configs/my-config.json", "Test", false, "test-configs/dab-config.Test.json", "test-configs/my-config.json", DisplayName = "Config file in different directory provided by user and environment variable is set and environment file is present")]
2040+
[DataRow("my-config.json", "", true, null, "my-config.json", DisplayName = "Config file in the current directory provided by user and environment variable is not set and absolute path is provided")]
2041+
[DataRow("test-configs/my-config.json", "", true, null, "test-configs/my-config.json", DisplayName = "Config file in different directory provided by user and environment variable is not set and absolute path is provided")]
2042+
[DataRow("my-config.json", "Test", true, "my-config.Test.json", "my-config.json", DisplayName = "Config file in the current directory provided by user and environment variable is set and absolute path is provided")]
2043+
[DataRow("test-configs/my-config.json", "Test", true, "test-configs/my-config.Test.json", "test-configs/my-config.json", DisplayName = "Config file in different directory provided by user and environment variable is set and absolute path is provided")]
2044+
[DataRow("my-config.json", "Test", true, "dab-config.Test.json", "my-config.json", DisplayName = "Config file in the current directory provided by user and environment variable is set and environment file is present and absolute path is provided")]
2045+
[DataRow("test-configs/my-config.json", "Test", true, "test-configs/dab-config.Test.json", "test-configs/my-config.json", DisplayName = "Config file in the different directory provided by user and environment variable is set and environment file is present and absolute path is provided")]
2046+
[DataRow(null, "", false, null, "dab-config.json", DisplayName = "Config file not provided by user and environment variable is not set")]
2047+
[DataRow(null, "Test", false, "dab-config.Test.json", "dab-config.json", DisplayName = "Config file not provided by user and environment variable is set and environment file is present")]
2048+
[DataRow(null, "Test", false, null, "dab-config.json", DisplayName = "Config file not provided by user and environment variable is set and environment file is not present")]
2049+
public void TestCorrectConfigFileIsSelectedForRuntimeEngine(
2050+
string userProvidedConfigFilePath,
2051+
string environmentValue,
2052+
bool useAbsolutePath,
2053+
string environmentFile,
2054+
string finalConfigFilePath)
2055+
{
2056+
MockFileSystem fileSystem = new();
2057+
if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(userProvidedConfigFilePath)))
2058+
{
2059+
fileSystem.AddDirectory("test-configs");
2060+
}
2061+
2062+
if (useAbsolutePath)
2063+
{
2064+
userProvidedConfigFilePath = fileSystem.Path.GetFullPath(userProvidedConfigFilePath);
2065+
finalConfigFilePath = fileSystem.Path.GetFullPath(finalConfigFilePath);
2066+
}
2067+
2068+
if (environmentFile is not null)
2069+
{
2070+
fileSystem.AddEmptyFile(environmentFile);
2071+
}
2072+
2073+
FileSystemRuntimeConfigLoader runtimeConfigLoader;
2074+
if (userProvidedConfigFilePath is not null)
2075+
{
2076+
runtimeConfigLoader = new(fileSystem, userProvidedConfigFilePath);
2077+
}
2078+
else
2079+
{
2080+
runtimeConfigLoader = new(fileSystem);
2081+
}
2082+
2083+
Assert.AreEqual(finalConfigFilePath, runtimeConfigLoader.ConfigFilePath);
2084+
}
2085+
20202086
private static RuntimeConfigValidator InitializeRuntimeConfigValidator()
20212087
{
20222088
MockFileSystem fileSystem = new();

src/Service/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, RuntimeC
301301
_logger.LogError("Exiting the runtime engine...");
302302
}
303303

304-
throw new ApplicationException($"Could not initialize the engine with the runtime config file: {fileSystemRuntimeConfigLoader.ConfigFileName}");
304+
throw new ApplicationException($"Could not initialize the engine with the runtime config file: {fileSystemRuntimeConfigLoader.ConfigFilePath}");
305305
}
306306
}
307307
else

0 commit comments

Comments
 (0)