-
Notifications
You must be signed in to change notification settings - Fork 281
Update variable replacement during deserialization to use replacement settings class and add AKV replacement logic. #2882
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
base: main
Are you sure you want to change the base?
Conversation
// Determines whether to replace environment variable with its | ||
// value or not while deserializing. | ||
private bool _replaceEnvVar; | ||
// Settings for variable replacement during deserialization. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make the comment a bit more descriptive, to show that it can replace the variable with either AKV, an environment variable, or nothing during deserialization.
// Determines whether to replace environment variable with its | ||
// value or not while deserializing. | ||
private bool _replaceEnvVar; | ||
// Settings for variable replacement during deserialization. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as comment above, this applies to all the places where the variable type was changed
/// <param name="replaceEnvVar">Whether to replace environment variable with its | ||
/// value or not while deserializing.</param> | ||
internal EntityCacheOptionsConverterFactory(bool replaceEnvVar) | ||
internal EntityCacheOptionsConverterFactory(DeserializationVariableReplacementSettings? replacementSettings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The param name
comment should also be changed.
/// <param name="replaceEnvVar">Whether to replace environment variable with its | ||
/// value or not while deserializing.</param> | ||
public EntityCacheOptionsConverter(bool replaceEnvVar) | ||
public EntityCacheOptionsConverter(DeserializationVariableReplacementSettings? replacementSettings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The param name
comment should also be changed.
foreach (KeyValuePair<Regex, Func<Match, string>> strategy in _replacementSettings.ReplacementStrategies) | ||
{ | ||
value = strategy.Key.Replace(value, new MatchEvaluator(strategy.Value)); | ||
} | ||
|
||
return value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused with how this section works, wouldn't this just give you the last replacement strategy since they are all saved in the same value?
|
||
if (!string.IsNullOrEmpty(json) && TryParseConfig(json, out RuntimeConfig, connectionString: _connectionString, replaceEnvVar: replaceEnvVar)) | ||
if (!string.IsNullOrEmpty(json) && TryParseConfig(json, out RuntimeConfig, | ||
new DeserializationVariableReplacementSettings(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAKVVar: true), logger: null, connectionString: _connectionString)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need to delete the parameter replaceEnvVar
from the TryLoadConfig
function since we don't use it anymore
// Add environment variable replacement if enabled | ||
if (enableEnvReplacement) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that there must be something missing inside this if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still need to get rid of envVar
in some places and resolve missing logic inside of an if
statement.
break; | ||
|
||
default: | ||
reader.Skip(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we want to skip an incorrect property? Shouldn't we want to throw an error?
} | ||
} | ||
} | ||
else if (reader.TokenType is JsonTokenType.Null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it would be better to put this on top so it is consistent with other ConverterFactories
{ | ||
string? value = reader.GetString(); | ||
return Regex.Replace(value!, ENV_PATTERN, new MatchEvaluator(ReplaceMatchWithEnvVariable)); | ||
if (string.IsNullOrEmpty(value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible for the value to be white spaces? If so I it would be better to use IsNullOrWhiteSpace
public static string? DeserializeString(this Utf8JsonReader reader, | ||
bool replaceEnvVar, | ||
EnvironmentVariableReplacementFailureMode replacementFailureMode = EnvironmentVariableReplacementFailureMode.Throw) | ||
DeserializationVariableReplacementSettings? replacementSettings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to update the parameters in the summary section
#pragma warning disable CA1024 // Use properties where appropriate | ||
public IChangeToken GetChangeToken() | ||
#pragma warning restore CA1024 // Use properties where appropriate | ||
#pragma warning restore CA1024 // Use properties where Appropriate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Change this back to lower case
clientOptions.Retry.MaxRetries = options.RetryPolicy.MaxCount ?? 3; | ||
clientOptions.Retry.Delay = TimeSpan.FromSeconds(options.RetryPolicy.DelaySeconds ?? 1); | ||
clientOptions.Retry.MaxDelay = TimeSpan.FromSeconds(options.RetryPolicy.MaxDelaySeconds ?? 16); | ||
clientOptions.Retry.NetworkTimeout = TimeSpan.FromSeconds(options.RetryPolicy.NetworkTimeoutSeconds ?? 30); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we use the DEFAULT
variables found in AKVRetryPolicyOptions
instead of using numbers?
catch | ||
{ | ||
// If we can't extract AKV options, return null and proceed without AKV variable replacement | ||
return null; | ||
} | ||
|
||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Returning the nulls like this seems a bit redundant, do you think there is a better way to do this?
public class DeserializationVariableReplacementSettings | ||
{ | ||
public bool DoReplaceEnvVar { get; set; } = true; | ||
public bool DoReplaceAKVVar { get; set; } = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public bool DoReplaceAKVVar { get; set; } = true; | |
public bool DoReplaceAkvVar { get; set; } = true; |
envFailureMode: replacementFailureMode); | ||
options.Converters.Add(new StringJsonConverterFactory(envOnlySettings)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
envFailureMode: replacementFailureMode); | |
options.Converters.Add(new StringJsonConverterFactory(envOnlySettings)); | |
envFailureMode: replacementFailureMode); | |
options.Converters.Add(new StringJsonConverterFactory(envOnlySettings)); |
using JsonDocument doc = JsonDocument.Parse(json); | ||
if (doc.RootElement.TryGetProperty("azure-key-vault", out JsonElement akvElement)) | ||
{ | ||
return JsonSerializer.Deserialize<AzureKeyVaultOptions>(akvElement.GetRawText(), options); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we want to deserialize the azure-key-vault
here? Don't we already do that with the ConverterFactory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added comments
catch | ||
{ | ||
// If we can't extract AKV options, return null and proceed without AKV variable replacement | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see anything explicitly handled in catch. It returns null as same outside the catch
updatedConnection = GetConnectionStringWithApplicationName(connectionValue); | ||
} | ||
else if (ds.DatabaseType is DatabaseType.PostgreSQL && replaceEnvVar) | ||
else if (ds.DatabaseType is DatabaseType.PostgreSQL && replacementSettings?.DoReplaceEnvVar == true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: replacementSettings?.DoReplaceEnvVar is true
?
clientOptions.Retry.Mode = retryMode; | ||
clientOptions.Retry.MaxRetries = options.RetryPolicy.MaxCount ?? 3; | ||
clientOptions.Retry.Delay = TimeSpan.FromSeconds(options.RetryPolicy.DelaySeconds ?? 1); | ||
clientOptions.Retry.MaxDelay = TimeSpan.FromSeconds(options.RetryPolicy.MaxDelaySeconds ?? 16); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should the MaxDelay be more than the NetworkTimeout? also are these configurable or going to be fixed always?
Why make this change?
Adds AKV variable replacement and expands our design for doing variable replacements to be more extensible when new variable replacement logic is added.
Closes #2708
Closes #2748
Related to #2863
What is this change?
Change the way that variable replacement is handled to instead of simply using a
bool
to indicate that we want env variable replacement, we add a class which holds all of the replacement settings. This will hold whether or not we will do replacement for each kind of variable that we will handle replacement for during deserialization. We also include the replacement failure mode, and put the logic for handling the replacements into a strategy dictionary which pairs the replacement variable type with the strategy for doing that replacement.Because Azure Key Vault secret replacement requires having the retry and connection settings in order to do the AKV replacement, we must do a first pass where we only do non-AKV replacement and get the required settings so that if AKV replacement is used we have the required settings to do that replacement.
We also have to keep in mind that the legacy of the
Configuration Controller
will ignore all variable replacement, so we construct the replacement settings for this code path to not use any variable replacement at all.How was this tested?
We have updated the logic for the tests to use the new system, however manual testing using an actual AKV is still required.
Sample Request(s)